diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1013646 --- /dev/null +++ b/CHANGELOG.md @@ -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 diff --git a/linux/src/file_store.cpp b/linux/src/file_store.cpp new file mode 100644 index 0000000..66d163f --- /dev/null +++ b/linux/src/file_store.cpp @@ -0,0 +1,296 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// File class - provides a simple file implementation + +#include "file_store.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 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* 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 diff --git a/linux/src/log.cpp b/linux/src/log.cpp new file mode 100644 index 0000000..7cf45fc --- /dev/null +++ b/linux/src/log.cpp @@ -0,0 +1,62 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Log - implemented using stdout. + +#include "log.h" + +#include +#include +#include +#include + +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(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 diff --git a/oemcrypto/include/OEMCryptoCENC.h b/oemcrypto/include/OEMCryptoCENC.h index b933ce1..97f64e1 100644 --- a/oemcrypto/include/OEMCryptoCENC.h +++ b/oemcrypto/include/OEMCryptoCENC.h @@ -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 diff --git a/oemcrypto/odk/include/core_message_features.h b/oemcrypto/odk/include/core_message_features.h index d66b95e..16289c6 100644 --- a/oemcrypto/odk/include/core_message_features.h +++ b/oemcrypto/odk/include/core_message_features.h @@ -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 { diff --git a/oemcrypto/odk/include/odk.h b/oemcrypto/odk/include/odk.h index b412a7d..941afc1 100644 --- a/oemcrypto/odk/include/odk.h +++ b/oemcrypto/odk/include/odk.h @@ -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 diff --git a/oemcrypto/odk/include/odk_structs.h b/oemcrypto/odk/include/odk_structs.h index 9c41fc7..2799732 100644 --- a/oemcrypto/odk/include/odk_structs.h +++ b/oemcrypto/odk/include/odk_structs.h @@ -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 diff --git a/oemcrypto/odk/src/core_message_serialize_proto.cpp b/oemcrypto/odk/src/core_message_serialize_proto.cpp index 860ea26..5132bda 100644 --- a/oemcrypto/odk/src/core_message_serialize_proto.cpp +++ b/oemcrypto/odk/src/core_message_serialize_proto.cpp @@ -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; diff --git a/oemcrypto/odk/src/odk.c b/oemcrypto/odk/src/odk.c index 6cb476e..019d50e 100644 --- a/oemcrypto/odk/src/odk.c +++ b/oemcrypto/odk/src/odk.c @@ -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); +} diff --git a/oemcrypto/odk/src/odk.gyp b/oemcrypto/odk/src/odk.gyp index 1bdd05c..b874247 100644 --- a/oemcrypto/odk/src/odk.gyp +++ b/oemcrypto/odk/src/odk.gyp @@ -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 to work. '_DEFAULT_SOURCE', diff --git a/oemcrypto/odk/test/fuzzing/odk_fuzz.gyp b/oemcrypto/odk/test/fuzzing/odk_fuzz.gyp index dea3674..7602e0a 100644 --- a/oemcrypto/odk/test/fuzzing/odk_fuzz.gyp +++ b/oemcrypto/odk/test/fuzzing/odk_fuzz.gyp @@ -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', diff --git a/oemcrypto/odk/test/fuzzing/odk_fuzz_helper.cpp b/oemcrypto/odk/test/fuzzing/odk_fuzz_helper.cpp index cf1be5f..1f3b230 100644 --- a/oemcrypto/odk/test/fuzzing/odk_fuzz_helper.cpp +++ b/oemcrypto/odk/test/fuzzing/odk_fuzz_helper.cpp @@ -3,6 +3,8 @@ // License Agreement. #include "fuzzing/odk_fuzz_helper.h" +#include + #include "odk.h" namespace oemcrypto_core_message { diff --git a/oemcrypto/oemcrypto_unittests.gyp b/oemcrypto/oemcrypto_unittests.gyp index ffbe1d5..f6a41ee 100644 --- a/oemcrypto/oemcrypto_unittests.gyp +++ b/oemcrypto/oemcrypto_unittests.gyp @@ -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', diff --git a/oemcrypto/opk/FILES.md b/oemcrypto/opk/FILES.md new file mode 100644 index 0000000..05100f2 --- /dev/null +++ b/oemcrypto/opk/FILES.md @@ -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` diff --git a/oemcrypto/opk/README.md b/oemcrypto/opk/README.md new file mode 100644 index 0000000..0d39899 --- /dev/null +++ b/oemcrypto/opk/README.md @@ -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. diff --git a/oemcrypto/opk/build/Makefile.opk b/oemcrypto/opk/build/Makefile.opk new file mode 100644 index 0000000..ff46389 --- /dev/null +++ b/oemcrypto/opk/build/Makefile.opk @@ -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 := +# CPPFLAGS := +# CXXFLAGS := + +# 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 diff --git a/oemcrypto/opk/build/Makefile.optee b/oemcrypto/opk/build/Makefile.optee new file mode 100644 index 0000000..4ec4665 --- /dev/null +++ b/oemcrypto/opk/build/Makefile.optee @@ -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 diff --git a/oemcrypto/opk/build/host.gyp b/oemcrypto/opk/build/host.gyp new file mode 100644 index 0000000..2a2903e --- /dev/null +++ b/oemcrypto/opk/build/host.gyp @@ -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)', + ], + }, + ] +} diff --git a/oemcrypto/opk/build/oemcrypto/opk/ports/optee/build/README.md b/oemcrypto/opk/build/oemcrypto/opk/ports/optee/build/README.md new file mode 100644 index 0000000..e9bcfb2 --- /dev/null +++ b/oemcrypto/opk/build/oemcrypto/opk/ports/optee/build/README.md @@ -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`. \ No newline at end of file diff --git a/oemcrypto/opk/build/ta.gyp b/oemcrypto/opk/build/ta.gyp new file mode 100644 index 0000000..435d403 --- /dev/null +++ b/oemcrypto/opk/build/ta.gyp @@ -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', + ], + }, + ], +} diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto.c b/oemcrypto/opk/oemcrypto_ta/oemcrypto.c new file mode 100644 index 0000000..ffd9004 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto.c @@ -0,0 +1,3594 @@ +/* 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 "OEMCryptoCENC.h" + +#include +#include + +#include "odk.h" +#include "odk_endian.h" +#include "odk_util.h" +#include "oemcrypto_api_macros.h" +#include "oemcrypto_asymmetric_key_table.h" +#include "oemcrypto_check_macros.h" +#include "oemcrypto_compiler_attributes.h" +#include "oemcrypto_entitled_key_session.h" +#include "oemcrypto_entitled_key_session_table.h" +#include "oemcrypto_key.h" +#include "oemcrypto_key_table.h" +#include "oemcrypto_output.h" +#include "oemcrypto_overflow.h" +#include "oemcrypto_session.h" +#include "oemcrypto_session_key_table.h" +#include "oemcrypto_session_table.h" +#include "oemcrypto_session_type.h" +#include "oemcrypto_usage_table.h" +#include "wtpi_abort_interface.h" +#include "wtpi_clock_interface_layer1.h" +#include "wtpi_config_interface.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" +#include "wtpi_crypto_asymmetric_interface.h" +#include "wtpi_idle_interface.h" +#include "wtpi_initialize_terminate_interface.h" +#include "wtpi_logging_interface.h" +#include "wtpi_root_of_trust_interface_layer1.h" + +typedef enum GlobalSystemState { + SYSTEM_NOT_INITIALIZED = (int)0x2ca77206, + SYSTEM_INITIALIZED = (int)0xf57fab49 +} GlobalSystemState; +static GlobalSystemState g_opk_system_state = SYSTEM_NOT_INITIALIZED; + +static bool IsSubstrInRange(size_t message_length, + OEMCrypto_Substring substring, bool allow_null) { + if (!substring.length) return (substring.offset == 0) && allow_null; + if (substring.offset > message_length) return false; + + size_t end_of_substring; + if (OPK_AddOverflowUX(substring.offset, substring.length, + &end_of_substring)) { + return false; + } + if (end_of_substring > message_length) return false; + return true; +} + +/* Cleanup functions for various OEMCrypto calls. */ + +static OEMCryptoResult FreeMacKeys(OEMCryptoSession* session) { + ABORT_IF(g_opk_system_state != SYSTEM_INITIALIZED, + "OEMCrypto is not yet initialized"); + ABORT_IF_NULL(session); + OEMCryptoResult result = OPKI_FreeKeyFromTable(&session->mac_key_server); + OEMCryptoResult free_key_result = + OPKI_FreeKeyFromTable(&session->mac_key_client); + if (result == OEMCrypto_SUCCESS) result = free_key_result; + return result; +} + +static OEMCryptoResult FreeMacAndEncryptionKeys(OEMCryptoSession* session) { + ABORT_IF(g_opk_system_state != SYSTEM_INITIALIZED, + "OEMCrypto is not yet initialized"); + ABORT_IF_NULL(session); + OEMCryptoResult result = FreeMacKeys(session); + OEMCryptoResult free_key_result = + OPKI_FreeKeyFromTable(&session->encryption_key); + if (result == OEMCrypto_SUCCESS) result = free_key_result; + return result; +} + +static OEMCryptoResult FreeContentAndEntitlementKeys( + OEMCryptoSession* session) { + ABORT_IF(g_opk_system_state != SYSTEM_INITIALIZED, + "OEMCrypto is not yet initialized"); + ABORT_IF_NULL(session); + OEMCryptoResult result = OEMCrypto_SUCCESS; + OEMCryptoResult free_key_result = result; + for (size_t i = 0; i < session->num_content_keys; i++) { + free_key_result = OPKI_FreeKeyFromTable(&session->content_keys[i]); + if (result == OEMCrypto_SUCCESS) result = free_key_result; + } + for (size_t i = 0; i < session->num_entitlement_keys; i++) { + free_key_result = OPKI_FreeKeyFromTable(&session->entitlement_keys[i]); + if (result == OEMCrypto_SUCCESS) result = free_key_result; + } + session->num_content_keys = 0; + session->num_entitlement_keys = 0; + return result; +} + +static OEMCryptoResult FreeEntitledContentKeys( + OEMCryptoEntitledKeySession* session) { + ABORT_IF(g_opk_system_state != SYSTEM_INITIALIZED, + "OEMCrypto is not yet initialized"); + ABORT_IF_NULL(session); + OEMCryptoResult result = OEMCrypto_SUCCESS; + OEMCryptoResult free_key_result = result; + for (size_t i = 0; i < session->num_entitled_content_keys; i++) { + free_key_result = OPKI_FreeKeyFromTable(&session->entitled_content_keys[i]); + if (result == OEMCrypto_SUCCESS) result = free_key_result; + session->entitlement_keys[i] = (EntitlementKeyInfo){0}; + } + session->num_entitled_content_keys = 0; + return result; +} + +static bool IsSupportedAsymmetricKeyType(AsymmetricKeyType key_type) { + return key_type == DRM_RSA_PRIVATE_KEY || key_type == DRM_ECC_PRIVATE_KEY; +} + +static OEMCryptoResult RewrapDeviceDRMKeyOEMCert( + OEMCryptoSession* session_context, const uint8_t* encrypted_message_key, + size_t encrypted_message_key_length, const uint8_t* enc_drm_key, + size_t enc_drm_key_length, const uint8_t* enc_drm_key_iv, + AsymmetricKeyType drm_key_type, uint8_t* wrapped_drm_key, + size_t wrapped_drm_key_length) { + ABORT_IF(g_opk_system_state != SYSTEM_INITIALIZED, + "OEMCrypto is not yet initialized"); + ABORT_IF(WTPI_GetProvisioningMethod() != OEMCrypto_OEMCertificate, + "This function is only valid on Provisioning 3.0 devices"); + + ABORT_IF_NULL(session_context); + ABORT_IF_NULL(encrypted_message_key); + ABORT_IF_ZERO(encrypted_message_key_length); + ABORT_IF_NULL(enc_drm_key); + ABORT_IF_ZERO(enc_drm_key_length); + ABORT_IF(enc_drm_key_length > PKCS8_DRM_KEY_MAX_SIZE, + "enc_drm_key_length of %zu is too large", enc_drm_key_length); + ABORT_IF_NULL(enc_drm_key_iv); + ABORT_IF(!IsSupportedAsymmetricKeyType(drm_key_type), + "drm_key_type %d is invalid", drm_key_type); + ABORT_IF_NULL(wrapped_drm_key); + ABORT_IF_ZERO(wrapped_drm_key_length); + + // TODO(b/180530495): implement this. + OEMCryptoResult result = OEMCrypto_ERROR_NOT_IMPLEMENTED; + + /* RSA decryption needs at most RSA_size to decrypt. 3072 is the largest size + OEM RSA keys we can use. */ + // uint8_t message_key[KEY_SIZE_3072]; + // size_t message_key_length = sizeof(message_key); + // DecryptMessageWithOEMPrivateKey( + // encrypted_message_key, encrypted_message_key_length, message_key, + // &message_key_length); + + if (result != OEMCrypto_SUCCESS) goto cleanup; + // if (message_key_length != KEY_SIZE_128) { + // /* Encryption key is expected to be an AES 128-bit key. */ + // result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + // goto cleanup; + // } + + // result = OPKI_CreateKey(&session_context->encryption_key, ENCRYPTION_KEY, + // KEY_SIZE_128); + // if (result != OEMCrypto_SUCCESS) goto cleanup; + // result = WTPI_CreateKeyHandle(message_key, message_key_length, + // ENCRYPTION_KEY, + // &(session_context->encryption_key->key_handle)); + // if (result != OEMCrypto_SUCCESS) goto cleanup; + + // result = RewrapDeviceDRMKeyCommon( + // session_context, enc_drm_key, enc_drm_key_length, enc_drm_key_iv, + // drm_key_type, wrapped_drm_key, wrapped_drm_key_length); + +cleanup:; + OEMCryptoResult free_key_result = + OPKI_FreeAsymmetricKeyFromTable(&session_context->drm_private_key); + if (result == OEMCrypto_SUCCESS) result = free_key_result; + free_key_result = FreeMacAndEncryptionKeys(session_context); + if (result == OEMCrypto_SUCCESS) result = free_key_result; + if (result != OEMCrypto_SUCCESS) session_context->state = SESSION_INVALID; + return result; +} + +// This function contains the RewrapDeviceDRMKey code that is shared between +// keybox and OEM certificate devices. +static OEMCryptoResult RewrapDeviceDRMKeyCommon(OEMCryptoSession* session, + const uint8_t* enc_drm_key, + size_t enc_drm_key_length, + const uint8_t* enc_drm_key_iv, + AsymmetricKeyType drm_key_type, + uint8_t* wrapped_drm_key, + size_t wrapped_drm_key_length) { + ABORT_IF(g_opk_system_state != SYSTEM_INITIALIZED, + "OEMCrypto is not yet initialized"); + ABORT_IF_NULL(session); + ABORT_IF_NULL(enc_drm_key); + ABORT_IF_ZERO(enc_drm_key_length); + ABORT_IF(enc_drm_key_length > PKCS8_DRM_KEY_MAX_SIZE, + "enc_drm_key_length of %zu is too large", enc_drm_key_length); + ABORT_IF_NULL(enc_drm_key_iv); + ABORT_IF(!IsSupportedAsymmetricKeyType(drm_key_type), + "drm_key_type %d is invalid", drm_key_type); + + if (!OPKI_CheckKey(session->encryption_key, ENCRYPTION_KEY)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + /* Decrypt and deserialize the DRM private key. */ + uint8_t clear_drm_key[PKCS8_DRM_KEY_MAX_SIZE]; + KeySize key_size; + OEMCryptoResult result = + WTPI_K1_GetKeySize(session->encryption_key->key_handle, &key_size); + if (result != OEMCrypto_SUCCESS) return result; + result = WTPI_C1_AESCBCDecrypt( + session->encryption_key->key_handle, (size_t)key_size, enc_drm_key, + enc_drm_key_length, enc_drm_key_iv, clear_drm_key); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to AES CBC decrypt DRM private key with result: %u", result); + return result; + } + + /* Adjust for RSA keys with specified schemes */ + uint32_t allowed_schemes = 0; + size_t key_offset = 0; + if (drm_key_type == DRM_RSA_PRIVATE_KEY) { + if (enc_drm_key_length >= 8 && + crypto_memcmp(clear_drm_key, "SIGN", 4) == 0) { + memcpy(&allowed_schemes, clear_drm_key + 4, 4); + allowed_schemes = oemcrypto_be32toh(allowed_schemes); + key_offset = 8; + } else { + allowed_schemes = kSign_RSASSA_PSS; + } + } + + WTPI_AsymmetricKey_Handle private_key_handle; + result = WTPI_CreateAsymmetricKeyHandle(clear_drm_key + key_offset, + enc_drm_key_length - key_offset, + drm_key_type, &private_key_handle); + if (result != OEMCrypto_SUCCESS) { + LOGE( + "Failed to create asymmetric key handle for DRM private key with " + "result: %u", + result); + return result; + } + size_t private_key_size; + result = WTPI_GetSignatureSize(private_key_handle, &private_key_size); + WTPI_FreeAsymmetricKeyHandle(private_key_handle); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get DRM private key size with result: %u", result); + return result; + } + + result = + WTPI_WrapAsymmetricKey(wrapped_drm_key, wrapped_drm_key_length, + drm_key_type, clear_drm_key, enc_drm_key_length); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to re-wrap DRM private key with result: %u", result); + return result; + } + /* Check that it's a valid DRM key. */ + result = OPKI_LoadDRMKey(session, drm_key_type, wrapped_drm_key, + wrapped_drm_key_length, private_key_size, + allowed_schemes); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to load DRM key with result: %u", result); + } + return result; +} + +static OEMCryptoResult RewrapDeviceDRMKeyKeybox( + OEMCryptoSession* session_context, const uint8_t* message, + size_t message_length, const uint8_t* signature, size_t signature_length, + const uint8_t* enc_drm_key, size_t enc_drm_key_length, + const uint8_t* enc_drm_key_iv, AsymmetricKeyType drm_key_type, + uint8_t* wrapped_drm_key, size_t wrapped_drm_key_length) { + ABORT_IF(g_opk_system_state != SYSTEM_INITIALIZED, + "OEMCrypto is not yet initialized"); + ABORT_IF(WTPI_GetProvisioningMethod() != OEMCrypto_Keybox, + "This function is only valid on Provisioning 2.0 devices"); + + ABORT_IF_NULL(session_context); + ABORT_IF_NULL(message); + ABORT_IF_ZERO(message_length); + ABORT_IF_NULL(signature); + ABORT_IF_ZERO(signature_length); + ABORT_IF(signature_length != SHA256_DIGEST_LENGTH, + "signature_length is not the length of a SHA256 digest"); + ABORT_IF_NULL(enc_drm_key); + ABORT_IF_ZERO(enc_drm_key_length); + ABORT_IF(enc_drm_key_length > PKCS8_DRM_KEY_MAX_SIZE, + "enc_drm_key_length of %zu is too large", enc_drm_key_length); + ABORT_IF_NULL(enc_drm_key_iv); + ABORT_IF(!IsSupportedAsymmetricKeyType(drm_key_type), + "drm_key_type %d is invalid", drm_key_type); + ABORT_IF_NULL(wrapped_drm_key); + ABORT_IF_ZERO(wrapped_drm_key_length); + + /* Use mac_key_server from previous call to GenerateDerivedKeys to verify the + message. */ + OEMCryptoResult result = OPKI_VerifySignatureWithMacKeyServer( + session_context, message, message_length, signature); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to verify signature with mac key server, error: %u", result); + goto cleanup; + } + + result = RewrapDeviceDRMKeyCommon( + session_context, enc_drm_key, enc_drm_key_length, enc_drm_key_iv, + drm_key_type, wrapped_drm_key, wrapped_drm_key_length); + +cleanup:; + OEMCryptoResult free_key_result = + OPKI_FreeAsymmetricKeyFromTable(&session_context->drm_private_key); + if (result == OEMCrypto_SUCCESS) result = free_key_result; + free_key_result = FreeMacAndEncryptionKeys(session_context); + if (result == OEMCrypto_SUCCESS) result = free_key_result; + if (result != OEMCrypto_SUCCESS) session_context->state = SESSION_INVALID; + + return result; +} + +static OEMCryptoResult GetDeviceID(uint8_t* device_id, + size_t* device_id_length) { + if (device_id_length == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (WTPI_GetProvisioningMethod() != OEMCrypto_Keybox) { + // TODO(b/180530495): Implement this. + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + if (*device_id_length < KEYBOX_DEVICE_ID_SIZE) { + *device_id_length = KEYBOX_DEVICE_ID_SIZE; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + if (device_id == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + *device_id_length = KEYBOX_DEVICE_ID_SIZE; + return WTPI_GetDeviceIDFromKeybox(device_id, *device_id_length); +} + +static OEMCryptoResult GetROTSignatureLength(size_t* signature_length) { + ABORT_IF(g_opk_system_state != SYSTEM_INITIALIZED, + "OEMCrypto is not yet initialized"); + ABORT_IF_NULL(signature_length); + const OEMCrypto_ProvisioningMethod provisioning_method = + WTPI_GetProvisioningMethod(); + if (provisioning_method == OEMCrypto_Keybox) { + *signature_length = SHA256_DIGEST_LENGTH; + return OEMCrypto_SUCCESS; + } else if (provisioning_method == OEMCrypto_OEMCertificate) { + // TODO(b/180530495): implement this. + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } else { + // TODO(b/180530495): implement this. + /* TODO: Add ECC support. */ + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } +} + +/* For entitled key session, gets session context for both the entitled key + * session and the entitlement session which is associated with this entitled + * key session. For entitlement session, places the session context in + * |entitlement_session| and leaves |key_session| blank. + */ +static OEMCryptoResult GetSessionContext( + OEMCrypto_SESSION session_id, OEMCryptoSession** entitlement_session, + OEMCryptoEntitledKeySession** key_session) { + ABORT_IF_NULL(entitlement_session); + ABORT_IF_NULL(key_session); + OEMCryptoResult result; + OEMCryptoSessionType session_type = OPKI_GetSessionType(session_id); + if (session_type != SESSION_TYPE_OEMCRYPTO && + session_type != SESSION_TYPE_ENTITLED_KEY) { + LOGE("Unexpected session type: %u", (unsigned int)session_type); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (session_type == SESSION_TYPE_ENTITLED_KEY) { + result = OPKI_GetEntitledKeySession(session_id, key_session); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get entitled key session with result: %u, session = %u", + result, session_id); + return result; + } + result = OPKI_GetSession((*key_session)->entitlement_session_id, + entitlement_session); + } else { + result = OPKI_GetSession(session_id, entitlement_session); + } + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session context with result: %u, session = %u", result, + session_id); + } + return result; +} + +OEMCryptoResult OEMCrypto_SetSandbox(const uint8_t* sandbox_id, + size_t sandbox_id_length) { + RETURN_INVALID_CONTEXT_IF_NULL(sandbox_id); + RETURN_INVALID_CONTEXT_IF_ZERO(sandbox_id_length); + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult OEMCrypto_Initialize(void) { + if (g_opk_system_state != SYSTEM_NOT_INITIALIZED) { + LOGE("OEMCrypto_Initialize called when not in uninitialized state."); + LOGE("OEMCrypto will now terminate and re-initialize."); + OEMCrypto_Terminate(); + } + + OEMCryptoResult result = WTPI_Initialize(); + if (result != OEMCrypto_SUCCESS) { + LOGE("OEMCrypto failed to |WTPI_Initialize| with result: %u", result); + result = OEMCrypto_ERROR_INIT_FAILED; + goto cleanup; + } + + result = WTPI_InitializeClock(); + if (result != OEMCrypto_SUCCESS) { + LOGE("OEMCrypto failed to |WTPI_InitializeClock| with result: %u", result); + result = OEMCrypto_ERROR_INIT_FAILED; + goto cleanup; + } + + result = WTPI_InitializeKeybox(); + if (result != OEMCrypto_SUCCESS) { + LOGE("Initializing keybox error %u. System not ready for production.", + result); + // This means we don't have a keybox installed. This is fine for a test + // device or for a device that is still in the factory before it has been + // provisioned with a keybox. In that case, we log the error and continue. + // A production system will fail later on when it tries to validate the + // keybox. + result = OEMCrypto_SUCCESS; + } + + OPKI_InitializeSessionTable(); + OPKI_InitializeEntitledKeySessionTable(); + OPKI_InitializeKeyTable(); + OPKI_InitializeAsymmetricKeyTable(); + + result = WTPI_K1_InitializeKeyManagement(); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to initialize key management with result: %u", result); + goto cleanup; + } + + result = OPKI_InitializeUsageTable(); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to initialize usage table with result: %u", result); + goto cleanup; + } + + g_opk_system_state = SYSTEM_INITIALIZED; + result = OEMCrypto_SUCCESS; + +cleanup: + if (result != OEMCrypto_SUCCESS) OEMCrypto_Terminate(); + + return result; +} + +OEMCryptoResult OEMCrypto_Terminate(void) { + OEMCryptoResult usage_table_terminate_result = OPKI_TerminateUsageTable(); + OEMCryptoResult entitled_key_session_terminate_result = + OPKI_TerminateEntitledKeySessionTable(); + OEMCryptoResult session_terminate_result = OPKI_TerminateSessionTable(); + OEMCryptoResult key_terminate_result = OPKI_TerminateKeyTable(); + OEMCryptoResult asymmetric_key_terminate_result = + OPKI_TerminateAsymmetricKeyTable(); + OEMCryptoResult keybox_result = WTPI_TerminateKeybox(); + OEMCryptoResult clock_result = WTPI_TerminateClock(); + OEMCryptoResult key_management_result = WTPI_K1_TerminateKeyManagement(); + OEMCryptoResult tee_result = WTPI_Terminate(); + g_opk_system_state = SYSTEM_NOT_INITIALIZED; + if (tee_result != OEMCrypto_SUCCESS) { + LOGE("OEMCrypto failed to |Terminate| with result: %u", tee_result); + return tee_result; + } + if (keybox_result != OEMCrypto_SUCCESS) { + return keybox_result; + } + if (clock_result != OEMCrypto_SUCCESS) { + return clock_result; + } + if (key_management_result != OEMCrypto_SUCCESS) { + return key_management_result; + } + if (session_terminate_result != OEMCrypto_SUCCESS) { + return session_terminate_result; + } + if (entitled_key_session_terminate_result != OEMCrypto_SUCCESS) { + return entitled_key_session_terminate_result; + } + if (usage_table_terminate_result != OEMCrypto_SUCCESS) { + return usage_table_terminate_result; + } + if (asymmetric_key_terminate_result != OEMCrypto_SUCCESS) { + return asymmetric_key_terminate_result; + } + return key_terminate_result; +} + +OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + RETURN_INVALID_CONTEXT_IF_NULL(session); + + OEMCryptoResult result = OPKI_GrabSession(session); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to grab and initialize session with result: %u", result); + return result; + } + OEMCryptoSession* session_context = NULL; + result = OPKI_GetSession(*session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + *session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_OPENSESSION); + if (result != OEMCrypto_SUCCESS) goto cleanup; + result = OPKI_SetStatePostCall(session_context, API_OPENSESSION); + +cleanup: + if (result != OEMCrypto_SUCCESS) { + OEMCryptoResult ignored UNUSED = OPKI_FreeSession(*session); + } + + return result; +} + +OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_CLOSESESSION); + if (result != OEMCrypto_SUCCESS) return result; + result = OPKI_FreeEntitledKeySessions(session); + if (result != OEMCrypto_SUCCESS) return result; + return OPKI_FreeSession(session); +} + +OEMCryptoResult OEMCrypto_GenerateDerivedKeys(OEMCrypto_SESSION session, + const uint8_t* mac_key_context, + size_t mac_key_context_length, + const uint8_t* enc_key_context, + size_t enc_key_context_length) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (WTPI_GetProvisioningMethod() != OEMCrypto_Keybox) { + LOGD("This function is only valid on Provisioning 2.0 devices"); + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_GENERATEDERIVEDKEYS); + if (result != OEMCrypto_SUCCESS) return result; + + RETURN_INVALID_CONTEXT_IF_NULL(mac_key_context); + RETURN_INVALID_CONTEXT_IF_ZERO(mac_key_context_length); + RETURN_INVALID_CONTEXT_IF_NULL(enc_key_context); + RETURN_INVALID_CONTEXT_IF_ZERO(enc_key_context_length); + + WTPI_K1_SymmetricKey_Handle keybox_key = NULL; + result = WTPI_K1_CreateKeyHandleFromKeybox(&keybox_key); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to create key handle from keybox with result: %u", result); + goto cleanup; + } + result = OPKI_DeriveMacAndEncryptionKeys( + session_context, keybox_key, mac_key_context, mac_key_context_length, + enc_key_context, enc_key_context_length); + if (result == OEMCrypto_SUCCESS) { + result = OPKI_SetStatePostCall(session_context, API_GENERATEDERIVEDKEYS); + } else { + LOGE("Failed to derive mac and encryption keys from keybox with result: %u", + result); + } +cleanup : { + OEMCryptoResult free_key_result = WTPI_K1_FreeKeyHandle(keybox_key); + if (result == OEMCrypto_SUCCESS) result = free_key_result; + if (result != OEMCrypto_SUCCESS) { + FreeMacAndEncryptionKeys(session_context); + session_context->state = SESSION_INVALID; + } +} + return result; +} + +OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( + OEMCrypto_SESSION session, const uint8_t* enc_session_key, + size_t enc_session_key_length, const uint8_t* mac_key_context, + size_t mac_key_context_length, const uint8_t* enc_key_context, + size_t enc_key_context_length) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + WTPI_K1_SymmetricKey_Handle session_key_handle = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = + OPKI_CheckStatePreCall(session_context, API_DERIVEKEYSFROMSESSIONKEY); + if (result != OEMCrypto_SUCCESS) return result; + + RETURN_INVALID_CONTEXT_IF_NULL(enc_session_key); + RETURN_INVALID_CONTEXT_IF_ZERO(enc_session_key_length); + RETURN_INVALID_CONTEXT_IF_NULL(mac_key_context); + RETURN_INVALID_CONTEXT_IF_ZERO(mac_key_context_length); + RETURN_INVALID_CONTEXT_IF_NULL(enc_key_context); + RETURN_INVALID_CONTEXT_IF_ZERO(enc_key_context_length); + + /* Decrypt the session key with the DRM key and then derive keys from it. + RSA decryption needs at most RSA_size to decrypt. 3072 is the largest size + DRM RSA keys we can use. */ + uint8_t session_key[KEY_SIZE_3072]; + size_t session_key_length = sizeof(session_key); + size_t expected_session_key_length = 0; + result = OEMCrypto_ERROR_DEVICE_NOT_RSA_PROVISIONED; + if (session_context->drm_private_key == NULL) goto cleanup; + WTPI_AsymmetricKey_Handle private_key_handle; + uint32_t allowed_schemes; + AsymmetricKey* private_key = session_context->drm_private_key; + result = WTPI_UnwrapIntoAsymmetricKeyHandle( + private_key->wrapped_key, private_key->wrapped_key_length, + private_key->key_type, &private_key_handle, &allowed_schemes); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to unwrap DRM private key into key handle with result: %u", + result); + return result; + } + switch (session_context->drm_private_key->key_type) { + case DRM_RSA_PRIVATE_KEY: { + if (session_context->allowed_schemes != kSign_RSASSA_PSS) { + LOGE("Bad RSA padding scheme: %u", session_context->allowed_schemes); + WTPI_FreeAsymmetricKeyHandle(private_key_handle); + return OEMCrypto_ERROR_INVALID_KEY; + } + result = WTPI_RSADecrypt(private_key_handle, enc_session_key, + enc_session_key_length, session_key, + &session_key_length); + expected_session_key_length = KEY_SIZE_128; + } break; + case DRM_ECC_PRIVATE_KEY: { + result = WTPI_ECCDeriveSessionKey(private_key_handle, enc_session_key, + enc_session_key_length, session_key, + &session_key_length); + expected_session_key_length = KEY_SIZE_256; + } break; + case PROV40_ED25519_PRIVATE_KEY: + // DRM key can never be ED25519 key. + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + WTPI_FreeAsymmetricKeyHandle(private_key_handle); + if (result != OEMCrypto_SUCCESS) { + LOGE( + "Failed to decrypt session key with the DRM private key with result: " + "%u", + result); + goto cleanup; + } + ABORT_IF_ZERO(expected_session_key_length); + if (session_key_length != expected_session_key_length) { + LOGE("Invalid session key length: %zu, expected = %zu", session_key_length, + expected_session_key_length); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + result = WTPI_K1_CreateKeyHandle(session_key, session_key_length, + DERIVING_KEY, &session_key_handle); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to create key handle type DERIVING_KEY with result: %u", + result); + goto cleanup; + } + + result = OPKI_DeriveMacAndEncryptionKeys( + session_context, session_key_handle, mac_key_context, + mac_key_context_length, enc_key_context, enc_key_context_length); + if (result != OEMCrypto_SUCCESS) { + LOGE( + "Failed to derive mac and encryption keys from session key with " + "result: %u", + result); + goto cleanup; + } + + result = OPKI_SetStatePostCall(session_context, API_DERIVEKEYSFROMSESSIONKEY); + +cleanup:; + OEMCryptoResult free_key_result = + OPKI_FreeAsymmetricKeyFromTable(&session_context->drm_private_key); + if (result == OEMCrypto_SUCCESS) result = free_key_result; + free_key_result = WTPI_K1_FreeKeyHandle(session_key_handle); + if (result == OEMCrypto_SUCCESS) result = free_key_result; + if (result != OEMCrypto_SUCCESS) { + FreeMacAndEncryptionKeys(session_context); + session_context->state = SESSION_INVALID; + } + + return result; +} + +OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, + uint32_t* nonce) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + RETURN_INVALID_CONTEXT_IF_NULL(nonce); + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_GENERATENONCE); + if (result != OEMCrypto_SUCCESS) return result; + + /* Get the current time. */ + uint64_t now; + result = WTPI_GetTrustedTime(&now); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get trusted time with result: %u", result); + return result; + } + + /* last_nonce_time should only be initialized once. */ + static uint64_t last_nonce_time = 0; + static int nonce_count = 0; + const int nonce_flood_count = 200; + if (last_nonce_time == now) { + nonce_count++; + if (nonce_count > nonce_flood_count) { + LOGE("Nonce flood detected: now = %" PRIu64, now); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } else { + nonce_count = 1; + last_nonce_time = now; + } + + /* Generate a new nonce that doesn't collide with other nonces. */ + uint32_t nonce_value = 0; + while (nonce_value == 0 || OPKI_NonceCollision(nonce_value)) { + result = WTPI_C1_RandomBytes((uint8_t*)(&nonce_value), sizeof(nonce_value)); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to generate random nonce with result: %u", result); + return result; + } + } + result = OPKI_SetNonce(session_context, nonce_value); + if (result != OEMCrypto_SUCCESS) return result; + *nonce = nonce_value; + + return OPKI_SetStatePostCall(session_context, API_GENERATENONCE); +} + +OEMCryptoResult OEMCrypto_PrepAndSignProvisioningRequest( + OEMCrypto_SESSION session, uint8_t* message, size_t message_length, + size_t* core_message_length, uint8_t* signature, size_t* signature_length) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, + API_PREPANDSIGN_PROVISION_REQUEST); + if (result != OEMCrypto_SUCCESS) return result; + RETURN_INVALID_CONTEXT_IF_NULL(core_message_length); + RETURN_INVALID_CONTEXT_IF_NULL(signature_length); + if (session_context->request_signed) { + LOGE("Attempt to sign provision request after license request"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + size_t required_signature_size; + result = GetROTSignatureLength(&required_signature_size); + if (result != OEMCrypto_SUCCESS) return result; + uint8_t device_id[ODK_DEVICE_ID_LEN_MAX] = {0}; + size_t device_id_length = ODK_DEVICE_ID_LEN_MAX; + result = GetDeviceID(device_id, &device_id_length); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get device id with result: %u", result); + return result; + } + result = ODK_PrepareCoreProvisioningRequest( + message, message_length, core_message_length, + &session_context->nonce_values, device_id, device_id_length); + if (*signature_length < required_signature_size || + result == OEMCrypto_ERROR_SHORT_BUFFER) { + *signature_length = required_signature_size; + /* The core_message_length has been correctly set by + * ODK_PrepareCoreProvisioningRequest, but the message and signature buffer + * will be initialized and filled in the subsequent call. */ + return OEMCrypto_ERROR_SHORT_BUFFER; + } + if (result != OEMCrypto_SUCCESS) { + LOGE("ODK error: %u", result); + return result; + } + RETURN_INVALID_CONTEXT_IF_NULL(message); + RETURN_INVALID_CONTEXT_IF_ZERO(message_length); + RETURN_INVALID_CONTEXT_IF_NULL(signature); + const OEMCrypto_ProvisioningMethod provisioning_method = + WTPI_GetProvisioningMethod(); + if (provisioning_method == OEMCrypto_Keybox) { + result = OPKI_GenerateSignatureWithMacKeyClient( + session_context, message, message_length, signature, signature_length); + } else if (provisioning_method == OEMCrypto_OEMCertificate) { + result = OPKI_GenerateCertSignature(session_context, message, + message_length, signature, + signature_length, CERT_SIGNATURE_OEM); + } else { + LOGE("Bad provision method = %#x", WTPI_GetProvisioningMethod()); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (result != OEMCrypto_SUCCESS) { + if (result != OEMCrypto_ERROR_SHORT_BUFFER) { + LOGE("Failed to generate signature with result: %u", result); + } + return result; + } + session_context->request_signed = true; + return OPKI_SetStatePostCall(session_context, + API_PREPANDSIGN_PROVISION_REQUEST); +} + +OEMCryptoResult OEMCrypto_PrepAndSignLicenseRequest( + OEMCrypto_SESSION session, uint8_t* message, size_t message_length, + size_t* core_message_length, uint8_t* signature, size_t* signature_length) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = + OPKI_CheckStatePreCall(session_context, API_PREPANDSIGN_LICENSE_REQUEST); + if (result != OEMCrypto_SUCCESS) return result; + RETURN_INVALID_CONTEXT_IF_NULL(core_message_length); + RETURN_INVALID_CONTEXT_IF_NULL(signature_length); + ABORT_IF_NULL(session_context->drm_private_key); + size_t required_signature_size = session_context->drm_private_key->key_size; + result = ODK_PrepareCoreLicenseRequest(message, message_length, + core_message_length, + &session_context->nonce_values); + if (*signature_length < required_signature_size || + result == OEMCrypto_ERROR_SHORT_BUFFER) { + *signature_length = required_signature_size; + /* The core_message_length has been correctly set by + * ODK_PrepareCoreLicenseRequest, but the message and signature buffer will + * be initialized and filled in the subsequent call. */ + return OEMCrypto_ERROR_SHORT_BUFFER; + } + if (result != OEMCrypto_SUCCESS) { + LOGE("ODK error: %u", result); + return result; + } + RETURN_INVALID_CONTEXT_IF_NULL(message); + if (message_length < *core_message_length) { + LOGE("message_length of %zu is too small for core message length %zu", + message_length, *core_message_length); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + RETURN_INVALID_CONTEXT_IF_NULL(signature); + if (session_context->request_signed) { + LOGE("Attempt to sign two license requests"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + /* For backwards compatibility, we only sign the message body, and we compute + * a SHA256 of the core message. */ + result = WTPI_C1_SHA256(message, *core_message_length, + session_context->license_request_hash); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to compute SHA256 of the core message with result: %u", + result); + return result; + } + result = OPKI_GenerateCertSignature(session_context, message, message_length, + signature, signature_length, + CERT_SIGNATURE_DRM); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to generate cert signature with result: %u", result); + return result; + } + + uint64_t now; + result = WTPI_GetTrustedTime(&now); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get trusted time with result: %u", result); + return result; + } + result = ODK_InitializeClockValues(&session_context->clock_values, now); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to initialize clock values with result: %u", result); + return result; + } + session_context->request_signed = true; + return OPKI_SetStatePostCall(session_context, + API_PREPANDSIGN_LICENSE_REQUEST); +} + +OEMCryptoResult OEMCrypto_PrepAndSignRenewalRequest( + OEMCrypto_SESSION session, uint8_t* message, size_t message_length, + size_t* core_message_length, uint8_t* signature, size_t* signature_length) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = + OPKI_CheckStatePreCall(session_context, API_PREPANDSIGN_RENEWAL_REQUEST); + if (result != OEMCrypto_SUCCESS) return result; + RETURN_INVALID_CONTEXT_IF_NULL(core_message_length); + RETURN_INVALID_CONTEXT_IF_NULL(signature_length); + /* If we have signed a request, but have not loaded it, something is wrong. On + * the other hand, we can sign a license release using the mac keys from the + * usage table. So it is OK if we have never signed a license request. */ + if (session_context->request_signed && !session_context->response_loaded) { + LOGE("Attempt to sign renewal before load"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const size_t required_signature_size = SHA256_DIGEST_LENGTH; + uint64_t now; + result = WTPI_GetTrustedTime(&now); + if (result != OEMCrypto_SUCCESS) return result; + result = ODK_PrepareCoreRenewalRequest( + message, message_length, core_message_length, + &session_context->nonce_values, &session_context->clock_values, now); + if (*signature_length < required_signature_size || + result == OEMCrypto_ERROR_SHORT_BUFFER) { + *signature_length = required_signature_size; + /* The core_message_length has been correctly set by + * ODK_PrepareCoreRenewalRequest, but the message and signature buffer will + * be initialized and filled in the subsequent call. */ + return OEMCrypto_ERROR_SHORT_BUFFER; + } + if (result != OEMCrypto_SUCCESS) { + LOGE("ODK error: %u", result); + return result; + } + RETURN_INVALID_CONTEXT_IF_NULL(message); + if (message_length < *core_message_length) { + LOGE("message_length of %zu is too short; expected at least %zu", + message_length, *core_message_length); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + RETURN_INVALID_CONTEXT_IF_NULL(signature); + if (session_context->nonce_values.api_major_version < ODK_FIRST_VERSION) { + /* If we are talking to an old license server, then we only sign the message + * body. */ + const uint8_t* message_body = message + *core_message_length; + const size_t message_body_length = message_length - *core_message_length; + if (OPKI_CheckKey(session_context->mac_key_client, MAC_KEY_CLIENT)) { + /* Sign with mac key client if it is available. */ + result = OPKI_GenerateSignatureWithMacKeyClient( + session_context, message_body, message_body_length, signature, + signature_length); + } else { + /* Otherwise, this is a release request. Use the usage entry to sign. */ + result = OPKI_SignReleaseRequest(session_context->session_id, + message_body, message_body_length, + signature, signature_length); + } + } else { + /* Sign the entire message. */ + if (OPKI_CheckKey(session_context->mac_key_client, MAC_KEY_CLIENT)) { + result = OPKI_GenerateSignatureWithMacKeyClient(session_context, message, + message_length, signature, + signature_length); + } else { + result = + OPKI_SignReleaseRequest(session_context->session_id, message, + message_length, signature, signature_length); + } + } + if (result != OEMCrypto_SUCCESS) { + if (result != OEMCrypto_ERROR_SHORT_BUFFER) { + LOGE( + "Failed to generate signature or sign release request with result: " + "%u", + result); + } + return result; + } + return OPKI_SetStatePostCall(session_context, + API_PREPANDSIGN_RENEWAL_REQUEST); +} + +static OEMCryptoResult LoadKeysNoSignature( + OEMCryptoSession* session, const uint8_t* message, size_t message_length, + OEMCrypto_Substring enc_mac_keys_iv, OEMCrypto_Substring enc_mac_keys, + size_t num_keys, const OEMCrypto_KeyObject* key_array, + OEMCrypto_Substring pst, OEMCrypto_Substring srm_restriction_data, + OEMCrypto_LicenseType license_type) { + ABORT_IF(g_opk_system_state != SYSTEM_INITIALIZED, + "OEMCrypto is not yet initialized"); + ABORT_IF_NULL(session); + ABORT_IF_NULL(message); + ABORT_IF_ZERO(message_length); + ABORT_IF_NULL(key_array); + RETURN_INVALID_CONTEXT_IF_NULL(key_array); + RETURN_INVALID_CONTEXT_IF_ZERO(num_keys); + + if (session->response_loaded) { + return OEMCrypto_ERROR_LICENSE_RELOAD; + } + + if (!IsSubstrInRange(message_length, enc_mac_keys_iv, true) || + !IsSubstrInRange(message_length, enc_mac_keys, true) || + !IsSubstrInRange(message_length, pst, true) || + !IsSubstrInRange(message_length, srm_restriction_data, true)) { + LOGE("Invalid substring range"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + /* Key control block is not encrypted starting OEMCrypto 16.5 */ + bool allow_null_iv = (session->nonce_values.api_major_version > 16 || + (session->nonce_values.api_major_version == 16 && + session->nonce_values.api_minor_version > 4)); + for (size_t i = 0; i < num_keys; i++) { + if (!IsSubstrInRange(message_length, key_array[i].key_id, false) || + !IsSubstrInRange(message_length, key_array[i].key_data, false) || + !IsSubstrInRange(message_length, key_array[i].key_data_iv, false) || + !IsSubstrInRange(message_length, key_array[i].key_control, false) || + !IsSubstrInRange(message_length, key_array[i].key_control_iv, + allow_null_iv) || + key_array[i].key_id.length == 0 || + key_array[i].key_id.length > KEY_ID_MAX_SIZE || + key_array[i].key_data_iv.length < KEY_IV_SIZE || + key_array[i].key_control.length < KEY_CONTROL_SIZE || + (key_array[i].key_control_iv.length != KEY_IV_SIZE && + key_array[i].key_control_iv.length != 0)) { + LOGE("Invalid substring range for key index: %zu", i); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + } + /* If enc_mac_keys or enc_mac_keys_iv is present, both must be present and + must be the right sizes. */ + if ((enc_mac_keys_iv.length > 0 || enc_mac_keys.length > 0) && + (enc_mac_keys_iv.length < KEY_IV_SIZE || + enc_mac_keys.length < 2 * MAC_KEY_SIZE)) { + LOGE("Invalid encrypted MAC key or MAC key IV length"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + /* Check to see if the MAC Keys' IV is directly before the MAC Keys, which is + forbidden. */ + if (enc_mac_keys.offset >= KEY_IV_SIZE && enc_mac_keys.length > 0 && + crypto_memcmp(message + enc_mac_keys.offset - KEY_IV_SIZE, + message + enc_mac_keys_iv.offset, KEY_IV_SIZE) == 0) { + LOGE("The bytes before the mac keys shouldn't be the same as the iv"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + /* If we've already loaded in a license and it isn't the current license type, + fail. */ + if (session->state == SESSION_LICENSE_LOADED && + session->license_type != license_type) { + LOGE( + "License type doesn't match. License type already loaded: %u, license " + "type to be loaded: %u", + session->license_type, license_type); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + OEMCryptoResult result = OEMCrypto_SUCCESS; + session->license_type = license_type; + session->refresh_valid = + IsSubstrInRange(message_length, enc_mac_keys, false) && + IsSubstrInRange(message_length, enc_mac_keys_iv, false); + + session->valid_srm_version = false; + if (srm_restriction_data.length != 0) { + static const uint8_t kSrmVerification[8] = {'H', 'D', 'C', 'P', + 'D', 'A', 'T', 'A'}; + uint32_t minimum_version_network; + if (srm_restriction_data.length < + sizeof(kSrmVerification) + sizeof(minimum_version_network)) { + LOGE("Bad SRM restriction data length: %zu", srm_restriction_data.length); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + if (crypto_memcmp(message + srm_restriction_data.offset, kSrmVerification, + sizeof(kSrmVerification))) { + LOGE("SRM verification failed"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + const uint8_t* const minimum_version_pointer = + message + srm_restriction_data.offset + sizeof(kSrmVerification); + memcpy(&minimum_version_network, minimum_version_pointer, + sizeof(minimum_version_network)); + const uint32_t minimum_version = oemcrypto_be32toh(minimum_version_network); + + uint32_t current_version = 0; + result = WTPI_GetCurrentSRMVersion(¤t_version); + if (result != OEMCrypto_SUCCESS) { + LOGE("Unable to fetch SRM data with error: %u", result); + } else if (current_version < minimum_version) { + LOGE("SRM Version is too small %u, required: %u", current_version, + minimum_version); + } else { + session->valid_srm_version = true; + } + } + + /* Decrypt and install keys in key object. Each key will have a key control + block. They will all have the same nonce. */ + session->num_content_keys = 0; + session->num_entitlement_keys = 0; + for (size_t i = 0; i < num_keys; i++) { + result = OPKI_InstallKey( + session, message + key_array[i].key_id.offset, + key_array[i].key_id.length, message + key_array[i].key_data.offset, + key_array[i].key_data.length, message + key_array[i].key_data_iv.offset, + message + key_array[i].key_control.offset, + message + key_array[i].key_control_iv.offset); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to install key with result: %u, index = %zu", result, i); + break; + } + if (session->license_type == OEMCrypto_ContentLicense) { + session->num_content_keys++; + } else { + session->num_entitlement_keys++; + } + } + if (result != OEMCrypto_SUCCESS) goto cleanup; + if (session->refresh_valid) { + result = OPKI_UpdateMacKeys(session, message + enc_mac_keys.offset, + message + enc_mac_keys_iv.offset); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to update mac keys with result: %u", result); + goto cleanup; + } + } + + /* If we have loaded a usage entry, or we should have loaded an entry, check + * it. */ + if (OPKI_GetUsageEntryStatus(session->session_id) != USAGE_ENTRY_NONE || + pst.length > 0) { + if (pst.length == 0) { + LOGE("Usage entry exists but PST doesn't"); + result = OEMCrypto_ERROR_INVALID_CONTEXT; + goto cleanup; + } + switch (OPKI_GetUsageEntryStatus(session->session_id)) { + case USAGE_ENTRY_NONE: + default: + /* PST specified, but no usage entry loaded. */ + LOGE("PST specified, but no usage entry loaded"); + result = OEMCrypto_ERROR_INVALID_CONTEXT; + break; + case USAGE_ENTRY_NEW: + /* This was the first time loading the license. Copy the pst and mac + * keys. */ + result = OPKI_SetUsageEntryPST(session->session_id, + message + pst.offset, pst.length); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to set usage entry PST with result: %u", result); + break; + } + if (session->mac_key_server == NULL || + session->mac_key_client == NULL) { + result = OEMCrypto_ERROR_INVALID_CONTEXT; + break; + } + result = OPKI_SetUsageEntryMacKeys(session->session_id, + session->mac_key_server->key_handle, + session->mac_key_client->key_handle); + break; + case USAGE_ENTRY_DEACTIVATED: + result = OEMCrypto_ERROR_LICENSE_INACTIVE; + break; + case USAGE_ENTRY_LOADED: + /* This was not the first time loading the license. Verify the pst and + * mac keys. */ + result = OPKI_VerfiyUsageEntryPST(session->session_id, + message + pst.offset, pst.length); + if (result != OEMCrypto_SUCCESS) { + LOGE( + "Failed to verify usage entry PST with result: %u, session_id = " + "%u", + result, session->session_id); + break; + } + if (session->mac_key_server == NULL || + session->mac_key_client == NULL) { + result = OEMCrypto_ERROR_INVALID_CONTEXT; + break; + } + result = OPKI_VerifyUsageEntryMacKeys( + session->session_id, session->mac_key_server->key_handle, + session->mac_key_client->key_handle); + if (result != OEMCrypto_SUCCESS) { + LOGE( + "Failed to verify usage entry mac keys with result: %u, " + "session_id = %u", + result, session->session_id); + } + break; + } + } +cleanup:; + OEMCryptoResult free_key_result = + OPKI_FreeKeyFromTable(&session->encryption_key); + if (result == OEMCrypto_SUCCESS) result = free_key_result; + if (result != OEMCrypto_SUCCESS || !session->refresh_valid) { + /* Mac keys must be freed in every case except when the call is successful + AND we are given new mac keys. In that case, the new keys should be + preserved in the session for future RefreshKeys calls. */ + free_key_result = FreeMacKeys(session); + if (result == OEMCrypto_SUCCESS) result = free_key_result; + } + if (result != OEMCrypto_SUCCESS) { + FreeContentAndEntitlementKeys(session); + session->state = SESSION_INVALID; + } + if (result == OEMCrypto_SUCCESS) session->response_loaded = true; + + return result; +} + +OEMCryptoResult OEMCrypto_LoadLicense(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + size_t core_message_length, + const uint8_t* signature, + size_t signature_length) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_LOADLICENSE); + if (result != OEMCrypto_SUCCESS) return result; + RETURN_INVALID_CONTEXT_IF_NULL(message); + RETURN_INVALID_CONTEXT_IF_ZERO(message_length); + RETURN_INVALID_CONTEXT_IF_NULL(signature); + if (signature_length != SHA256_DIGEST_LENGTH) { + LOGE("signature_length is not the length of a SHA256 digest"); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + /* Check state before we check signature. State is change in + * LoadKeysNoSignature. */ + if (session_context->response_loaded) { + return OEMCrypto_ERROR_LICENSE_RELOAD; + } + ODK_ParsedLicense parsed_response; + const bool initial_license_load = + (OPKI_GetUsageEntryStatus(session_context->session_id) != + USAGE_ENTRY_LOADED); + const bool usage_entry_present = + (OPKI_GetUsageEntryStatus(session_context->session_id) != + USAGE_ENTRY_NONE); + result = ODK_ParseLicense(message, message_length, core_message_length, + initial_license_load, usage_entry_present, + &session_context->timer_limits, + &session_context->clock_values, + &session_context->nonce_values, &parsed_response); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to parse license with result: %u", result); + return result; + } + + if (parsed_response.license_type != OEMCrypto_ContentLicense && + parsed_response.license_type != OEMCrypto_EntitlementLicense) { + LOGE("Invalid license type: %u", parsed_response.license_type); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + result = OPKI_VerifySignatureWithMacKeyServer(session_context, message, + message_length, signature); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to verify signature with server mac key, error: %u", result); + return result; + } + + const uint8_t* message_body = message + core_message_length; + const size_t message_body_length = message_length - core_message_length; + result = LoadKeysNoSignature( + session_context, message_body, message_body_length, + parsed_response.enc_mac_keys_iv, parsed_response.enc_mac_keys, + parsed_response.key_array_length, parsed_response.key_array, + parsed_response.pst, parsed_response.srm_restriction_data, + parsed_response.license_type); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to load keys with result: %u", result); + return result; + } + return OPKI_SetStatePostCall(session_context, API_LOADLICENSE); +} + +OEMCryptoResult OEMCrypto_LoadKeys( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, + OEMCrypto_Substring enc_mac_keys_iv, OEMCrypto_Substring enc_mac_keys, + size_t key_array_length, const OEMCrypto_KeyObject* key_array, + OEMCrypto_Substring pst, OEMCrypto_Substring srm_restriction_data, + OEMCrypto_LicenseType license_type) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_LOADKEYS); + if (result != OEMCrypto_SUCCESS) return result; + + RETURN_INVALID_CONTEXT_IF_NULL(message); + RETURN_INVALID_CONTEXT_IF_ZERO(message_length); + RETURN_INVALID_CONTEXT_IF_NULL(signature); + if (signature_length != SHA256_DIGEST_LENGTH) { + LOGE("signature_length is not the length of a SHA256 digest"); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + RETURN_INVALID_CONTEXT_IF_NULL(key_array); + RETURN_INVALID_CONTEXT_IF_ZERO(key_array_length); + + if (license_type != OEMCrypto_ContentLicense && + license_type != OEMCrypto_EntitlementLicense) { + LOGE("Invalid license type: %u", license_type); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + /* Check state before we check signature. State is change in + * LoadKeysNoSignature. */ + if (session_context->response_loaded) { + return OEMCrypto_ERROR_LICENSE_RELOAD; + } + + result = OPKI_VerifySignatureWithMacKeyServer(session_context, message, + message_length, signature); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to verify signature with server mac key, error: %u", result); + return result; + } + + result = LoadKeysNoSignature( + session_context, message, message_length, enc_mac_keys_iv, enc_mac_keys, + key_array_length, key_array, pst, srm_restriction_data, license_type); + if (result != OEMCrypto_SUCCESS) return result; + + /* Update clock values using the duration for the loaded key. */ + SymmetricKey* key = NULL; + const int kIndex = 0; + switch (session_context->license_type) { + case OEMCrypto_ContentLicense: + if (session_context->num_content_keys == 0) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + key = session_context->content_keys[kIndex]; + break; + case OEMCrypto_EntitlementLicense: + if (session_context->num_entitlement_keys == 0) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + key = session_context->entitlement_keys[kIndex]; + break; + default: + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + const uint32_t duration = key ? key->key_control_block.duration : 0; + uint64_t now; + result = WTPI_GetTrustedTime(&now); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get trusted time with result: %u", result); + return result; + } + result = ODK_InitializeV15Values( + &session_context->timer_limits, &session_context->clock_values, + &session_context->nonce_values, duration, now); + if (result != OEMCrypto_SUCCESS) return result; + return OPKI_SetStatePostCall(session_context, API_LOADKEYS); +} + +OEMCryptoResult OEMCrypto_LoadEntitledContentKeys( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + size_t key_array_length, + const OEMCrypto_EntitledContentKeyObject* key_array) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (OPKI_GetSessionType(session) != SESSION_TYPE_ENTITLED_KEY) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoEntitledKeySession* key_session = NULL; + OEMCryptoResult result = + GetSessionContext(session, &session_context, &key_session); + if (result != OEMCrypto_SUCCESS) return result; + ABORT_IF(session_context == NULL, + "Failed to get the entitlement session context."); + ABORT_IF(key_session == NULL, + "Failed to get the entitled key session context."); + result = OPKI_CheckStatePreCall(session_context, API_LOADENTITLEDCONTENTKEYS); + if (result != OEMCrypto_SUCCESS) return result; + + RETURN_INVALID_CONTEXT_IF_NULL(message); + RETURN_INVALID_CONTEXT_IF_ZERO(message_length); + RETURN_INVALID_CONTEXT_IF_NULL(key_array); + RETURN_INVALID_CONTEXT_IF_ZERO(key_array_length); + + if (session_context->license_type != OEMCrypto_EntitlementLicense || + session_context->num_entitlement_keys == 0) { + LOGE("Not a entitlement license or no keys found"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + for (size_t i = 0; i < key_array_length; i++) { + if (!IsSubstrInRange(message_length, key_array[i].entitlement_key_id, + false) || + !IsSubstrInRange(message_length, key_array[i].content_key_id, false) || + !IsSubstrInRange(message_length, key_array[i].content_key_data, + false) || + !IsSubstrInRange(message_length, key_array[i].content_key_data_iv, + false) || + key_array[i].entitlement_key_id.length > KEY_ID_MAX_SIZE || + key_array[i].content_key_id.length > KEY_ID_MAX_SIZE || + key_array[i].content_key_data.length != KEY_SIZE_128 || + key_array[i].content_key_data_iv.length != KEY_IV_SIZE) { + LOGE("Invalid substring range for key index: %zu", i); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + } + + result = OEMCrypto_SUCCESS; + for (size_t i = 0; i < key_array_length; i++) { + const OEMCrypto_EntitledContentKeyObject key_object = key_array[i]; + SymmetricKey* entitlement_key = OPKI_FindKeyFromTable( + session_context, false, message + key_object.entitlement_key_id.offset, + key_object.entitlement_key_id.length); + if (!OPKI_CheckKey(entitlement_key, ENTITLEMENT_KEY)) { + result = OEMCrypto_KEY_NOT_ENTITLED; + goto cleanup; + } + + /* Initialized the index to which the entitled content key to be loaded. */ + uint32_t entitled_content_key_index = + key_session->num_entitled_content_keys; + /* It prefers to reuse an existing content key index that is entitled by the + * same entitlement key if one exists already, but will allocate a new index + * if there are none that can be reused. + * The block below searches whether there is an existing content key + * entitled by the same entitlement key, and will reuse the index to load + * the new content key if there is one. */ + for (uint32_t k = 0; k < key_session->num_entitled_content_keys; k++) { + /* Gets the entitlement key of the entitled content key at index k. */ + const EntitlementKeyInfo* key_info = &key_session->entitlement_keys[k]; + if (key_info->entitlement_key_id_size == entitlement_key->key_id_size && + memcmp(key_info->entitlement_key_id, entitlement_key->key_id, + key_info->entitlement_key_id_size) == 0) { + /* Found an existing content key which is already entitled by this + * entitlement key. Since each entitlement key can only be referenced + * by one content key within an entitled key session, the previous + * content key will be freed and the index will be reused to load the + * new content key. */ + entitled_content_key_index = k; + result = OPKI_FreeKeyFromTable( + &key_session->entitled_content_keys[entitled_content_key_index]); + if (result != OEMCrypto_SUCCESS) goto cleanup; + break; + } + } + + /* If we're not updating an existing content key and we can't add any more, + we fail. */ + const bool adding_new_content_key = + entitled_content_key_index == key_session->num_entitled_content_keys; + if (adding_new_content_key && + key_session->num_entitled_content_keys == CONTENT_KEYS_PER_SESSION) { + result = OEMCrypto_ERROR_INSUFFICIENT_RESOURCES; + goto cleanup; + } + SymmetricKey** content_key_ptr = + &key_session->entitled_content_keys[entitled_content_key_index]; + result = OPKI_CreateKey(content_key_ptr, CONTENT_KEY, + (KeySize)key_object.content_key_data.length); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to create CONTENT_KEY with result: %u", result); + goto cleanup; + } + SymmetricKey* content_key = *content_key_ptr; + result = WTPI_K1_AESDecryptAndCreateKeyHandle( + entitlement_key->key_handle, + message + key_object.content_key_data.offset, + key_object.content_key_data.length, + message + key_object.content_key_data_iv.offset, CONTENT_KEY, + &(content_key->key_handle)); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to decrypt and create content key handle with result %u", + result); + goto cleanup; + } + + memcpy(content_key->key_id, message + key_object.content_key_id.offset, + key_object.content_key_id.length); + content_key->key_id_size = key_object.content_key_id.length; + content_key->session_key_index = entitled_content_key_index; + + /* Associate the new content key with its entitlement key. Fill out + * entitlement key info of the new content key in key session. */ + content_key->is_entitled_content_key = true; + memcpy(key_session->entitlement_keys[entitled_content_key_index] + .entitlement_key_id, + entitlement_key->key_id, entitlement_key->key_id_size); + key_session->entitlement_keys[entitled_content_key_index] + .entitlement_key_id_size = entitlement_key->key_id_size; + + if (adding_new_content_key) { + key_session->num_entitled_content_keys++; + } + } // for loop over key_array + + result = OPKI_SetStatePostCall(session_context, API_LOADENTITLEDCONTENTKEYS); + +cleanup: + if (result != OEMCrypto_SUCCESS) { + FreeEntitledContentKeys(key_session); + FreeContentAndEntitlementKeys(session_context); + session_context->state = SESSION_INVALID; + } + return result; +} + +OEMCryptoResult OEMCrypto_LoadRenewal(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + size_t core_message_length, + const uint8_t* signature, + size_t signature_length) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_LOADRENEWAL); + if (result != OEMCrypto_SUCCESS) return result; + + RETURN_INVALID_CONTEXT_IF_NULL(message); + RETURN_INVALID_CONTEXT_IF_ZERO(message_length); + RETURN_INVALID_CONTEXT_IF_NULL(signature); + if (signature_length != SHA256_DIGEST_LENGTH) { + LOGE("signature_length is not the length of a SHA256 digest"); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + + result = OPKI_VerifySignatureWithMacKeyServer(session_context, message, + message_length, signature); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to verify signature with server mac key, error: %u", result); + return result; + } + + uint64_t now; + result = WTPI_GetTrustedTime(&now); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get trusted time with result: %u", result); + return result; + } + uint64_t* timer_value = NULL; + result = ODK_ParseRenewal(message, message_length, core_message_length, + &session_context->nonce_values, now, + &session_context->timer_limits, + &session_context->clock_values, timer_value); + if (result == ODK_TIMER_EXPIRED) return OEMCrypto_ERROR_KEY_EXPIRED; + if (result == ODK_SET_TIMER || result == ODK_DISABLE_TIMER) { + return OPKI_SetStatePostCall(session_context, API_LOADRENEWAL); + } + return result; +} + +OEMCryptoResult OEMCrypto_RefreshKeys( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, size_t key_array_length, + const OEMCrypto_KeyRefreshObject* key_array) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_REFRESHKEYS); + if (result != OEMCrypto_SUCCESS) return result; + + RETURN_INVALID_CONTEXT_IF_NULL(message); + RETURN_INVALID_CONTEXT_IF_ZERO(message_length); + RETURN_INVALID_CONTEXT_IF_NULL(signature); + if (signature_length != SHA256_DIGEST_LENGTH) { + LOGE("signature_length is not the length of a SHA256 digest"); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + RETURN_INVALID_CONTEXT_IF_NULL(key_array); + RETURN_INVALID_CONTEXT_IF_ZERO(key_array_length); + + /* We only use the first key object to update the entire license. Since we + * know num_keys > 0 after the last if statement, we can assume index is not + * out of bounds. */ + static const size_t kIndex = 0; + if (!IsSubstrInRange(message_length, key_array[kIndex].key_id, true) || + !IsSubstrInRange(message_length, key_array[kIndex].key_control, false) || + !IsSubstrInRange(message_length, key_array[kIndex].key_control_iv, + true) || + key_array[kIndex].key_id.length > KEY_ID_MAX_SIZE || + key_array[kIndex].key_control.length < KEY_CONTROL_SIZE || + (key_array[kIndex].key_control_iv.length != 0 && + key_array[kIndex].key_control_iv.length != KEY_IV_SIZE)) { + LOGE("Invalid substring range for key: %zu", kIndex); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + if (!session_context->refresh_valid) { + LOGE("Invalid key refresh"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + result = OPKI_VerifySignatureWithMacKeyServer(session_context, message, + message_length, signature); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to verify signature with server mac key, error: %u", result); + return result; + } + + result = OEMCrypto_SUCCESS; + const uint8_t* key_id = NULL; + const uint8_t* key_control_iv = NULL; + if (key_array[kIndex].key_id.length != 0) { + key_id = message + key_array[kIndex].key_id.offset; + } + if (key_array[kIndex].key_control_iv.length != 0) { + key_control_iv = message + key_array[kIndex].key_control_iv.offset; + } + result = OPKI_RefreshKey( + session_context, key_id, key_array[kIndex].key_id.length, + message + key_array[kIndex].key_control.offset, key_control_iv); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to refresh key with result: %u", result); + return result; + } + return OPKI_SetStatePostCall(session_context, API_REFRESHKEYS); +} + +OEMCryptoResult OEMCrypto_QueryKeyControl(OEMCrypto_SESSION session, + const uint8_t* content_key_id, + size_t content_key_id_length, + uint8_t* key_control_block, + size_t* key_control_block_length) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoEntitledKeySession* key_session = NULL; + OEMCryptoResult result = + GetSessionContext(session, &session_context, &key_session); + if (result != OEMCrypto_SUCCESS) return result; + ABORT_IF(session_context == NULL, + "GetSessionContext() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_QUERYKEYCONTROL); + if (result != OEMCrypto_SUCCESS) return result; + + RETURN_INVALID_CONTEXT_IF_NULL(key_control_block_length); + + if (*key_control_block_length < KEY_CONTROL_SIZE) { + *key_control_block_length = KEY_CONTROL_SIZE; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + RETURN_INVALID_CONTEXT_IF_NULL(content_key_id); + RETURN_INVALID_CONTEXT_IF_ZERO(content_key_id_length); + RETURN_INVALID_CONTEXT_IF_NULL(key_control_block); + if (content_key_id_length > KEY_ID_MAX_SIZE) { + LOGE("content_key_id_length is too large"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + *key_control_block_length = KEY_CONTROL_SIZE; + SymmetricKey* content_key = NULL; + if (OPKI_GetSessionType(session) == SESSION_TYPE_ENTITLED_KEY) { + content_key = OPKI_FindEntitledContentKeyFromTable( + key_session, content_key_id, content_key_id_length); + } else { + content_key = OPKI_FindKeyFromTable(session_context, true, content_key_id, + content_key_id_length); + } + if (content_key == NULL) return OEMCrypto_ERROR_NO_CONTENT_KEY; + + KeyControlBlock control_block; + result = OPKI_GetKeyControlBlock(content_key, session_context, key_session, + &control_block); + if (result != OEMCrypto_SUCCESS) return result; + const uint32_t new_block[4] = { + 0, /* Verification optional. */ + oemcrypto_htobe32(control_block.duration), + 0, /* Nonce optional. */ + oemcrypto_htobe32(control_block.control_bits), + }; + if (sizeof(new_block) != KEY_CONTROL_SIZE) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + memcpy(key_control_block, new_block, KEY_CONTROL_SIZE); + + return OPKI_SetStatePostCall(session_context, API_QUERYKEYCONTROL); +} + +OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session, + const uint8_t* content_key_id, + size_t content_key_id_length, + OEMCryptoCipherMode cipher_mode) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoEntitledKeySession* key_session = NULL; + OEMCryptoResult result = + GetSessionContext(session, &session_context, &key_session); + if (result != OEMCrypto_SUCCESS) return result; + ABORT_IF(session_context == NULL, + "GetSessionContext() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_SELECTKEY); + if (result != OEMCrypto_SUCCESS) return result; + + RETURN_INVALID_CONTEXT_IF_NULL(content_key_id); + RETURN_INVALID_CONTEXT_IF_ZERO(content_key_id_length); + if (content_key_id_length > KEY_ID_MAX_SIZE) { + LOGE("content_key_id_length is too large"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (cipher_mode != OEMCrypto_CipherMode_CENC && + cipher_mode != OEMCrypto_CipherMode_CBCS) { + LOGE("Unrecognized cipher_mode %u", (unsigned int)cipher_mode); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + OEMCryptoSessionType session_type = OPKI_GetSessionType(session); + SymmetricKey* content_key = NULL; + if (session_type == SESSION_TYPE_ENTITLED_KEY) { + if (key_session->num_entitled_content_keys == 0) { + LOGE("Selected entitled key session has no content keys."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + content_key = OPKI_FindEntitledContentKeyFromTable( + key_session, content_key_id, content_key_id_length); + } else { + if (session_context->num_content_keys == 0) { + LOGE("Selected session has no content keys."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + content_key = OPKI_FindKeyFromTable(session_context, true, content_key_id, + content_key_id_length); + } + if (!content_key) return OEMCrypto_ERROR_NO_CONTENT_KEY; + KeyControlBlock key_control_block; + result = OPKI_GetKeyControlBlock(content_key, session_context, key_session, + &key_control_block); + if (result != OEMCrypto_SUCCESS) return result; + if (WTPI_HasAnalogDisplay() && WTPI_SupportsCGMS_A() && + (key_control_block.control_bits & CONTROL_OBSERVE_CGMS)) { + result = WTPI_ApplyCGMS(key_control_block.control_bits & CONTROL_CGMS_MASK); + if (result != OEMCrypto_SUCCESS) return result; + } + if (session_type == SESSION_TYPE_ENTITLED_KEY) { + key_session->current_entitled_content_key_index = + content_key->session_key_index; + } else { + session_context->current_content_key_index = content_key->session_key_index; + } + content_key->cipher_mode = cipher_mode; + + return OPKI_SetStatePostCall(session_context, API_SELECTKEY); +} + +/* This function will only be called if at least some of the data is encrypted, + meaning this function can assume a key is required. */ +static OEMCryptoResult DecryptCENCInternal( + OEMCrypto_SESSION session, const OEMCrypto_SampleDescription* samples, + size_t samples_length, const OEMCrypto_CENCEncryptPatternDesc* pattern) { + ABORT_IF(g_opk_system_state != SYSTEM_INITIALIZED, + "The system is uninitialized, and this was not caught at a higher " + "level."); + OEMCryptoSession* session_context = NULL; + OEMCryptoEntitledKeySession* key_session = NULL; + OEMCryptoResult result = + GetSessionContext(session, &session_context, &key_session); + if (result != OEMCrypto_SUCCESS) return result; + ABORT_IF(session_context == NULL, + "GetSessionContext() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_DECRYPTCENC); + if (result != OEMCrypto_SUCCESS) return result; + RETURN_INVALID_CONTEXT_IF_NULL(samples); + RETURN_INVALID_CONTEXT_IF_ZERO(samples_length); + RETURN_INVALID_CONTEXT_IF_NULL(pattern); + + const size_t max_length = WTPI_MaxSampleSize(); + /* Iterate through all the samples and validate them before doing any decrypt. + */ + for (size_t sample_index = 0; sample_index < samples_length; ++sample_index) { + const OEMCrypto_SampleDescription* const sample = &(samples[sample_index]); + if (sample->buffers.input_data == NULL || + sample->buffers.input_data_length == 0) { + LOGE("Sample %zu has invalid input data.", sample_index); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + /* Iterate through all the subsamples and sum their lengths. */ + size_t subsample_length_tally = 0; + for (size_t subsample_index = 0; + subsample_index < sample->subsamples_length; ++subsample_index) { + const OEMCrypto_SubSampleDescription* const subsample = + &(sample->subsamples[subsample_index]); + size_t length; + if (OPK_AddOverflowUX(subsample->num_bytes_clear, + subsample->num_bytes_encrypted, &length)) { + return OEMCrypto_ERROR_BUFFER_TOO_LARGE; + } + if (OPK_AddOverflowUX(subsample_length_tally, length, + &subsample_length_tally)) { + return OEMCrypto_ERROR_BUFFER_TOO_LARGE; + } + } + if (max_length != 0 && subsample_length_tally > max_length) { + return OEMCrypto_ERROR_BUFFER_TOO_LARGE; + } + if (subsample_length_tally != sample->buffers.input_data_length) { + LOGE("Sample %zu's length and subsample lengths do not match.", + sample_index); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + } + + result = OPKI_DecryptSamples(session_context, key_session, samples, + samples_length, pattern); + if (result == OEMCrypto_ERROR_SHORT_BUFFER) { + /* OPKI_DecryptSamples may pass back an OEMCrypto_ERROR_SHORT_BUFFER that + originated in OPK_CheckOutputBounds(). This is not a valid return code + for DecryptCENC(), so we translate it here. */ + LOGE("Output buffer size is too small"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } else if (result != OEMCrypto_SUCCESS) { + return result; + } + + return OPKI_SetStatePostCall(session_context, API_DECRYPTCENC); +} + +OEMCryptoResult OEMCrypto_DecryptCENC( + OEMCrypto_SESSION session, const OEMCrypto_SampleDescription* samples, + size_t samples_length, const OEMCrypto_CENCEncryptPatternDesc* pattern) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + RETURN_INVALID_CONTEXT_IF_NULL(samples); + RETURN_INVALID_CONTEXT_IF_ZERO(samples_length); + RETURN_INVALID_CONTEXT_IF_NULL(pattern); + /* DecryptCENC may be called for calls that could go through CopyBuffer. We + separate those calls out here so that the decrypt code path only has to + handle cases where a key is expected. */ + bool has_encrypted_data = false; + for (size_t sample_index = 0; sample_index < samples_length; ++sample_index) { + if (samples[sample_index].subsamples_length != 1 || + samples[sample_index].subsamples[0].num_bytes_encrypted > 0) { + has_encrypted_data = true; + break; + } + } + if (has_encrypted_data) + return DecryptCENCInternal(session, samples, samples_length, pattern); + + const size_t max_length = WTPI_MaxSampleSize(); + for (size_t sample_index = 0; sample_index < samples_length; ++sample_index) { + const OEMCrypto_SampleDescription* const sample = &(samples[sample_index]); + ABORT_IF(sample->subsamples_length != 1, + "Sample %zu with multiple subsamples was somehow not treated as " + "encrypted.", + sample_index); + const OEMCrypto_SubSampleDescription* const subsample = + &(sample->subsamples[0]); + if (max_length != 0 && subsample->num_bytes_clear > max_length) { + LOGE("Sample size too large: %zu bytes. Max allowed: %zu", + subsample->num_bytes_clear, max_length); + return OEMCrypto_ERROR_BUFFER_TOO_LARGE; + } + if (subsample->num_bytes_clear != sample->buffers.input_data_length) { + LOGE("Sample %zu's length and subsample length do not match.", + sample_index); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + OEMCryptoResult result = OEMCrypto_CopyBuffer( + session, sample->buffers.input_data, sample->buffers.input_data_length, + &sample->buffers.output_descriptor, subsample->subsample_flags); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to copy buffer with result: %u", result); + return result; + } + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult OEMCrypto_CopyBuffer( + OEMCrypto_SESSION session UNUSED, const uint8_t* data_addr, + size_t data_length, const OEMCrypto_DestBufferDesc* dest_buffer_desc, + uint8_t subsample_flags UNUSED) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + /* We don't need to check the session, CopyBuffer should be allowed for all + session states since it doesn't access any state data. */ + RETURN_INVALID_CONTEXT_IF_NULL(data_addr); + RETURN_INVALID_CONTEXT_IF_ZERO(data_length); + RETURN_INVALID_CONTEXT_IF_NULL(dest_buffer_desc); + OEMCryptoResult result; + + OPK_OutputBuffer output_buffer; + size_t offset; + result = OPK_ParseDestBufferDesc(dest_buffer_desc, &output_buffer, &offset); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to parse dest buffer description with result: %u", result); + return result; + } + ABORT_IF(!OPK_IsOutputBufferValid(&output_buffer), "Invalid output buffer."); + result = OPK_CheckOutputBounds(&output_buffer, offset, data_length); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to check output bounds with result: %u", result); + return result; + } + return WTPI_C1_CopyToOutputBuffer(data_addr, data_length, &output_buffer, + offset); +} + +OEMCryptoResult OEMCrypto_Generic_Encrypt( + OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length, + const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoEntitledKeySession* key_session = NULL; + OEMCryptoResult result = + GetSessionContext(session, &session_context, &key_session); + if (result != OEMCrypto_SUCCESS) return result; + ABORT_IF(session_context == NULL, + "GetSessionContext() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_GENERICENCRYPT); + if (result != OEMCrypto_SUCCESS) return result; + + RETURN_INVALID_CONTEXT_IF_NULL(in_buffer); + RETURN_INVALID_CONTEXT_IF_NULL(out_buffer); + RETURN_INVALID_CONTEXT_IF_ZERO(buffer_length); + RETURN_INVALID_CONTEXT_IF_NULL(iv); + if (algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) { + LOGE("Invalid algorithm."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (buffer_length % AES_BLOCK_SIZE != 0) { + LOGE("buffer_length of %zu is not a multiple of the crypto block length.", + buffer_length); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + size_t max_buffer_size = WTPI_MaxBufferSizeForGenericCrypto(); + if (max_buffer_size != 0 && buffer_length > max_buffer_size) { + return OEMCrypto_ERROR_BUFFER_TOO_LARGE; + } + SymmetricKey* current_content_key = NULL; + result = OPKI_GetCurrentContentKey(session_context, key_session, + ¤t_content_key); + if (result != OEMCrypto_SUCCESS) return result; + if (current_content_key == NULL || + current_content_key->key_size != KEY_SIZE_128) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + result = OPKI_CheckCurrentContentKeyUsage(session_context, key_session, + CONTROL_ALLOW_ENCRYPT, + OPK_CLEAR_INSECURE_OUTPUT_BUFFER); + if (result != OEMCrypto_SUCCESS) return result; + result = OPKI_UpdatePlaybackTimeAndUsageEntryStatus(session_context); + if (result != OEMCrypto_SUCCESS) return result; + result = WTPI_C1_AESCBCEncrypt(current_content_key->key_handle, in_buffer, + buffer_length, iv, out_buffer); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to AES CBC encrypt with result: %u", result); + return result; + } + + return OPKI_SetStatePostCall(session_context, API_GENERICENCRYPT); +} + +OEMCryptoResult OEMCrypto_Generic_Decrypt( + OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length, + const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoEntitledKeySession* key_session = NULL; + OEMCryptoResult result = + GetSessionContext(session, &session_context, &key_session); + if (result != OEMCrypto_SUCCESS) return result; + ABORT_IF(session_context == NULL, + "GetSessionContext() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_GENERICDECRYPT); + if (result != OEMCrypto_SUCCESS) return result; + + RETURN_INVALID_CONTEXT_IF_NULL(in_buffer); + RETURN_INVALID_CONTEXT_IF_NULL(out_buffer); + RETURN_INVALID_CONTEXT_IF_ZERO(buffer_length); + RETURN_INVALID_CONTEXT_IF_NULL(iv); + if (algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) { + LOGE("Invalid algorithm."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (buffer_length % AES_BLOCK_SIZE != 0) { + LOGE("buffer_length of %zu is not a multiple of the crypto block length.", + buffer_length); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + size_t max_buffer_size = WTPI_MaxBufferSizeForGenericCrypto(); + if (max_buffer_size != 0 && buffer_length > max_buffer_size) { + return OEMCrypto_ERROR_BUFFER_TOO_LARGE; + } + SymmetricKey* current_content_key = NULL; + result = OPKI_GetCurrentContentKey(session_context, key_session, + ¤t_content_key); + if (result != OEMCrypto_SUCCESS) return result; + if (current_content_key == NULL || + current_content_key->key_size != KEY_SIZE_128) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + result = OPKI_CheckCurrentContentKeyUsage(session_context, key_session, + CONTROL_ALLOW_DECRYPT, + OPK_CLEAR_INSECURE_OUTPUT_BUFFER); + if (result != OEMCrypto_SUCCESS) return result; + result = OPKI_UpdatePlaybackTimeAndUsageEntryStatus(session_context); + if (result != OEMCrypto_SUCCESS) return result; + KeySize key_size; + result = WTPI_K1_GetKeySize(current_content_key->key_handle, &key_size); + if (result != OEMCrypto_SUCCESS) return result; + result = + WTPI_C1_AESCBCDecrypt(current_content_key->key_handle, (size_t)key_size, + in_buffer, buffer_length, iv, out_buffer); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to AES CBC decrypt with result: %u", result); + return result; + } + + return OPKI_SetStatePostCall(session_context, API_GENERICDECRYPT); +} + +OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + uint8_t* signature, + size_t* signature_length) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoEntitledKeySession* key_session = NULL; + OEMCryptoResult result = + GetSessionContext(session, &session_context, &key_session); + if (result != OEMCrypto_SUCCESS) return result; + ABORT_IF(session_context == NULL, + "GetSessionContext() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_GENERICSIGN); + if (result != OEMCrypto_SUCCESS) return result; + + RETURN_INVALID_CONTEXT_IF_NULL(signature_length); + + if (*signature_length < SHA256_DIGEST_LENGTH) { + *signature_length = SHA256_DIGEST_LENGTH; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + RETURN_INVALID_CONTEXT_IF_NULL(in_buffer); + RETURN_INVALID_CONTEXT_IF_ZERO(buffer_length); + RETURN_INVALID_CONTEXT_IF_NULL(signature); + if (algorithm != OEMCrypto_HMAC_SHA256) { + LOGE("Invalid algorithm."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + *signature_length = SHA256_DIGEST_LENGTH; + + size_t max_buffer_size = WTPI_MaxBufferSizeForGenericCrypto(); + if (max_buffer_size != 0 && buffer_length > max_buffer_size) { + return OEMCrypto_ERROR_BUFFER_TOO_LARGE; + } + SymmetricKey* current_content_key = NULL; + result = OPKI_GetCurrentContentKey(session_context, key_session, + ¤t_content_key); + if (result != OEMCrypto_SUCCESS) return result; + /* Only allow 256-bit keys for signing and verifying. */ + if (current_content_key == NULL || + current_content_key->key_size != KEY_SIZE_256) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + result = OPKI_CheckCurrentContentKeyUsage(session_context, key_session, + CONTROL_ALLOW_SIGN, + OPK_CLEAR_INSECURE_OUTPUT_BUFFER); + if (result != OEMCrypto_SUCCESS) return result; + result = OPKI_UpdatePlaybackTimeAndUsageEntryStatus(session_context); + if (result != OEMCrypto_SUCCESS) return result; + result = WTPI_C1_HMAC_SHA256(current_content_key->key_handle, in_buffer, + buffer_length, signature); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to HMAC-SHA256 with result: %u", result); + return result; + } + + return OPKI_SetStatePostCall(session_context, API_GENERICSIGN); +} + +OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + const uint8_t* signature, + size_t signature_length) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoEntitledKeySession* key_session = NULL; + OEMCryptoResult result = + GetSessionContext(session, &session_context, &key_session); + if (result != OEMCrypto_SUCCESS) return result; + ABORT_IF(session_context == NULL, + "GetSessionContext() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_GENERICVERIFY); + if (result != OEMCrypto_SUCCESS) return result; + + RETURN_INVALID_CONTEXT_IF_NULL(in_buffer); + RETURN_INVALID_CONTEXT_IF_ZERO(buffer_length); + RETURN_INVALID_CONTEXT_IF_NULL(signature); + if (signature_length != SHA256_DIGEST_LENGTH) { + LOGE("signature_length is not the length of a SHA256 digest"); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + if (algorithm != OEMCrypto_HMAC_SHA256) { + LOGE("Invalid algorithm."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + size_t max_buffer_size = WTPI_MaxBufferSizeForGenericCrypto(); + if (max_buffer_size != 0 && buffer_length > max_buffer_size) { + return OEMCrypto_ERROR_BUFFER_TOO_LARGE; + } + SymmetricKey* current_content_key = NULL; + result = OPKI_GetCurrentContentKey(session_context, key_session, + ¤t_content_key); + if (result != OEMCrypto_SUCCESS) return result; + /* Only allow 256-bit keys for signing and verifying. */ + if (current_content_key == NULL || + current_content_key->key_size != KEY_SIZE_256) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + result = OPKI_CheckCurrentContentKeyUsage(session_context, key_session, + CONTROL_ALLOW_VERIFY, + OPK_CLEAR_INSECURE_OUTPUT_BUFFER); + if (result != OEMCrypto_SUCCESS) return result; + result = OPKI_UpdatePlaybackTimeAndUsageEntryStatus(session_context); + if (result != OEMCrypto_SUCCESS) return result; + result = WTPI_C1_HMAC_SHA256_Verify(current_content_key->key_handle, + in_buffer, buffer_length, signature); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to HMAC-SHA256 verify with result: %u", result); + return result; + } + + return OPKI_SetStatePostCall(session_context, API_GENERICVERIFY); +} + +OEMCryptoResult OEMCrypto_WrapKeyboxOrOEMCert( + const uint8_t* rot UNUSED, size_t rotLength UNUSED, + uint8_t* wrappedRot UNUSED, size_t* wrappedRotLength UNUSED, + const uint8_t* transportKey UNUSED, size_t transportKeyLength UNUSED) { + // TODO(fredgc, ncbray, marcone): One installation option is that there an + // installation tool that uses a system key to unwrap the keybox and then + // re-wraps it using a device key. This wrapped keybox can be stored on the + // file system. I think we don't plan to use this. If we do, then Widevine + // needs to implement this function, and we have to agree on an porting layer + // interface for: + // decrypt w/system key -> encrypt w/device key -> return buffer. + // + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult OEMCrypto_InstallKeyboxOrOEMCert(const uint8_t* keybox, + size_t length) { + // TODO(b/180530495): We currently only support keyboxes. + return WTPI_UnwrapValidateAndInstallKeybox(keybox, length); +} + +OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(void) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ProvisioningError; + } + return WTPI_GetProvisioningMethod(); +} + +OEMCryptoResult OEMCrypto_IsKeyboxOrOEMCertValid(void) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const OEMCrypto_ProvisioningMethod provisioning_method = + WTPI_GetProvisioningMethod(); + if (provisioning_method == OEMCrypto_OEMCertificate) { + // TODO(b/180530495): Implement this. + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } else if (provisioning_method == OEMCrypto_Keybox) { + return WTPI_ValidateKeybox(); + } else { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } +} + +OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // GetDeviceID handles validation of the parameters, so we do not validate + // them here. + return GetDeviceID(deviceID, idLength); +} + +OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* key_data, + size_t* key_data_length) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (WTPI_GetProvisioningMethod() != OEMCrypto_Keybox) { + LOGD("This function is only valid on Provisioning 2.0 devices"); + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + + RETURN_INVALID_CONTEXT_IF_NULL(key_data_length); + if (*key_data_length < KEYBOX_KEY_DATA_SIZE) { + *key_data_length = KEYBOX_KEY_DATA_SIZE; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + RETURN_INVALID_CONTEXT_IF_NULL(key_data); + *key_data_length = KEYBOX_KEY_DATA_SIZE; + return WTPI_GetKeyDataFromKeybox(key_data, *key_data_length); +} + +OEMCryptoResult OEMCrypto_LoadTestKeybox(const uint8_t* buffer, size_t length) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (WTPI_GetProvisioningMethod() != OEMCrypto_Keybox) { + LOGD("This function is only valid on Provisioning 2.0 devices"); + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + RETURN_INVALID_CONTEXT_IF_NULL(buffer); + if (length < sizeof(WidevineKeybox)) { + LOGE("length too short"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + return WTPI_LoadTestKeybox(buffer, length); +} + +OEMCryptoResult OEMCrypto_GetOEMPublicCertificate( + uint8_t* public_cert UNUSED, size_t* public_cert_length UNUSED) { + // TODO(b/180530495): implement this. + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult OEMCrypto_LoadOEMPrivateKey(OEMCrypto_SESSION session) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (WTPI_GetProvisioningMethod() != OEMCrypto_OEMCertificate) { + LOGD("This function is only valid on Provisioning 3.0 devices"); + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_LOADOEMPRIVATEKEY); + if (result != OEMCrypto_SUCCESS) return result; + /* This API sets the session state to indicate that the OEM private key is + * ready for use. It doesn't actually load it. It is best to load the OEM + * private key in memory only when being used. */ + return OPKI_SetStatePostCall(session_context, API_LOADOEMPRIVATEKEY); +} + +OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + RETURN_INVALID_CONTEXT_IF_NULL(randomData); + RETURN_INVALID_CONTEXT_IF_ZERO(dataLength); + return WTPI_C1_RandomBytes(randomData, dataLength); +} + +uint32_t OEMCrypto_APIVersion(void) { return API_MAJOR_VERSION; } + +uint32_t OEMCrypto_MinorAPIVersion(void) { return API_MINOR_VERSION; } + +OEMCryptoResult OEMCrypto_BuildInformation(char* buffer, + size_t* buffer_length) { + RETURN_INVALID_CONTEXT_IF_NULL(buffer); + RETURN_INVALID_CONTEXT_IF_NULL(buffer_length); + const size_t max_length = strnlen(BUILD_INFO(), 128); + if (*buffer_length < max_length) { + *buffer_length = max_length; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *buffer_length = max_length; + memcpy(buffer, BUILD_INFO(), *buffer_length); + return OEMCrypto_SUCCESS; +} + +uint8_t OEMCrypto_Security_Patch_Level(void) { return 0; } + +OEMCrypto_Security_Level OEMCrypto_SecurityLevel(void) { + return WTPI_GetSecurityLevel(); +} + +OEMCryptoResult OEMCrypto_GetHDCPCapability( + OEMCrypto_HDCP_Capability* current, OEMCrypto_HDCP_Capability* maximum) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + RETURN_INVALID_CONTEXT_IF_NULL(current); + RETURN_INVALID_CONTEXT_IF_NULL(maximum); + + *current = WTPI_CurrentHDCPCapability(); + *maximum = WTPI_MaxHDCPCapability(); + return OEMCrypto_SUCCESS; +} + +bool OEMCrypto_SupportsUsageTable(void) { return true; } + +size_t OEMCrypto_MaximumUsageTableHeaderSize(void) { + return MAX_NUMBER_OF_USAGE_ENTRIES; +} + +bool OEMCrypto_IsAntiRollbackHwPresent(void) { + return WTPI_IsAntiRollbackHWPresent(); +} + +OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(size_t* count) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + RETURN_INVALID_CONTEXT_IF_NULL(count); + + uint32_t num_open_sessions = 0; + OEMCryptoResult result = OPKI_NumberOfOpenSessions(&num_open_sessions); + *count = num_open_sessions; + return result; +} + +OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(size_t* max) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + RETURN_INVALID_CONTEXT_IF_NULL(max); + + *max = OPKI_MaxNumberOfSessions(); + return OEMCrypto_SUCCESS; +} + +uint32_t OEMCrypto_SupportedCertificates(void) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return 0; + } + return WTPI_SupportedCertificates(); +} + +OEMCryptoResult OEMCrypto_GetCurrentSRMVersion(uint16_t* version) { + RETURN_INVALID_CONTEXT_IF_NULL(version); + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +uint32_t OEMCrypto_GetAnalogOutputFlags(void) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return 0; + } + uint32_t analog_bits = 0; + if (WTPI_HasAnalogDisplay()) analog_bits |= 0x1; + if (WTPI_CanDisableAnalogDisplay()) analog_bits |= 0x2; + if (WTPI_SupportsCGMS_A()) analog_bits |= 0x4; + return analog_bits; +} + +uint32_t OEMCrypto_ResourceRatingTier(void) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return 0; + } + return WTPI_GetResourceRatingTier(); +} + +OEMCryptoResult OEMCrypto_LoadDRMPrivateKey(OEMCrypto_SESSION session, + OEMCrypto_PrivateKeyType key_type, + const uint8_t* wrapped_drm_key, + size_t wrapped_drm_key_length) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + AsymmetricKeyType drm_key_type; + if (!OPKI_PrivateKeyTypeToAsymmetricKey(key_type, &drm_key_type)) { + LOGE("Invalid key_type: %d", key_type); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_LOADDRMPRIVATEKEY); + if (result != OEMCrypto_SUCCESS) return result; + + WTPI_AsymmetricKey_Handle private_key_handle; + uint32_t allowed_schemes; + result = WTPI_UnwrapIntoAsymmetricKeyHandle( + wrapped_drm_key, wrapped_drm_key_length, drm_key_type, + &private_key_handle, &allowed_schemes); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to unwrap DRM private key into key handle with result: %u", + result); + return result; + } + size_t private_key_size; + result = WTPI_GetSignatureSize(private_key_handle, &private_key_size); + WTPI_FreeAsymmetricKeyHandle(private_key_handle); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get DRM private key size with result: %u", result); + return result; + } + + result = OPKI_LoadDRMKey(session_context, drm_key_type, wrapped_drm_key, + wrapped_drm_key_length, private_key_size, + allowed_schemes); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to load DRM key"); + goto cleanup; + } + result = OPKI_SetStatePostCall(session_context, API_LOADDRMPRIVATEKEY); + +cleanup:; + OEMCryptoResult free_key_result = FreeMacAndEncryptionKeys(session_context); + if (result == OEMCrypto_SUCCESS) result = free_key_result; + if (result != OEMCrypto_SUCCESS) { + OPKI_FreeAsymmetricKeyFromTable(&session_context->drm_private_key); + session_context->state = SESSION_INVALID; + } + return result; +} + +OEMCryptoResult OEMCrypto_LoadTestRSAKey(void) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult OEMCrypto_GenerateRSASignature( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length, + RSA_Padding_Scheme padding_scheme) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_GENERATERSASIGNATURE); + if (result != OEMCrypto_SUCCESS) return result; + + RETURN_INVALID_CONTEXT_IF_NULL(message); + RETURN_INVALID_CONTEXT_IF_ZERO(message_length); + RETURN_INVALID_CONTEXT_IF_NULL(signature_length); + + if (padding_scheme == kSign_PKCS1_Block1 && message_length > 83) { + LOGE("message_length is too long for the given padding_scheme."); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + + if (session_context->state == SESSION_LOAD_OEM_RSA_KEY && + padding_scheme == kSign_RSASSA_PSS) { + // SignMessageWithOEMPrivateKey handles signature being NULL, so we + // intentionally do not check it here. + result = OEMCrypto_ERROR_NOT_IMPLEMENTED; + // TODO(b/180530495): implement this. + // SignMessageWithOEMPrivateKey(message, message_length, signature, + // signature_length); + if (result != OEMCrypto_SUCCESS) return result; + } else if (session_context->state == SESSION_LOAD_DRM_RSA_KEY) { + if ((session_context->allowed_schemes & padding_scheme) != padding_scheme) { + LOGE("Invalid padding scheme: %u", session_context->allowed_schemes); + return OEMCrypto_ERROR_INVALID_KEY; + } + WTPI_AsymmetricKey_Handle private_key_handle; + uint32_t allowed_schemes; + AsymmetricKey* private_key = session_context->drm_private_key; + result = WTPI_UnwrapIntoAsymmetricKeyHandle( + private_key->wrapped_key, private_key->wrapped_key_length, + private_key->key_type, &private_key_handle, &allowed_schemes); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to unwrap DRM private key into key handle with result: %u", + result); + return result; + } + // WTPI_RSASign handles signature being NULL, so we intentionally do not + // check it here. + result = WTPI_RSASign(private_key_handle, message, message_length, + signature, signature_length, padding_scheme); + WTPI_FreeAsymmetricKeyHandle(private_key_handle); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to generate RSA signature with result: %u", result); + return result; + } + } else { + LOGE( + "Invalid session state or padding scheme. session state: %#x, padding " + "scheme: %u", + session_context->state, padding_scheme); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + return OPKI_SetStatePostCall(session_context, API_GENERATERSASIGNATURE); +} + +OEMCryptoResult OEMCrypto_LoadProvisioning( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + size_t core_message_length, const uint8_t* signature, + size_t signature_length, uint8_t* wrapped_private_key, + size_t* wrapped_private_key_length) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_LOADPROVISIONING); + if (result != OEMCrypto_SUCCESS) return result; + + RETURN_INVALID_CONTEXT_IF_NULL(message); + RETURN_INVALID_CONTEXT_IF_ZERO(message_length); + RETURN_INVALID_CONTEXT_IF_NULL(signature); + RETURN_INVALID_CONTEXT_IF_ZERO(signature_length); + if (signature_length != SHA256_DIGEST_LENGTH) { + LOGE("signature_length is not the length of a SHA256 digest"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + RETURN_INVALID_CONTEXT_IF_NULL(wrapped_private_key_length); + + uint8_t device_id[ODK_DEVICE_ID_LEN_MAX] = {0}; + size_t device_id_length = ODK_DEVICE_ID_LEN_MAX; + result = GetDeviceID(device_id, &device_id_length); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get device id with result: %u", result); + return result; + } + ODK_ParsedProvisioning parsed_response; + result = ODK_ParseProvisioning(message, message_length, core_message_length, + &session_context->nonce_values, device_id, + device_id_length, &parsed_response); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to parse provisioning with result: %u", result); + return result; + } + + AsymmetricKeyType drm_key_type; + if (!OPKI_PrivateKeyTypeToAsymmetricKey(parsed_response.key_type, + &drm_key_type)) { + LOGE("Unexpected key type: %#x", parsed_response.key_type); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (parsed_response.enc_private_key.length > PKCS8_DRM_KEY_MAX_SIZE) { + LOGE("Encrypted private key length is too large to handle."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (parsed_response.enc_private_key_iv.length != KEY_IV_SIZE) { + LOGE("Encrypted private key iv has invalid length: %zu", + parsed_response.enc_private_key_iv.length); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + size_t buffer_size = 0; + result = WTPI_GetWrappedAsymmetricKeySize( + parsed_response.enc_private_key.length, drm_key_type, &buffer_size); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get wrapped DRM key size with result: %u", result); + return result; + } + ABORT_IF_ZERO(buffer_size); + if (wrapped_private_key == NULL || + *wrapped_private_key_length < buffer_size) { + *wrapped_private_key_length = buffer_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + /* Tell caller how much space we used. */ + *wrapped_private_key_length = buffer_size; + + const uint8_t* message_body = message + core_message_length; + const OEMCrypto_ProvisioningMethod provisioning_method = + WTPI_GetProvisioningMethod(); + if (provisioning_method == OEMCrypto_Keybox) { + result = RewrapDeviceDRMKeyKeybox( + session_context, message, message_length, signature, signature_length, + message_body + parsed_response.enc_private_key.offset, + parsed_response.enc_private_key.length, + message_body + parsed_response.enc_private_key_iv.offset, drm_key_type, + wrapped_private_key, *wrapped_private_key_length); + } else if (provisioning_method == OEMCrypto_OEMCertificate) { + result = RewrapDeviceDRMKeyOEMCert( + session_context, + message_body + parsed_response.encrypted_message_key.offset, + parsed_response.encrypted_message_key.length, + message_body + parsed_response.enc_private_key.offset, + parsed_response.enc_private_key.length, + message_body + parsed_response.enc_private_key_iv.offset, drm_key_type, + wrapped_private_key, *wrapped_private_key_length); + } else { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (result != OEMCrypto_SUCCESS) return result; + return OPKI_SetStatePostCall(session_context, API_LOADPROVISIONING); +} + +OEMCryptoResult OEMCrypto_CreateUsageTableHeader(uint8_t* header_buffer, + size_t* header_buffer_length) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // OPKI_CreateUsageTableHeader handles validation of the parameters, so we do + // not validate them here. + return OPKI_CreateUsageTableHeader(header_buffer, header_buffer_length); +} + +OEMCryptoResult OEMCrypto_LoadUsageTableHeader(const uint8_t* header_buffer, + size_t header_buffer_length) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // OPKI_LoadUsageTableHeader handles validation of the parameters, so we do + // not validate them here. + return OPKI_LoadUsageTableHeader(header_buffer, header_buffer_length); +} + +OEMCryptoResult OEMCrypto_CreateNewUsageEntry(OEMCrypto_SESSION session, + uint32_t* usage_entry_number) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_CREATENEWUSAGEENTRY); + if (result != OEMCrypto_SUCCESS) return result; + + // OPKI_CreateNewUsageEntry handles validation of the parameters, so we do not + // validate them here. + result = + OPKI_CreateNewUsageEntry(session_context->session_id, usage_entry_number); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to create new usage entry with result: %u, session_id = %u", + result, session_context->session_id); + return result; + } + return OPKI_SetStatePostCall(session_context, API_CREATENEWUSAGEENTRY); +} + +OEMCryptoResult OEMCrypto_LoadUsageEntry(OEMCrypto_SESSION session, + uint32_t usage_entry_number, + const uint8_t* buffer, + size_t buffer_length) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + RETURN_INVALID_CONTEXT_IF_NULL(buffer); + RETURN_INVALID_CONTEXT_IF_ZERO(buffer_length); + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_LOADUSAGEENTRY); + if (result != OEMCrypto_SUCCESS) return result; + + result = OPKI_LoadUsageEntry(session_context->session_id, + &session_context->clock_values, + usage_entry_number, buffer, buffer_length); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to load usage entry with result: %u, session_id = %u", result, + session_context->session_id); + return result; + } + return OPKI_SetStatePostCall(session_context, API_LOADUSAGEENTRY); +} + +OEMCryptoResult OEMCrypto_UpdateUsageEntry(OEMCrypto_SESSION session, + uint8_t* header_buffer, + size_t* header_buffer_length, + uint8_t* entry_buffer, + size_t* entry_buffer_length) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_UPDATEUSAGEENTRY); + if (result != OEMCrypto_SUCCESS) return result; + + // OPKI_UpdateUsageEntry handles validation of the parameters, so we do not + // validate them here. + result = OPKI_UpdateUsageEntry( + session_context->session_id, &session_context->clock_values, + header_buffer, header_buffer_length, entry_buffer, entry_buffer_length); + if (result != OEMCrypto_SUCCESS) { + if (result != OEMCrypto_ERROR_SHORT_BUFFER) { + LOGE("Failed to update usage entry with result: %u, session_id = %u", + result, session_context->session_id); + } + return result; + } + return OPKI_SetStatePostCall(session_context, API_UPDATEUSAGEENTRY); +} + +OEMCryptoResult OEMCrypto_DeactivateUsageEntry(OEMCrypto_SESSION session, + const uint8_t* pst, + size_t pst_length) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_DEACTIVATEUSAGEENTRY); + if (result != OEMCrypto_SUCCESS) return result; + + // OPKI_DeactivateUsageEntry handles validation of the parameters, so we do + // not validate them here. + result = OPKI_DeactivateUsageEntry(session_context->session_id, + &session_context->clock_values, pst, + pst_length); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to deactivate usage entry with result: %u, session_id = %u", + result, session_context->session_id); + return result; + } + return OPKI_SetStatePostCall(session_context, API_DEACTIVATEUSAGEENTRY); +} + +OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session, + const uint8_t* pst, size_t pst_length, + uint8_t* buffer, size_t* buffer_length) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_REPORTUSAGE); + if (result != OEMCrypto_SUCCESS) return result; + + WTPI_K1_SymmetricKey_Handle mac_key_client = + OPKI_CheckKey(session_context->mac_key_client, MAC_KEY_CLIENT) + ? session_context->mac_key_client->key_handle + : NULL; + // OPKI_ReportUsage handles validation of the parameters, so we do not + // validate them here. + result = OPKI_ReportUsage(session_context->session_id, mac_key_client, pst, + pst_length, buffer, buffer_length); + if (result != OEMCrypto_SUCCESS) return result; + return OPKI_SetStatePostCall(session_context, API_REPORTUSAGE); +} + +OEMCryptoResult OEMCrypto_MoveEntry(OEMCrypto_SESSION session, + uint32_t new_index) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_MOVEENTRY); + if (result != OEMCrypto_SUCCESS) return result; + + result = OPKI_MoveEntry(session_context->session_id, new_index); + if (result != OEMCrypto_SUCCESS) { + LOGE( + "Failed to move entry with result: %u, session_id = %u, new_index = %u", + result, session_context->session_id, new_index); + return result; + } + return OPKI_SetStatePostCall(session_context, API_MOVEENTRY); +} + +OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(uint32_t new_entry_count, + uint8_t* header_buffer, + size_t* header_buffer_length) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // OPKI_ShrinkUsageTableHeader handles validation of the parameters, so we do + // not validate them here. + return OPKI_ShrinkUsageTableHeader(new_entry_count, header_buffer, + header_buffer_length); +} + +OEMCryptoResult OEMCrypto_RemoveSRM(void) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +uint32_t OEMCrypto_SupportsDecryptHash(void) { + return OEMCrypto_CRC_Clear_Buffer; +} + +OEMCryptoResult OEMCrypto_SetDecryptHash(OEMCrypto_SESSION session, + uint32_t frame_number, + const uint8_t* hash, + size_t hash_length) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoEntitledKeySession* key_session = NULL; + OEMCryptoResult result = + GetSessionContext(session, &session_context, &key_session); + if (result != OEMCrypto_SUCCESS) return result; + ABORT_IF(session_context == NULL, + "GetSessionContext() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_SETDECRYPTHASH); + if (result != OEMCrypto_SUCCESS) return result; + RETURN_INVALID_CONTEXT_IF_NULL(hash); + + DecryptHash* decrypt_hash; + OEMCryptoSessionType session_type = OPKI_GetSessionType(session); + switch (session_type) { + case SESSION_TYPE_OEMCRYPTO: + decrypt_hash = &session_context->decrypt_hash; + break; + case SESSION_TYPE_ENTITLED_KEY: + decrypt_hash = &key_session->decrypt_hash; + break; + default: + LOGE("Invalid session type: %u", session_type); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + const size_t expected_hash_length = sizeof(decrypt_hash->given_hash); + if (hash_length < expected_hash_length) return OEMCrypto_ERROR_SHORT_BUFFER; + if (hash_length > expected_hash_length) { + LOGE("Bad hash_length: %zu, expected: %zu", hash_length, + expected_hash_length); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + decrypt_hash->compute_hash = true; + decrypt_hash->current_frame_number = frame_number; + /* This is safe since it's host-to-host. */ + memcpy(&decrypt_hash->given_hash, hash, hash_length); + + return OPKI_SetStatePostCall(session_context, API_SETDECRYPTHASH); +} + +OEMCryptoResult OEMCrypto_GetHashErrorCode(OEMCrypto_SESSION session, + uint32_t* failed_frame_number) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoEntitledKeySession* key_session = NULL; + OEMCryptoResult result = + GetSessionContext(session, &session_context, &key_session); + if (result != OEMCrypto_SUCCESS) return result; + ABORT_IF(session_context == NULL, + "GetSessionContext() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_GETHASHERRORCODE); + if (result != OEMCrypto_SUCCESS) return result; + RETURN_INVALID_CONTEXT_IF_NULL(failed_frame_number); + + DecryptHash* decrypt_hash; + OEMCryptoSessionType session_type = OPKI_GetSessionType(session); + switch (session_type) { + case SESSION_TYPE_OEMCRYPTO: + decrypt_hash = &session_context->decrypt_hash; + break; + case SESSION_TYPE_ENTITLED_KEY: + decrypt_hash = &key_session->decrypt_hash; + break; + default: + LOGE("Invalid session type: %u", session_type); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (decrypt_hash->hash_error != OEMCrypto_SUCCESS) { + *failed_frame_number = decrypt_hash->bad_frame_number; + } + + result = OPKI_SetStatePostCall(session_context, API_GETHASHERRORCODE); + if (result != OEMCrypto_SUCCESS) return result; + return decrypt_hash->hash_error; +} + +OEMCryptoResult OEMCrypto_AllocateSecureBuffer( + OEMCrypto_SESSION session UNUSED, size_t buffer_size, + OEMCrypto_DestBufferDesc* output_descriptor, int* secure_fd) { + RETURN_INVALID_CONTEXT_IF_ZERO(buffer_size); + RETURN_INVALID_CONTEXT_IF_NULL(output_descriptor); + RETURN_INVALID_CONTEXT_IF_NULL(secure_fd); + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult OEMCrypto_FreeSecureBuffer( + OEMCrypto_SESSION session UNUSED, + OEMCrypto_DestBufferDesc* output_descriptor, int secure_fd UNUSED) { + RETURN_INVALID_CONTEXT_IF_NULL(output_descriptor); + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult OEMCrypto_CreateEntitledKeySession( + OEMCrypto_SESSION entitlement_session, OEMCrypto_SESSION* key_session) { + RETURN_INVALID_CONTEXT_IF_NULL(key_session); + if (OPKI_GetSessionType(entitlement_session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = + OPKI_GetSession(entitlement_session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + entitlement_session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = + OPKI_CheckStatePreCall(session_context, API_CREATEENTITLEDKEYSESSION); + if (result != OEMCrypto_SUCCESS) return result; + result = OPKI_GrabEntitledKeySession(key_session, entitlement_session); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to grab and initialize entitled key session with result: %u", + result); + return result; + } + return OPKI_SetStatePostCall(session_context, API_CREATEENTITLEDKEYSESSION); +} + +OEMCryptoResult OEMCrypto_RemoveEntitledKeySession( + OEMCrypto_SESSION key_session) { + if (OPKI_GetSessionType(key_session) != SESSION_TYPE_ENTITLED_KEY) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoEntitledKeySession* key_session_context = NULL; + OEMCryptoResult result = + GetSessionContext(key_session, &session_context, &key_session_context); + if (result != OEMCrypto_SUCCESS) return result; + ABORT_IF(session_context == NULL, + "Failed to get the entitlement session context."); + ABORT_IF(key_session_context == NULL, + "Failed to get the entitled key session context."); + return OPKI_FreeEntitledKeySession(key_session); +} + +OEMCryptoResult OEMCrypto_GenerateOTARequest(OEMCrypto_SESSION session UNUSED, + uint8_t* buffer UNUSED, + size_t* buffer_length, + uint32_t use_test_key UNUSED) { + RETURN_INVALID_CONTEXT_IF_NULL(buffer_length); + /* Implementing this method is optional. It is only applicable + * a specific set of devices. */ + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult OEMCrypto_ProcessOTAKeybox(OEMCrypto_SESSION session UNUSED, + const uint8_t* buffer, + size_t buffer_length, + uint32_t use_test_key UNUSED) { + RETURN_INVALID_CONTEXT_IF_NULL(buffer); + RETURN_INVALID_CONTEXT_IF_ZERO(buffer_length); + /* Implementing this method is optional. It is only applicable + * a specific set of devices. */ + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult OEMCrypto_GetBootCertificateChain( + uint8_t* bcc, size_t* bcc_size, uint8_t* additional_signature UNUSED, + size_t* additional_signature_size) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + RETURN_INVALID_CONTEXT_IF_NULL(bcc_size); + RETURN_INVALID_CONTEXT_IF_NULL(additional_signature_size); + + *additional_signature_size = 0; + return WTPI_GetBootCertificateChain(bcc, bcc_size); +} + +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_PrivateKeyType* key_type) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + RETURN_INVALID_CONTEXT_IF_NULL(public_key_size); + RETURN_INVALID_CONTEXT_IF_NULL(public_key_signature_size); + RETURN_INVALID_CONTEXT_IF_NULL(wrapped_private_key_size); + RETURN_INVALID_CONTEXT_IF_NULL(key_type); + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = + OPKI_CheckStatePreCall(session_context, API_GENERATECERTIFICATEKEYPAIR); + if (result != OEMCrypto_SUCCESS) return result; + + // Generate the key pair and fill the key type. + const size_t required_signature_size = 1024; + AsymmetricKeyType generated_key_type; + result = WTPI_GenerateRandomCertificateKeyPair( + &generated_key_type, wrapped_private_key, wrapped_private_key_size, + public_key, public_key_size); + if (public_key_signature == NULL || + *public_key_signature_size < required_signature_size || + result == OEMCrypto_ERROR_SHORT_BUFFER) { + /* The wrapped_private_key_size and public_key_size should have been + * correctly set by WTPI_GenerateRandomCertificateKeyPair. Also set the + * required public_key_signature_size in this case. */ + *public_key_signature_size = required_signature_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to generate certificate key pair result: %u", result); + return result; + } + if (!OPKI_AsymmetricKeyToPrivateKeyType(generated_key_type, key_type)) { + LOGE("Invalid key_type: %d", generated_key_type); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + // The provisioning stage is determined by whether the prov40_oem_private_key + // is installed. NOT installed: first stage; Installed: second stage. + if (session_context->prov40_oem_private_key == NULL) { + // This is for obtaining an OEM leaf cert. Sign with BCC leaf private key. + result = WTPI_DeviceKeyCoseSign1(public_key, *public_key_size, + public_key_signature, + public_key_signature_size); + if (result != OEMCrypto_SUCCESS) return result; + return OPKI_SetStatePostCall(session_context, + API_GENERATECERTIFICATEKEYPAIR); + } + + // This is the second stage for obtaining a DRM cert. Sign with the installed + // prov40_oem_private_key. + WTPI_AsymmetricKey_Handle signing_key_handle; + uint32_t allowed_schemes_unused; + AsymmetricKey* oem_private_key = session_context->prov40_oem_private_key; + result = WTPI_UnwrapIntoAsymmetricKeyHandle( + oem_private_key->wrapped_key, oem_private_key->wrapped_key_length, + oem_private_key->key_type, &signing_key_handle, &allowed_schemes_unused); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to unwrap OEM private key into key handle with result: %u", + result); + return result; + } + + // Sign the generated public key. + switch (oem_private_key->key_type) { + case DRM_ECC_PRIVATE_KEY: { + result = WTPI_ECCSign(signing_key_handle, public_key, *public_key_size, + public_key_signature, public_key_signature_size); + break; + } + case DRM_RSA_PRIVATE_KEY: { + // If the signing key is RSA, the signing key must be OEM private key. + if ((session_context->prov40_oem_allowed_schemes & kSign_RSASSA_PSS) != + kSign_RSASSA_PSS) { + LOGE("Bad prov40 OEM RSA padding scheme: %x", + session_context->prov40_oem_allowed_schemes); + result = OEMCrypto_ERROR_INVALID_KEY; + break; + } + result = WTPI_RSASign(signing_key_handle, public_key, *public_key_size, + public_key_signature, public_key_signature_size, + kSign_RSASSA_PSS); + break; + } + case PROV40_ED25519_PRIVATE_KEY: + default: + LOGE("Unexpected signing private key type"); + result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + break; + } + WTPI_FreeAsymmetricKeyHandle(signing_key_handle); + if (result != OEMCrypto_SUCCESS) { + return result; + } + return OPKI_SetStatePostCall(session_context, API_GENERATECERTIFICATEKEYPAIR); +} + +OEMCryptoResult OEMCrypto_InstallOemPrivateKey( + OEMCrypto_SESSION session, OEMCrypto_PrivateKeyType key_type, + const uint8_t* wrapped_private_key, size_t wrapped_private_key_length) { + RETURN_INVALID_CONTEXT_IF_NULL(wrapped_private_key); + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + AsymmetricKeyType oem_key_type; + if (!OPKI_PrivateKeyTypeToAsymmetricKey(key_type, &oem_key_type)) { + LOGE("Invalid key_type: %d", key_type); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF(session_context == NULL, + "OPKI_GetSession() provided invalid output."); + result = OPKI_CheckStatePreCall(session_context, API_INSTALLOEMPRIVATEKEY); + if (result != OEMCrypto_SUCCESS) return result; + + WTPI_AsymmetricKey_Handle private_key_handle; + uint32_t allowed_schemes; + result = WTPI_UnwrapIntoAsymmetricKeyHandle( + wrapped_private_key, wrapped_private_key_length, oem_key_type, + &private_key_handle, &allowed_schemes); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to unwrap OEM private key into key handle with result: %u", + result); + return result; + } + size_t private_key_size; + result = WTPI_GetSignatureSize(private_key_handle, &private_key_size); + WTPI_FreeAsymmetricKeyHandle(private_key_handle); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get DRM private key size with result: %u", result); + return result; + } + + result = OPKI_LoadProv40OEMKey( + session_context, oem_key_type, wrapped_private_key, + wrapped_private_key_length, private_key_size, allowed_schemes); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to install OEM key"); + goto cleanup; + } + result = OPKI_SetStatePostCall(session_context, API_INSTALLOEMPRIVATEKEY); + +cleanup:; + OEMCryptoResult free_key_result = FreeMacAndEncryptionKeys(session_context); + if (result == OEMCrypto_SUCCESS) result = free_key_result; + if (result != OEMCrypto_SUCCESS) { + OPKI_FreeAsymmetricKeyFromTable(&session_context->prov40_oem_private_key); + session_context->state = SESSION_INVALID; + } + return result; +} + +OEMCryptoResult OEMCrypto_ReassociateEntitledKeySession( + OEMCrypto_SESSION key_session, OEMCrypto_SESSION entitlement_session) { + if (OPKI_GetSessionType(key_session) != SESSION_TYPE_ENTITLED_KEY) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION; + } + if (OPKI_GetSessionType(entitlement_session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoEntitledKeySession* key_session_context = NULL; + OEMCryptoResult result = + OPKI_GetEntitledKeySession(key_session, &key_session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get entitled key session with result: %u, session = %u", + result, key_session); + return result; + } + OEMCryptoSession* entitlement_session_context = NULL; + /* entitlement_session must be present to be associated to. */ + result = OPKI_GetSession(entitlement_session, &entitlement_session_context); + if (result != OEMCrypto_SUCCESS || entitlement_session_context == NULL) { + LOGE("Failed to get session with result: %u, session = %u", result, + entitlement_session); + return result; + } + key_session_context->entitlement_session_id = entitlement_session; + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult OEMCrypto_LoadCasECMKeys( + OEMCrypto_SESSION session UNUSED, const uint8_t* message, + size_t message_length UNUSED, + const OEMCrypto_EntitledContentKeyObject* even_key, + const OEMCrypto_EntitledContentKeyObject* odd_key) { + RETURN_INVALID_CONTEXT_IF_NULL(message); + RETURN_INVALID_CONTEXT_IF_NULL(even_key); + RETURN_INVALID_CONTEXT_IF_NULL(odd_key); + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult OEMCrypto_ProductionReady(void) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const OPK_FeatureStatus anti_rollback_state = WTPI_IsTAAntiRollbackEnabled(); + switch (anti_rollback_state) { + case OPK_FEATURE_NOT_SUPPORTED: + LOGD("TA anti-rollback is not supported."); + break; + case OPK_FEATURE_ENABLED: + LOGD("TA anti-rollback is enabled."); + break; + case OPK_FEATURE_DISABLED: + LOGD("TA anti-rollback is disabled."); + break; + default: + ABORT("Invalid TA anti-rollback state."); + } + +#if OPK_IS_DEBUG + LOGD("OPK was built in debug mode."); +#else + LOGD( + "OPK was not built in debug mode. This message should never be logged, " + "therefore. If you are reading this message, something has gone wrong."); +#endif + + const bool wtpi_prod_ready = WTPI_IsProductionReady(); + LOGD("The WTPI reports it %s production-ready.", + wtpi_prod_ready ? "is" : "is not"); + + if (OPK_IS_DEBUG || anti_rollback_state == OPK_FEATURE_DISABLED || + !wtpi_prod_ready) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } else { + return OEMCrypto_SUCCESS; + } +} + +OEMCryptoResult OEMCrypto_Idle(OEMCrypto_IdleState state, + uint32_t os_specific_code) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return WTPI_Idle(state, os_specific_code); +} + +OEMCryptoResult OEMCrypto_Wake(void) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return WTPI_Wake(); +} + +OEMCryptoResult OEMCrypto_ReuseUsageEntry(OEMCrypto_SESSION session, + uint32_t usage_entry_number) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (OPKI_GetSessionType(session) != SESSION_TYPE_OEMCRYPTO) { + LOGE("Unexpected session type."); + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoSession* session_context = NULL; + OEMCryptoResult result = OPKI_GetSession(session, &session_context); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get session with result: %u, session = %u", result, + session); + return result; + } + ABORT_IF_NULL(session_context); + result = OPKI_CheckStatePreCall(session_context, API_REUSEUSAGEENTRY); + if (result != OEMCrypto_SUCCESS) return result; + + result = + OPKI_ReuseUsageEntry(session_context->session_id, usage_entry_number); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to create new usage entry with result: %u, session_id = %u", + result, session_context->session_id); + return result; + } + return OPKI_SetStatePostCall(session_context, API_REUSEUSAGEENTRY); +} + +OEMCryptoResult OEMCrypto_GetDTCP2Capability( + OEMCrypto_DTCP2_Capability* capability) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + RETURN_INVALID_CONTEXT_IF_NULL(capability); + *capability = WTPI_GetDTCP2Capability(); + return OEMCrypto_SUCCESS; +} + +OEMCrypto_WatermarkingSupport OEMCrypto_GetWatermarkingSupport(void) { + if (g_opk_system_state != SYSTEM_INITIALIZED) { + LOGE("OEMCrypto is not yet initialized"); + return OEMCrypto_WatermarkingError; + } + return WTPI_GetWatermarkingSupport(); +} diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_api_macros.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_api_macros.h new file mode 100644 index 0000000..8016733 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_api_macros.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_asymmetric_key_table.c b/oemcrypto/opk/oemcrypto_ta/oemcrypto_asymmetric_key_table.c new file mode 100644 index 0000000..c7d831d --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_asymmetric_key_table.c @@ -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 +#include +#include + +#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; +} diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_asymmetric_key_table.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_asymmetric_key_table.h new file mode 100644 index 0000000..b9223ef --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_asymmetric_key_table.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_check_macros.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_check_macros.h new file mode 100644 index 0000000..54a938e --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_check_macros.h @@ -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_ diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_compiler_attributes.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_compiler_attributes.h new file mode 100644 index 0000000..302241d --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_compiler_attributes.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_compiler_detection.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_compiler_detection.h new file mode 100644 index 0000000..6971a8b --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_compiler_detection.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session.c b/oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session.c new file mode 100644 index 0000000..28f282e --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session.c @@ -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 + +#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; +} diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session.h new file mode 100644 index 0000000..e61462e --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session_table.c b/oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session_table.c new file mode 100644 index 0000000..31464d2 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session_table.c @@ -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; +} diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session_table.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session_table.h new file mode 100644 index 0000000..81d56b9 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session_table.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_key.c b/oemcrypto/opk/oemcrypto_ta/oemcrypto_key.c new file mode 100644 index 0000000..2b8304d --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_key.c @@ -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 + +#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; +} diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_key.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_key.h new file mode 100644 index 0000000..57d3d67 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_key.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_key_control_block.c b/oemcrypto/opk/oemcrypto_ta/oemcrypto_key_control_block.c new file mode 100644 index 0000000..1c7257e --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_key_control_block.c @@ -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 + +#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; +} diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_key_control_block.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_key_control_block.h new file mode 100644 index 0000000..4cd1db1 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_key_control_block.h @@ -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_ diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_key_table.c b/oemcrypto/opk/oemcrypto_ta/oemcrypto_key_table.c new file mode 100644 index 0000000..c856d8a --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_key_table.c @@ -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 +#include + +#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; +} diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_key_table.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_key_table.h new file mode 100644 index 0000000..b57500d --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_key_table.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_key_types.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_key_types.h new file mode 100644 index 0000000..570937d --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_key_types.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_math.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_math.h new file mode 100644 index 0000000..e9ef285 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_math.h @@ -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 + +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_ diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_object_table.c b/oemcrypto/opk/oemcrypto_ta/oemcrypto_object_table.c new file mode 100644 index 0000000..cea8108 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_object_table.c @@ -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; +} diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_object_table.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_object_table.h new file mode 100644 index 0000000..bab0434 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_object_table.h @@ -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 +#include // for size_t +#include // 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_ diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_output.c b/oemcrypto/opk/oemcrypto_ta/oemcrypto_output.c new file mode 100644 index 0000000..623a36a --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_output.c @@ -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)); +} diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_output.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_output.h new file mode 100644 index 0000000..af100c8 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_output.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_overflow.c b/oemcrypto/opk/oemcrypto_ta/oemcrypto_overflow.c new file mode 100644 index 0000000..251ed2e --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_overflow.c @@ -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 diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_overflow.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_overflow.h new file mode 100644 index 0000000..ab0b98c --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_overflow.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_serialized_usage_table.c b/oemcrypto/opk/oemcrypto_ta/oemcrypto_serialized_usage_table.c new file mode 100644 index 0000000..84545fa --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_serialized_usage_table.c @@ -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; +} diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_serialized_usage_table.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_serialized_usage_table.h new file mode 100644 index 0000000..fcd5367 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_serialized_usage_table.h @@ -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_ diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_session.c b/oemcrypto/opk/oemcrypto_ta/oemcrypto_session.c new file mode 100644 index 0000000..9639ba7 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_session.c @@ -0,0 +1,1349 @@ +/* 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.h" + +#include + +#include "odk.h" +#include "odk_util.h" +#include "oemcrypto_api_macros.h" +#include "oemcrypto_asymmetric_key_table.h" +#include "oemcrypto_check_macros.h" +#include "oemcrypto_compiler_attributes.h" +#include "oemcrypto_key_control_block.h" +#include "oemcrypto_key_table.h" +#include "oemcrypto_math.h" +#include "oemcrypto_output.h" +#include "oemcrypto_overflow.h" +#include "oemcrypto_session_key_table.h" +#include "oemcrypto_usage_table.h" +#include "wtpi_abort_interface.h" +#include "wtpi_clock_interface_layer1.h" +#include "wtpi_crc32_interface.h" +#include "wtpi_decrypt_sample_interface.h" +#include "wtpi_logging_interface.h" +#include "wtpi_root_of_trust_interface_layer1.h" + +NO_IGNORE_RESULT static bool IsSupportedDrmKeyType(AsymmetricKeyType key_type) { + return key_type == DRM_RSA_PRIVATE_KEY || key_type == DRM_ECC_PRIVATE_KEY; +} + +OEMCryptoResult OPKI_InitializeSession(OEMCryptoSession* session, + OEMCrypto_SESSION session_id) { + if (session == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT; + session->session_id = session_id; + session->state = SESSION_INVALID; + session->drm_private_key = NULL; + session->prov40_oem_private_key = NULL; + session->mac_key_client = NULL; + session->mac_key_server = NULL; + session->encryption_key = NULL; + session->refresh_valid = false; + session->license_type = OEMCrypto_ContentLicense; + session->current_content_key_index = CONTENT_KEYS_PER_SESSION; + for (int i = 0; i < CONTENT_KEYS_PER_SESSION; i++) { + session->content_keys[i] = NULL; + } + session->num_content_keys = 0; + for (int i = 0; i < ENTITLEMENT_KEYS_PER_SESSION; i++) { + session->entitlement_keys[i] = NULL; + } + session->num_entitlement_keys = 0; + session->valid_srm_version = false; + session->timer_start = 0; + session->decrypt_hash.compute_hash = false; + session->decrypt_hash.current_hash = 0; + session->decrypt_hash.given_hash = 0; + session->decrypt_hash.current_frame_number = 0; + session->decrypt_hash.bad_frame_number = 0; + session->decrypt_hash.hash_error = OEMCrypto_SUCCESS; + session->allowed_schemes = kSign_RSASSA_PSS; + session->prov40_oem_allowed_schemes = kSign_RSASSA_PSS; + session->decrypt_started = false; + session->nonce_created = false; + session->request_signed = false; + session->response_loaded = false; + memset(session->license_request_hash, 0, ODK_SHA256_HASH_SIZE); + OEMCryptoResult result = ODK_InitializeSessionValues( + &session->timer_limits, &session->clock_values, &session->nonce_values, + API_MAJOR_VERSION, session->session_id); + return result; +} + +OEMCryptoResult OPKI_TerminateSession(OEMCryptoSession* session) { + if (session == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT; + OEMCryptoResult result = OEMCrypto_SUCCESS; + + result = OPKI_FreeAsymmetricKeyFromTable(&session->drm_private_key); + + OEMCryptoResult free_key_result = + OPKI_FreeKeyFromTable(&session->mac_key_client); + if (result == OEMCrypto_SUCCESS) result = free_key_result; + + free_key_result = OPKI_FreeKeyFromTable(&session->mac_key_server); + if (result == OEMCrypto_SUCCESS) result = free_key_result; + + free_key_result = OPKI_FreeKeyFromTable(&session->encryption_key); + if (result == OEMCrypto_SUCCESS) result = free_key_result; + + for (uint32_t i = 0; i < session->num_content_keys; i++) { + free_key_result = OPKI_FreeKeyFromTable(&session->content_keys[i]); + if (result == OEMCrypto_SUCCESS) result = free_key_result; + } + + for (uint32_t i = 0; i < session->num_entitlement_keys; i++) { + free_key_result = OPKI_FreeKeyFromTable(&session->entitlement_keys[i]); + if (result == OEMCrypto_SUCCESS) result = free_key_result; + } + + OPKI_ReleaseEntry(session->session_id); + memset(session, 0, sizeof(OEMCryptoSession)); + return result; +} + +/* State definitions and transitions are described in section: "Clarify + * OEMCrypto States and Remove Unused Functions" in + * <$CDM_DIR>/doc/Widevine_Modular_DRM_Version_16_Delta.pdf */ +OEMCryptoResult OPKI_CheckStatePreCall(OEMCryptoSession* session, + OEMCryptoSessionAPI api) { + if (session == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT; + switch (api) { + case API_OPENSESSION: + switch (session->state) { + case SESSION_INVALID: + return OEMCrypto_SUCCESS; + default: + goto err; + } + case API_GENERATENONCE: + switch (session->state) { + case (SESSION_OPENED): + case (SESSION_LOAD_DRM_RSA_KEY): + return OEMCrypto_SUCCESS; + default: + goto err; + } + case API_GENERATEDERIVEDKEYS: + switch (session->state) { + case (SESSION_OPENED): + case (SESSION_WAIT_FOR_PROVISIONING): + case (SESSION_PREPARING_REQUEST): + return OEMCrypto_SUCCESS; + default: + goto err; + } + case API_DERIVEKEYSFROMSESSIONKEY: + switch (session->state) { + case (SESSION_WAIT_FOR_PROVISIONING): + case (SESSION_WAIT_FOR_LICENSE): + case (SESSION_LOAD_DRM_RSA_KEY): + case (SESSION_USAGE_ENTRY_LOADED): + return OEMCrypto_SUCCESS; + default: + goto err; + } + case API_PREPANDSIGN_LICENSE_REQUEST: + switch (session->state) { + case (SESSION_PREPARING_REQUEST): + case (SESSION_LOAD_DRM_RSA_KEY): + return OEMCrypto_SUCCESS; + default: + goto err; + } + case API_PREPANDSIGN_PROVISION_REQUEST: + switch (session->state) { + case (SESSION_PREPARING_REQUEST): + case (SESSION_LOAD_OEM_RSA_KEY): + return OEMCrypto_SUCCESS; + default: + goto err; + } + case API_PREPANDSIGN_RENEWAL_REQUEST: + switch (session->state) { + case (SESSION_LICENSE_LOADED): + case (SESSION_EXPIRED): + case (SESSION_PLAYING): + case (SESSION_USAGE_ENTRY_LOADED): + case (SESSION_INACTIVE): /* release request */ + return OEMCrypto_SUCCESS; + default: + goto err; + } + case API_LOADPROVISIONING: + switch (session->state) { + case (SESSION_WAIT_FOR_PROVISIONING): + return OEMCrypto_SUCCESS; + default: + goto err; + } + case API_LOADOEMPRIVATEKEY: + switch (session->state) { + case (SESSION_OPENED): + case (SESSION_PREPARING_REQUEST): + return OEMCrypto_SUCCESS; + default: + goto err; + } + case API_LOADDRMPRIVATEKEY: + switch (session->state) { + case (SESSION_OPENED): + case (SESSION_PREPARING_REQUEST): + case (SESSION_USAGE_ENTRY_LOADED): + // This can happen when using testing the CDM with a pre-provisioned + // device. + case (SESSION_LOAD_DRM_RSA_KEY): + return OEMCrypto_SUCCESS; + default: + goto err; + } + case API_INSTALLOEMPRIVATEKEY: + switch (session->state) { + case (SESSION_OPENED): + case (SESSION_PREPARING_REQUEST): + case (SESSION_USAGE_ENTRY_LOADED): + return OEMCrypto_SUCCESS; + default: + goto err; + } + case API_GENERATECERTIFICATEKEYPAIR: + switch (session->state) { + case (SESSION_OPENED): + case (SESSION_PREPARING_REQUEST): + case (SESSION_USAGE_ENTRY_LOADED): + case (SESSION_INSTALL_OEM_PRIVATE_KEY): + return OEMCrypto_SUCCESS; + default: + goto err; + } + case API_GENERATERSASIGNATURE: + switch (session->state) { + case (SESSION_OPENED): + case (SESSION_LOAD_OEM_RSA_KEY): + case (SESSION_LOAD_DRM_RSA_KEY): + return OEMCrypto_SUCCESS; + default: + goto err; + } + case API_CREATENEWUSAGEENTRY: + switch (session->state) { + case (SESSION_OPENED): + case (SESSION_LOAD_DRM_RSA_KEY): + case (SESSION_WAIT_FOR_LICENSE): + return OEMCrypto_SUCCESS; + default: + goto err; + } + case API_LOADUSAGEENTRY: + switch (session->state) { + case (SESSION_OPENED): + case (SESSION_LOAD_DRM_RSA_KEY): + return OEMCrypto_SUCCESS; + default: + goto err; + } + case API_REUSEUSAGEENTRY: + switch (session->state) { + case (SESSION_OPENED): + case (SESSION_LOAD_DRM_RSA_KEY): + return OEMCrypto_SUCCESS; + default: + goto err; + } + case API_CREATEENTITLEDKEYSESSION: + switch (session->state) { + case (SESSION_OPENED): + case (SESSION_USAGE_ENTRY_LOADED): + case (SESSION_WAIT_FOR_LICENSE): + case (SESSION_LICENSE_LOADED): + case (SESSION_EXPIRED): + case (SESSION_PLAYING): + return OEMCrypto_SUCCESS; + default: + goto err; + } + case API_SELECTKEY: + case API_LOADENTITLEDCONTENTKEYS: + case API_QUERYKEYCONTROL: + switch (session->state) { + case (SESSION_USAGE_ENTRY_LOADED): + case (SESSION_LICENSE_LOADED): + case (SESSION_EXPIRED): + case (SESSION_PLAYING): + return OEMCrypto_SUCCESS; + default: + goto err; + } + case API_DEACTIVATEUSAGEENTRY: + switch (session->state) { + case (SESSION_USAGE_ENTRY_LOADED): + case (SESSION_LICENSE_LOADED): + case (SESSION_EXPIRED): + case (SESSION_PLAYING): + case (SESSION_INACTIVE): + return OEMCrypto_SUCCESS; + default: + goto err; + } + case API_REFRESHKEYS: + case API_LOADRENEWAL: + switch (session->state) { + case (SESSION_LICENSE_LOADED): + case (SESSION_EXPIRED): + case (SESSION_PLAYING): + return OEMCrypto_SUCCESS; + default: + goto err; + } + case API_DECRYPTCENC: + case API_GENERICENCRYPT: + case API_GENERICDECRYPT: + case API_GENERICSIGN: + case API_GENERICVERIFY: + switch (session->state) { + case (SESSION_OPENED): + case (SESSION_LICENSE_LOADED): + case (SESSION_LOAD_DRM_RSA_KEY): + case (SESSION_PLAYING): + return OEMCrypto_SUCCESS; + default: + goto err; + } + case API_MOVEENTRY: + switch (session->state) { + case (SESSION_USAGE_ENTRY_LOADED): + return OEMCrypto_SUCCESS; + default: + goto err; + } + case API_LOADKEYS: + case API_LOADLICENSE: + case API_REPORTUSAGE: + case API_UPDATEUSAGEENTRY: + case API_GETHASHERRORCODE: + case API_SETDECRYPTHASH: + switch (session->state) { + case (SESSION_INVALID): + goto err; + default: + return OEMCrypto_SUCCESS; + } + case API_CLOSESESSION: + return OEMCrypto_SUCCESS; + default: + LOGE("OPKI_CheckStatePreCall failed: API=%#x, current state=%#x", + (unsigned int)api, (unsigned int)(session->state)); + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } +err:; + LOGE("OPKI_CheckStatePreCall failed: API=%#x, current state=%#x", + (unsigned int)api, (unsigned int)(session->state)); + return OEMCrypto_ERROR_INVALID_CONTEXT; +} + +OEMCryptoResult OPKI_SetStatePostCall(OEMCryptoSession* session, + OEMCryptoSessionAPI api) { + if (session == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT; + switch (api) { + case API_OPENSESSION: + if (session->drm_private_key) { + session->state = SESSION_LOAD_DRM_RSA_KEY; + } else { + session->state = SESSION_OPENED; + } + break; + case API_GENERATENONCE: + session->state = SESSION_PREPARING_REQUEST; + break; + case API_PREPANDSIGN_LICENSE_REQUEST: + session->state = SESSION_WAIT_FOR_LICENSE; + break; + case API_PREPANDSIGN_PROVISION_REQUEST: + session->state = SESSION_WAIT_FOR_PROVISIONING; + break; + case API_LOADKEYS: + case API_LOADLICENSE: + case API_LOADRENEWAL: + case API_REFRESHKEYS: + session->state = SESSION_LICENSE_LOADED; + break; + case API_LOADPROVISIONING: + session->state = SESSION_PROVISIONED; + break; + case API_GENERATERSASIGNATURE: + session->state = SESSION_CAST_RECEIVER; + break; + case API_LOADUSAGEENTRY: + session->state = SESSION_USAGE_ENTRY_LOADED; + break; + case API_DEACTIVATEUSAGEENTRY: + session->state = SESSION_INACTIVE; + break; + case API_LOADDRMPRIVATEKEY: + session->state = SESSION_LOAD_DRM_RSA_KEY; + break; + case API_LOADOEMPRIVATEKEY: + session->state = SESSION_LOAD_OEM_RSA_KEY; + break; + case API_INSTALLOEMPRIVATEKEY: + session->state = SESSION_INSTALL_OEM_PRIVATE_KEY; + break; + case API_DECRYPTCENC: + case API_GENERICENCRYPT: + case API_GENERICDECRYPT: + case API_GENERICSIGN: + case API_GENERICVERIFY: + session->state = SESSION_PLAYING; + break; + case API_GENERATEDERIVEDKEYS: + case API_DERIVEKEYSFROMSESSIONKEY: + case API_PREPANDSIGN_RENEWAL_REQUEST: + case API_CREATENEWUSAGEENTRY: + case API_REUSEUSAGEENTRY: + case API_LOADENTITLEDCONTENTKEYS: + case API_SELECTKEY: + case API_QUERYKEYCONTROL: + case API_MOVEENTRY: + case API_REPORTUSAGE: + case API_UPDATEUSAGEENTRY: + case API_SETDECRYPTHASH: + case API_GETHASHERRORCODE: + case API_CREATEENTITLEDKEYSESSION: + case API_GENERATECERTIFICATEKEYPAIR: + /* State does not change. */ + break; + default: + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult OPKI_SetNonce(OEMCryptoSession* session, uint32_t nonce) { + if (session == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT; + if (session->nonce_created) return OEMCrypto_ERROR_INVALID_CONTEXT; + if (nonce == 0) return OEMCrypto_ERROR_INVALID_CONTEXT; + const OEMCryptoResult result = + ODK_SetNonceValues(&session->nonce_values, nonce); + if (result == OEMCrypto_SUCCESS) session->nonce_created = true; + return 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) { + if (session == NULL || wrapped_key == NULL || wrapped_key_length == 0 || + !IsSupportedDrmKeyType(key_type)) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + // Free any existing keys. This will only happen if we are using the test RSA + // key and then try to load an existing device certificate. + OEMCryptoResult result = + OPKI_FreeAsymmetricKeyFromTable(&session->drm_private_key); + if (result != OEMCrypto_SUCCESS) return result; + + if (key_type != DRM_RSA_PRIVATE_KEY) { + allowed_schemes = 0; + } + + result = + OPKI_CreateAsymmetricKey(&session->drm_private_key, key_type, wrapped_key, + wrapped_key_length, key_size, allowed_schemes); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to create asymmetric DRM private key with result: %u", result); + return result; + } + if (key_type == DRM_RSA_PRIVATE_KEY) { + session->allowed_schemes = allowed_schemes; + } else { + session->allowed_schemes = 0; + } + return OEMCrypto_SUCCESS; +} + +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) { + if (session == NULL || wrapped_key == NULL || wrapped_key_length == 0 || + !IsSupportedDrmKeyType(key_type)) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + // Free any existing keys. This will only happen if we are using the test RSA + // key and then try to load an existing device certificate. + OEMCryptoResult result = + OPKI_FreeAsymmetricKeyFromTable(&session->prov40_oem_private_key); + if (result != OEMCrypto_SUCCESS) return result; + + if (key_type != DRM_RSA_PRIVATE_KEY) { + allowed_schemes = 0; + } + + result = OPKI_CreateAsymmetricKey(&session->prov40_oem_private_key, key_type, + wrapped_key, wrapped_key_length, key_size, + allowed_schemes); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to create asymmetric OEM private key with result: %u", result); + return result; + } + session->prov40_oem_allowed_schemes = allowed_schemes; + + return OEMCrypto_SUCCESS; +} + +NO_IGNORE_RESULT static OEMCryptoResult DeriveKey( + WTPI_K1_SymmetricKey_Handle master_key, uint8_t counter, + const uint8_t* context, size_t context_length, + SymmetricKeyType out_key_type, KeySize out_key_size, + SymmetricKey** new_key) { + if (new_key == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + // TODO(fredgc): remove this from the key table, too. + OEMCryptoResult result = OPKI_CreateKey(new_key, out_key_type, out_key_size); + if (result != OEMCrypto_SUCCESS) return result; + if (*new_key == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + WTPI_K1_SymmetricKey_Handle* handle = &(*new_key)->key_handle; + result = WTPI_K1_DeriveKeyFromKeyHandle(master_key, counter, context, + context_length, out_key_type, + out_key_size, handle); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to derive key from handle with result: %u", result); + OPKI_FreeKeyFromTable(new_key); + } + return result; +} + +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) { + if (session == NULL || mac_key_context == NULL || + mac_key_context_length == 0 || enc_key_context == NULL || + enc_key_context_length == 0 || master_key == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + uint8_t counter = 1; + OEMCryptoResult result = + DeriveKey(master_key, counter, mac_key_context, mac_key_context_length, + MAC_KEY_SERVER, MAC_KEY_SIZE, &session->mac_key_server); + if (result != OEMCrypto_SUCCESS) return result; + counter += 2; // increment counter by 2 because 2 blocks used for server key. + result = + DeriveKey(master_key, counter, mac_key_context, mac_key_context_length, + MAC_KEY_CLIENT, MAC_KEY_SIZE, &session->mac_key_client); + if (result != OEMCrypto_SUCCESS) { + OPKI_FreeKeyFromTable(&session->mac_key_server); + return result; + } + counter = 1; // Start over with encryption context. + result = + DeriveKey(master_key, counter, enc_key_context, enc_key_context_length, + ENCRYPTION_KEY, KEY_SIZE_128, &session->encryption_key); + if (result != OEMCrypto_SUCCESS) { + OPKI_FreeKeyFromTable(&session->mac_key_server); + OPKI_FreeKeyFromTable(&session->mac_key_client); + } + return result; +} + +OEMCryptoResult OPKI_VerifySignatureWithMacKeyServer(OEMCryptoSession* session, + const uint8_t* message, + size_t message_length, + const uint8_t* signature) { + if (session == NULL || message == NULL || message_length == 0 || + signature == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (!OPKI_CheckKey(session->mac_key_server, MAC_KEY_SERVER)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return WTPI_C1_HMAC_SHA256_Verify(session->mac_key_server->key_handle, + message, message_length, signature); +} + +OEMCryptoResult OPKI_GenerateSignatureWithMacKeyClient( + OEMCryptoSession* session, const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length) { + if (session == NULL || message == NULL || message_length == 0 || + signature == NULL || signature_length == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (!OPKI_CheckKey(session->mac_key_client, MAC_KEY_CLIENT)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (*signature_length != SHA256_DIGEST_LENGTH) { + *signature_length = SHA256_DIGEST_LENGTH; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + OEMCryptoResult result = WTPI_C1_HMAC_SHA256( + session->mac_key_client->key_handle, message, message_length, signature); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to compute HMAC-SHA256 signature with result: %u", result); + return result; + } + *signature_length = SHA256_DIGEST_LENGTH; + return OEMCrypto_SUCCESS; +} + +NO_IGNORE_RESULT static bool DRMKeyMaySign(const OEMCryptoSession* session) { + switch (session->drm_private_key->key_type) { + case DRM_RSA_PRIVATE_KEY: + return (session->allowed_schemes & kSign_RSASSA_PSS) == kSign_RSASSA_PSS; + case DRM_ECC_PRIVATE_KEY: + return true; + case PROV40_ED25519_PRIVATE_KEY: + // DRM key can never be ED25519 key. + return false; + } + return false; +} + +OEMCryptoResult OPKI_GenerateCertSignature(OEMCryptoSession* session, + const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length, + CertSignatureType signature_type) { + if (session == NULL || message == NULL || message_length == 0 || + signature == NULL || signature_length == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + switch (signature_type) { + case CERT_SIGNATURE_OEM: + // TODO(b/180530495): implement this. + // return SignMessageWithOEMPrivateKey(message, message_length, signature, + // signature_length); + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + case CERT_SIGNATURE_DRM: { + if (!DRMKeyMaySign(session)) return OEMCrypto_ERROR_INVALID_KEY; + WTPI_AsymmetricKey_Handle drm_private_key_handle; + uint32_t allowed_schemes; + AsymmetricKey* drm_private_key = session->drm_private_key; + OEMCryptoResult result = WTPI_UnwrapIntoAsymmetricKeyHandle( + drm_private_key->wrapped_key, drm_private_key->wrapped_key_length, + drm_private_key->key_type, &drm_private_key_handle, &allowed_schemes); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to unwrap DRM private key into key handle with result: %u", + result); + return result; + } + if (drm_private_key->key_type == DRM_RSA_PRIVATE_KEY) { + result = WTPI_RSASign(drm_private_key_handle, message, message_length, + signature, signature_length, kSign_RSASSA_PSS); + } else { + result = WTPI_ECCSign(drm_private_key_handle, message, message_length, + signature, signature_length); + } + WTPI_FreeAsymmetricKeyHandle(drm_private_key_handle); + return result; + } + default: + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +} + +NO_IGNORE_RESULT static OEMCryptoResult CheckStatusOnline( + OEMCryptoSession* session, uint32_t nonce, uint32_t control) { + if (!session) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + if (session->nonce_values.nonce != nonce) { + return OEMCrypto_ERROR_INVALID_NONCE; + } + switch (OPKI_GetUsageEntryStatus(session->session_id)) { + default: /* Invalid status. */ + case USAGE_ENTRY_NONE: + return OEMCrypto_ERROR_INVALID_CONTEXT; + case USAGE_ENTRY_LOADED: + /* Cannot reload usage entry for online license. */ + return OEMCrypto_ERROR_INVALID_CONTEXT; + case USAGE_ENTRY_NEW: + return OEMCrypto_SUCCESS; + } +} + +NO_IGNORE_RESULT static OEMCryptoResult CheckStatusOffline( + OEMCryptoSession* session, uint32_t nonce, uint32_t control) { + if (!session) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + switch (OPKI_GetUsageEntryStatus(session->session_id)) { + default: /* Invalid status. */ + case USAGE_ENTRY_NONE: + return OEMCrypto_ERROR_INVALID_CONTEXT; + case USAGE_ENTRY_LOADED: + /* We do not check the nonce. Instead, LoadKeys will verify the pst and + * mac keys match. */ + return OEMCrypto_SUCCESS; + case USAGE_ENTRY_NEW: + if (session->nonce_values.nonce != nonce) { + return OEMCrypto_ERROR_INVALID_NONCE; + } + return OEMCrypto_SUCCESS; + } +} + +NO_IGNORE_RESULT static OEMCryptoResult check_nonce_or_entry( + OEMCryptoSession* session, KeyControlBlock key_control_block) { + /* Note: we only check the nonce if the bit is enabled. */ + uint32_t replay_bits = key_control_block.control_bits & CONTROL_REPLAY_MASK; + switch (replay_bits) { + case CONTROL_NONCE_REQUIRED: + /* Online license. Nonce always required. */ + return CheckStatusOnline(session, key_control_block.nonce, + key_control_block.control_bits); + break; + case CONTROL_NONCE_OR_ENTRY: + /* Offline license. Nonce required on first load. */ + return CheckStatusOffline(session, key_control_block.nonce, + key_control_block.control_bits); + break; + default: + if ((key_control_block.control_bits & CONTROL_NONCE_ENABLED) && + session->nonce_values.nonce != key_control_block.nonce) { + return OEMCrypto_ERROR_INVALID_NONCE; + } + return OEMCrypto_SUCCESS; + } +} + +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) { + if (session == NULL || key_id == NULL || key_id_length == 0 || + key_id_length > KEY_ID_MAX_SIZE || key_data == NULL || + key_data_length == 0 || key_data_iv == NULL || key_control == NULL || + key_control_iv == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + uint8_t raw_key_control[KEY_CONTROL_SIZE]; + + SymmetricKey** current_key_ptr; + uint32_t current_key_index = 0; + SymmetricKeyType key_type; + if (session->license_type == OEMCrypto_ContentLicense) { + if (key_data_length != KEY_SIZE_128 && key_data_length != KEY_SIZE_256) { + /* Generic crypto allows both key sizes. */ + LOGE("Invalid key data length: %zu", key_data_length); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + current_key_index = session->num_content_keys; + if (current_key_index == CONTENT_KEYS_PER_SESSION) { + return OEMCrypto_ERROR_INSUFFICIENT_RESOURCES; + } + current_key_ptr = &session->content_keys[current_key_index]; + key_type = CONTENT_KEY; + } else { + if (key_data_length != KEY_SIZE_256) return OEMCrypto_ERROR_INVALID_CONTEXT; + current_key_index = session->num_entitlement_keys; + if (current_key_index == ENTITLEMENT_KEYS_PER_SESSION) { + return OEMCrypto_ERROR_INSUFFICIENT_RESOURCES; + } + current_key_ptr = &session->entitlement_keys[current_key_index]; + key_type = ENTITLEMENT_KEY; + } + + if (!OPKI_CheckKey(session->encryption_key, ENCRYPTION_KEY)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoResult result = + OPKI_CreateKey(current_key_ptr, key_type, (KeySize)key_data_length); + if (result != OEMCrypto_SUCCESS) return result; + SymmetricKey* current_key = *current_key_ptr; + result = WTPI_K1_AESDecryptAndCreateKeyHandle( + session->encryption_key->key_handle, key_data, key_data_length, + key_data_iv, key_type, &(current_key->key_handle)); + if (result != OEMCrypto_SUCCESS) { + LOGE( + "Failed to AES CBC decrypt key data and create key handle with result: " + "%u", + result); + goto cleanup; + } + + memcpy(current_key->key_id, key_id, key_id_length); + current_key->key_id_size = key_id_length; + current_key->session_key_index = current_key_index; + + if (!OPKI_CheckKey(current_key, key_type)) { + result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup; + } + if (CheckApiVersionAtMost(&session->nonce_values, 16, 4)) { + /* We use the first 128 bits regardless of the license type to decrypt the + key control. */ + result = WTPI_C1_AESCBCDecrypt(current_key->key_handle, KEY_SIZE_128, + key_control, KEY_CONTROL_SIZE, + key_control_iv, raw_key_control); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to AES CBC decrypt key control data with result: %u", + result); + goto cleanup; + } + } else { + memcpy(raw_key_control, key_control, KEY_CONTROL_SIZE); + } + + result = OPKI_ParseKeyControlBlock(raw_key_control, + ¤t_key->key_control_block); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to parse key control block with result: %u", result); + goto cleanup; + } + if (!(current_key->key_control_block.valid)) { + LOGE("Key control block is not valid"); + result = OEMCrypto_ERROR_INVALID_CONTEXT; + goto cleanup; + } + if ((current_key->key_control_block.control_bits & + CONTROL_REQUIRE_ANTI_ROLLBACK_HARDWARE) && + !WTPI_IsAntiRollbackHWPresent()) { + LOGE("Key control requires anti-rollback hardware, which is not present"); + result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup; + } + uint8_t minimum_patch_level = (current_key->key_control_block.control_bits & + CONTROL_SECURITY_PATCH_LEVEL_MASK) >> + CONTROL_SECURITY_PATCH_LEVEL_SHIFT; + if (minimum_patch_level > OEMCrypto_Security_Patch_Level()) { + LOGE( + "Security patch level doesn't meet the minimum requirement. Current: " + "%u, required: %u", + OEMCrypto_Security_Patch_Level(), minimum_patch_level); + result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup; + } + result = check_nonce_or_entry(session, current_key->key_control_block); + if (result != OEMCrypto_SUCCESS) goto cleanup; + if (current_key->key_control_block.control_bits & + CONTROL_SRM_VERSION_REQUIRED) { + if (!session->valid_srm_version) { + /* Require local display only. */ + current_key->key_control_block.control_bits |= CONTROL_HDCP_VERSION_MASK; + } + } + if (session->license_type == OEMCrypto_ContentLicense) { + current_key->is_entitled_content_key = false; + } + +cleanup: + if (result != OEMCrypto_SUCCESS) OPKI_FreeKeyFromTable(current_key_ptr); + return result; +} + +OEMCryptoResult OPKI_UpdateMacKeys(OEMCryptoSession* session, + const uint8_t* enc_mac_keys, + const uint8_t* mac_keys_iv) { + if (session == NULL || enc_mac_keys == NULL || mac_keys_iv == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (!OPKI_CheckKey(session->encryption_key, ENCRYPTION_KEY)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoResult free_mac_key_server_result = + OPKI_FreeKeyFromTable(&session->mac_key_server); + OEMCryptoResult free_mac_key_client_result = + OPKI_FreeKeyFromTable(&session->mac_key_client); + if (free_mac_key_server_result != OEMCrypto_SUCCESS) { + return free_mac_key_server_result; + } + if (free_mac_key_client_result != OEMCrypto_SUCCESS) { + return free_mac_key_client_result; + } + OEMCryptoResult result = + OPKI_CreateKey(&session->mac_key_server, MAC_KEY_SERVER, MAC_KEY_SIZE); + if (result != OEMCrypto_SUCCESS) goto cleanup; + result = + OPKI_CreateKey(&session->mac_key_client, MAC_KEY_CLIENT, MAC_KEY_SIZE); + if (result != OEMCrypto_SUCCESS) goto cleanup; + result = WTPI_K1_AESDecryptAndCreateKeyHandleForMacKeys( + session->encryption_key->key_handle, enc_mac_keys, 2 * MAC_KEY_SIZE, + mac_keys_iv, &(session->mac_key_server->key_handle), + &(session->mac_key_client->key_handle)); + if (result != OEMCrypto_SUCCESS) { + LOGE( + "Failed to AES CBC decrypt mac keys and create handles with result: %u", + result); + return result; + } +cleanup: + if (result != OEMCrypto_SUCCESS) { + OPKI_FreeKeyFromTable(&session->mac_key_server); + OPKI_FreeKeyFromTable(&session->mac_key_client); + } + return 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) { + if (session == NULL || key_control == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + OEMCryptoResult result = OEMCrypto_SUCCESS; + KeyControlBlock key_control_block; + if (key_id == NULL) { + result = OPKI_ParseKeyControlBlock(key_control, &key_control_block); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to parse key control block with result: %u", result); + return result; + } + } else { + bool is_content_key = session->license_type == OEMCrypto_ContentLicense; + SymmetricKey* key = + OPKI_FindKeyFromTable(session, is_content_key, key_id, key_id_length); + if (key == NULL) return OEMCrypto_ERROR_NO_CONTENT_KEY; + + uint8_t raw_key_control[KEY_CONTROL_SIZE]; + if (key_control_iv != NULL) { + SymmetricKeyType key_type; + if (is_content_key) { + key_type = CONTENT_KEY; + } else { + key_type = ENTITLEMENT_KEY; + } + if (!OPKI_CheckKey(key, key_type)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + /* Decrypt using only the first 128 bits of the key. */ + result = WTPI_C1_AESCBCDecrypt(key->key_handle, KEY_SIZE_128, key_control, + KEY_CONTROL_SIZE, key_control_iv, + raw_key_control); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to AES CBC decrypt key control with result: %u", result); + return result; + } + } else { + memcpy(raw_key_control, key_control, KEY_CONTROL_SIZE); + } + result = OPKI_ParseKeyControlBlock(raw_key_control, &key_control_block); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to parse raw key control with result: %u", result); + return result; + } + } + + if (!key_control_block.valid) return OEMCrypto_ERROR_INVALID_CONTEXT; + if ((key_control_block.control_bits & CONTROL_NONCE_ENABLED) && + session->nonce_values.nonce != key_control_block.nonce) { + return OEMCrypto_ERROR_INVALID_NONCE; + } + + uint32_t new_key_duration = key_control_block.duration; + uint64_t* timer_value = NULL; + uint64_t now; + result = WTPI_GetTrustedTime(&now); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get trusted time with result: %u", result); + return result; + } + result = ODK_RefreshV15Values(&session->timer_limits, &session->clock_values, + &session->nonce_values, now, new_key_duration, + timer_value); + if (result == ODK_SET_TIMER || result == ODK_DISABLE_TIMER) { + return OEMCrypto_SUCCESS; + } + if (result == ODK_TIMER_EXPIRED) return OEMCrypto_ERROR_KEY_EXPIRED; + return result; +} + +OEMCryptoResult OPKI_GetCurrentContentKey( + OEMCryptoSession* session, OEMCryptoEntitledKeySession* key_session, + SymmetricKey** key) { + RETURN_INVALID_CONTEXT_IF_NULL(session); + RETURN_INVALID_CONTEXT_IF_NULL(key); + if (session->license_type == OEMCrypto_EntitlementLicense) { + RETURN_INVALID_CONTEXT_IF_NULL(key_session); + if (key_session->current_entitled_content_key_index >= + key_session->num_entitled_content_keys) { + LOGE("Entitled content key index out of range, index = %u", + key_session->current_entitled_content_key_index); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + *key = key_session->entitled_content_keys + [key_session->current_entitled_content_key_index]; + } else { + if (session->current_content_key_index >= session->num_content_keys) { + LOGE("Content key index out of range, index = %u", + session->current_content_key_index); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + *key = session->content_keys[session->current_content_key_index]; + } + if (*key == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult OPKI_GetEntitlementKey(OEMCryptoSession* session, + const uint8_t* key_id, + size_t key_id_size, SymmetricKey** key) { + RETURN_INVALID_CONTEXT_IF_NULL(session); + RETURN_INVALID_CONTEXT_IF_NULL(key_id); + RETURN_INVALID_CONTEXT_IF_ZERO(key_id_size); + RETURN_INVALID_CONTEXT_IF_NULL(key); + if (session->license_type != OEMCrypto_EntitlementLicense) { + LOGE("Unexpected license type %u", session->license_type); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + for (uint32_t i = 0; i < session->num_entitlement_keys; i++) { + SymmetricKey* cur_key = session->entitlement_keys[i]; + if (cur_key != NULL && cur_key->key_id_size == (uint8_t)key_id_size && + memcmp(cur_key->key_id, key_id, key_id_size) == 0) { + *key = cur_key; + return OEMCrypto_SUCCESS; + } + } + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +OEMCryptoResult OPKI_GetKeyControlBlock( + SymmetricKey* content_key, OEMCryptoSession* session, + OEMCryptoEntitledKeySession* key_session, KeyControlBlock* control_block) { + RETURN_INVALID_CONTEXT_IF_NULL(control_block); + RETURN_INVALID_CONTEXT_IF_NULL(content_key); + RETURN_INVALID_CONTEXT_IF_NULL(session); + if (content_key->is_entitled_content_key) { + RETURN_INVALID_CONTEXT_IF_NULL(key_session); + if (session->license_type != OEMCrypto_EntitlementLicense) { + LOGE("License type %u mismatch for entitled content key", + session->license_type); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + /* For entitled content key, get the corresponding entitlement key which + * holds the key control block. */ + ABORT_IF(content_key->session_key_index >= + key_session->num_entitled_content_keys, + "Invalid content key index."); + EntitlementKeyInfo* entitlement_key_info = + &key_session->entitlement_keys[content_key->session_key_index]; + ABORT_IF_ZERO(entitlement_key_info->entitlement_key_id_size); + SymmetricKey* entitlement_key = NULL; + OEMCryptoResult result = OPKI_GetEntitlementKey( + session, entitlement_key_info->entitlement_key_id, + entitlement_key_info->entitlement_key_id_size, &entitlement_key); + if (result != OEMCrypto_SUCCESS) { + LOGE( + "Could not find entitlement key for entitled content key at index %u", + content_key->session_key_index); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (OPKI_CheckKey(entitlement_key, ENTITLEMENT_KEY)) { + *control_block = entitlement_key->key_control_block; + } else { + LOGE("Invalid entitlement key."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } else { + *control_block = content_key->key_control_block; + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult OPKI_CheckCurrentContentKeyUsage( + OEMCryptoSession* session, OEMCryptoEntitledKeySession* key_session, + uint32_t use_type, OPK_OutputBuffer_Type buffer_type) { + if (session == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT; + if ((session->license_type == OEMCrypto_ContentLicense && + key_session != NULL) || + (session->license_type == OEMCrypto_EntitlementLicense && + key_session == NULL)) { + LOGE("Mismatched license type and entitled key session."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + ABORT_IF(buffer_type != OPK_CLEAR_INSECURE_OUTPUT_BUFFER && + buffer_type != OPK_SECURE_OUTPUT_BUFFER, + "Impossible buffer type."); + SymmetricKey* current_content_key = NULL; + OEMCryptoResult result = + OPKI_GetCurrentContentKey(session, key_session, ¤t_content_key); + if (result != OEMCrypto_SUCCESS) return result; + if (!OPKI_CheckKey(current_content_key, CONTENT_KEY)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + KeyControlBlock control; + result = OPKI_GetKeyControlBlock(current_content_key, session, key_session, + &control); + if (result != OEMCrypto_SUCCESS) return result; + if (use_type && (!(control.control_bits & use_type))) { + /* Could not use this key for the given use type. */ + LOGE("Could not use this key for the given use type: %u", use_type); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (control.control_bits & CONTROL_DATA_PATH_SECURE) { + if (!WTPI_IsClosedPlatform() && + buffer_type == OPK_CLEAR_INSECURE_OUTPUT_BUFFER) { + return OEMCrypto_ERROR_DECRYPT_FAILED; + } + } + const OEMCrypto_HDCP_Capability capability = WTPI_CurrentHDCPCapability(); + if (capability != HDCP_NO_DIGITAL_OUTPUT && + (control.control_bits & CONTROL_HDCP_REQUIRED)) { + /* Check to see if HDCP requirements are satisfied. */ + const uint8_t required_hdcp = + (control.control_bits & CONTROL_HDCP_VERSION_MASK) >> + CONTROL_HDCP_VERSION_SHIFT; + if (required_hdcp > capability || capability == HDCP_NONE) { + return OEMCrypto_ERROR_INSUFFICIENT_HDCP; + } + } + /* Disable the analog outputs, if required. */ + if (control.control_bits & CONTROL_DISABLE_ANALOG_OUTPUT) { + if (WTPI_IsAnalogDisplayActive() && !WTPI_DisableAnalogDisplay()) { + LOGE("Analog output could not be disabled"); + return OEMCrypto_ERROR_ANALOG_OUTPUT; + } + } + /* If CGMS-A is required, either it must be active or analog output must be + disabled. */ + if (control.control_bits & CONTROL_CGMS_MASK) { + if (!WTPI_IsCGMS_AActive() && WTPI_IsAnalogDisplayActive() && + !WTPI_DisableAnalogDisplay()) { + LOGE("CGMS must be active if analog output is active"); + return OEMCrypto_ERROR_ANALOG_OUTPUT; + } + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult OPKI_UpdatePlaybackTimeAndUsageEntryStatus( + OEMCryptoSession* session) { + if (session == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT; + OEMCryptoResult result; + uint64_t now; + result = WTPI_GetTrustedTime(&now); + if (result != OEMCrypto_SUCCESS) return result; + /* Update ODK clock values. */ + if (!session->decrypt_started) { + uint64_t* timer_expiration = NULL; + result = ODK_AttemptFirstPlayback(now, &session->timer_limits, + &session->clock_values, timer_expiration); + } else { + /* Continued playback. */ + result = ODK_UpdateLastPlaybackTime(now, &session->timer_limits, + &session->clock_values); + } + if (result == ODK_TIMER_EXPIRED) return OEMCrypto_ERROR_KEY_EXPIRED; + if (result != OEMCrypto_SUCCESS && result != ODK_DISABLE_TIMER && + result != ODK_SET_TIMER) { + /* Results other than ODK_DISABLE_TIMER and ODK_SET_TIMER are treated as + * errors. */ + return result; + } + + /* Update usage entry. */ + switch (OPKI_GetUsageEntryStatus(session->session_id)) { + case USAGE_ENTRY_NONE: + /* No entry, don't do anything. */ + break; + case USAGE_ENTRY_DEACTIVATED: + return OEMCrypto_ERROR_KEY_EXPIRED; + case USAGE_ENTRY_NEW: + case USAGE_ENTRY_LOADED: + if (!session->decrypt_started) { + result = OPKI_ForbidReportUsage(session->session_id); + } else { + result = OPKI_SetUsageEntryRecentDecrypt(session->session_id); + } + if (result != OEMCrypto_SUCCESS) return result; + break; + default: + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + session->decrypt_started = true; + return OEMCrypto_SUCCESS; +} + +NO_IGNORE_RESULT static OEMCryptoResult ComputeDecryptHash( + OEMCryptoSession* session, OEMCryptoEntitledKeySession* key_session, + const OEMCrypto_SampleDescription* sample, + SymmetricKey* current_content_key, OPK_OutputBuffer* output_buffer, + size_t starting_output_offset) { + ABORT_IF_NULL(session); + ABORT_IF_NULL(sample); + ABORT_IF_NULL(current_content_key); + ABORT_IF_NULL(output_buffer); + ABORT_IF(session->license_type == OEMCrypto_EntitlementLicense && + key_session == NULL, + "entitled key session is NULL"); + OEMCryptoResult result = OEMCrypto_SUCCESS; + bool can_compute_hash = false; + if (OPKI_CheckKey(current_content_key, CONTENT_KEY)) { + KeyControlBlock control_block; + result = OPKI_GetKeyControlBlock(current_content_key, session, key_session, + &control_block); + if (result != OEMCrypto_SUCCESS) return result; + uint32_t control_bits = control_block.control_bits; + can_compute_hash = (control_bits & CONTROL_ALLOW_HASH_VERIFICATION) != 0; + } + + DecryptHash* decrypt_hash = + (session->license_type == OEMCrypto_EntitlementLicense) + ? &key_session->decrypt_hash + : &session->decrypt_hash; + if (!can_compute_hash) { + decrypt_hash->hash_error = OEMCrypto_ERROR_UNKNOWN_FAILURE; + decrypt_hash->compute_hash = false; + decrypt_hash->current_hash = 0; + decrypt_hash->current_frame_number = 0; + } else { + size_t offset = 0; + for (size_t subsample_index = 0; + subsample_index < sample->subsamples_length; ++subsample_index) { + const OEMCrypto_SubSampleDescription* subsample = + &(sample->subsamples[subsample_index]); + size_t subsample_length; + if (OPK_AddOverflowUX(subsample->num_bytes_clear, + subsample->num_bytes_encrypted, + &subsample_length)) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + size_t current_offset; + if (OPK_AddOverflowUX(starting_output_offset, offset, ¤t_offset)) { + LOGE("Output buffer overflow"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + result = OPK_CheckOutputBounds(output_buffer, current_offset, + subsample_length); + if (result != OEMCrypto_SUCCESS) { + LOGE("Output bounds check failed with result: %u", result); + return result; + } + + if (OEMCrypto_FirstSubsample & subsample->subsample_flags) { + result = WTPI_Crc32Init(&decrypt_hash->current_hash); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to init CRC32 with result: %u", result); + return result; + } + } + result = WTPI_Crc32Cont_OutputBuffer( + output_buffer, current_offset, subsample_length, + decrypt_hash->current_hash, &decrypt_hash->current_hash); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to compute CRC32 with result: %u", result); + return result; + } + if (OEMCrypto_LastSubsample & subsample->subsample_flags) { + if (decrypt_hash->current_hash != decrypt_hash->given_hash) { + /* Update bad_frame_number if this is the first bad frame. */ + if (decrypt_hash->hash_error == OEMCrypto_SUCCESS) { + decrypt_hash->bad_frame_number = decrypt_hash->current_frame_number; + decrypt_hash->hash_error = OEMCrypto_ERROR_BAD_HASH; + } + } + decrypt_hash->compute_hash = false; + } + + /* Advance the offset. */ + if (OPK_AddOverflowUX(offset, subsample_length, &offset)) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + } + } + return result; +} + +NO_IGNORE_RESULT static OEMCryptoResult DecryptOneSample( + OEMCryptoSession* session, OEMCryptoEntitledKeySession* key_session, + const OEMCrypto_SampleDescription* sample, + const OEMCrypto_CENCEncryptPatternDesc* pattern) { + ABORT_IF_NULL(session); + ABORT_IF_NULL(sample); + ABORT_IF_NULL(pattern); + OEMCryptoResult result = OEMCrypto_SUCCESS; + + OPK_OutputBuffer output_buffer; + size_t starting_output_offset; + result = OPK_ParseDestBufferDesc(&sample->buffers.output_descriptor, + &output_buffer, &starting_output_offset); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to parse dest buffer description with result: %u", result); + return result; + } + ABORT_IF(!OPK_IsOutputBufferValid(&output_buffer), "Invalid output buffer."); + + /* Use 0 as use_type since there is no key control bit indicating whether + * this key can be used as for DecryptCENC. */ + result = OPKI_CheckCurrentContentKeyUsage(session, key_session, 0, + output_buffer.type); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to verify current content key usage with result: %u", result); + return result; + } + result = OPKI_UpdatePlaybackTimeAndUsageEntryStatus(session); + if (result != OEMCrypto_SUCCESS) { + if (result == OEMCrypto_ERROR_KEY_EXPIRED) { + session->state = SESSION_EXPIRED; + } + return result; + } + SymmetricKey* current_content_key = NULL; + result = + OPKI_GetCurrentContentKey(session, key_session, ¤t_content_key); + if (result != OEMCrypto_SUCCESS) return result; + result = WTPI_DecryptSample(current_content_key->key_handle, sample, pattern, + &output_buffer, starting_output_offset, + current_content_key->cipher_mode); + if (result != OEMCrypto_SUCCESS) return result; + + /* Compute hash for the decrypted sample. */ + bool compute_hash = (session->license_type == OEMCrypto_EntitlementLicense) + ? key_session->decrypt_hash.compute_hash + : session->decrypt_hash.compute_hash; + if (compute_hash) { + result = + ComputeDecryptHash(session, key_session, sample, current_content_key, + &output_buffer, starting_output_offset); + } + return result; +} + +OEMCryptoResult OPKI_DecryptSamples( + OEMCryptoSession* session, OEMCryptoEntitledKeySession* key_session, + const OEMCrypto_SampleDescription* samples, size_t samples_length, + const OEMCrypto_CENCEncryptPatternDesc* pattern) { + if (session == NULL || samples == NULL || pattern == NULL || + samples_length == 0) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if ((session->license_type == OEMCrypto_ContentLicense && + key_session != NULL) || + (session->license_type == OEMCrypto_EntitlementLicense && + key_session == NULL)) { + LOGE("Mismatched license type and entitled key session."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + /* Iterate through all the samples and decrypt each one. */ + for (size_t sample_index = 0; sample_index < samples_length; ++sample_index) { + const OEMCrypto_SampleDescription* sample = &(samples[sample_index]); + const OEMCryptoResult result = + DecryptOneSample(session, key_session, sample, pattern); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failure %#x at sample index %zu", result, sample_index); + return result; + } + } + + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult OPKI_GetNonceValues(const OEMCryptoSession* session, + ODK_NonceValues* nonce_values) { + if (session == NULL || nonce_values == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + if (!session->nonce_created) { + LOGE("Nonce has not been created"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + *nonce_values = session->nonce_values; + + return OEMCrypto_SUCCESS; +} diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_session.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_session.h new file mode 100644 index 0000000..7f243a6 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_session.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_session_key_table.c b/oemcrypto/opk/oemcrypto_ta/oemcrypto_session_key_table.c new file mode 100644 index 0000000..d1535c0 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_session_key_table.c @@ -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 + +#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; +} diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_session_key_table.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_session_key_table.h new file mode 100644 index 0000000..7552a87 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_session_key_table.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_session_table.c b/oemcrypto/opk/oemcrypto_ta/oemcrypto_session_table.c new file mode 100644 index 0000000..2c26e72 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_session_table.c @@ -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 +#include + +#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; +} diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_session_table.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_session_table.h new file mode 100644 index 0000000..d638f03 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_session_table.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_session_type.c b/oemcrypto/opk/oemcrypto_ta/oemcrypto_session_type.c new file mode 100644 index 0000000..58643f6 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_session_type.c @@ -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; +} diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_session_type.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_session_type.h new file mode 100644 index 0000000..fab2e4b --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_session_type.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_ta.gyp b/oemcrypto/opk/oemcrypto_ta/oemcrypto_ta.gyp new file mode 100644 index 0000000..fccd686 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_ta.gyp @@ -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)', + ], + }, + }, + ], +} diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_usage_table.c b/oemcrypto/opk/oemcrypto_ta/oemcrypto_usage_table.c new file mode 100644 index 0000000..136a1bf --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_usage_table.c @@ -0,0 +1,1202 @@ +/* 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_usage_table.h" + +#include +#include + +#include "OEMCryptoCENC.h" +#include "odk_endian.h" +#include "odk_util.h" +#include "oemcrypto_check_macros.h" +#include "oemcrypto_object_table.h" +#include "oemcrypto_serialized_usage_table.h" +#include "wtpi_clock_interface_layer1.h" +#include "wtpi_config_macros.h" +#include "wtpi_device_key_interface.h" +#include "wtpi_generation_number_interface.h" +#include "wtpi_logging_interface.h" + +/** + The usage table consists of a short array of active entries, and a large + array of possible entries. The usage table header is stored in the large + array. The large array stores the generation number for each entry and an + index into the short array if the entry is active. The short array holds + entries that have been loaded and are connected to a session. +*/ + +/* Global usage table states. Used to tell if we need to load the usage table or + * not. */ +typedef enum UsageTableState { + /* Initial state. */ + USAGE_TABLE_NOT_INITIALIZED = (int)0xec887d04, + /* Error state. Actually, anything but the other states are considered + * errors, but we can explicitly set the state to this value. */ + USAGE_TABLE_ERROR_STATE = (int)0x7bd06ca1, + /* After the usage table has been initialized to an empty table. The usage + * table cannot be used in this state because the generation number is not yet + * valid. */ + USAGE_TABLE_INITIALIZED_BUT_NOT_LOADED_STATE = (int)0x1776dcfa, + /* After the usage table has been loaded or created with + * OEMCrypto_LoadUsageTableHeader or OEMCrypto_CreateUsageTableHeader. The key + * distinction between this state and the previous one is that the generation + * number is valid in this state. The usage table might be empty in this + * state, but it is ready to have new entries created. */ + USAGE_TABLE_ACTIVE_STATE = (int)0x7331face, +} UsageTableState; + +/* Data storage for a usage entry in memory. It has the data for a usage entry + * that is saved to the filesystem, and also all of the transient state, such as + * the current session it's tied to and the current status. */ +typedef struct UsageEntry { + /* The part of the usage entry that is saved to the file system. */ + SavedUsageEntry data; + OEMCrypto_SESSION session_id; /* Which session this entry is actively in. */ + bool forbid_report; + bool recent_decrypt; + bool is_loaded; +} UsageEntry; + +DEFINE_OBJECT_TABLE(usage_entry_table, UsageEntry, + MAX_NUMBER_OF_ACTIVE_USAGE_ENTRIES, NULL); + +/* A data structure holding the current, active usage table -- that means it has + * a header and all of the resident entries. */ +typedef struct UsageTable { + /* The master generation number. */ + uint64_t master_generation_number; + /* Storage for the active entries. */ + UsageEntry* entries[MAX_NUMBER_OF_USAGE_ENTRIES]; + /* Number of entries in the |generation_numbers| table. */ + uint32_t table_size; + /* These are the generation numbers of each entry. */ + uint64_t generation_numbers[MAX_NUMBER_OF_USAGE_ENTRIES]; +} UsageTable; + +/* A file local variable indicating whether the usage table has been initialized + * or not. */ +static UsageTableState g_usage_table_state = USAGE_TABLE_NOT_INITIALIZED; +/* The file local variable holding the current active usage table. This is only + * valid if |g_usage_table_state| is USAGE_TABLE_ACTIVE. */ +static UsageTable g_usage_table; + +/* TODO(b/158720996): figure out a way to avoid __attribute__(packed). */ +typedef struct { + uint8_t signature[20]; // -- HMAC SHA1 of the rest of the report. + uint8_t status; // current status of entry. (OEMCrypto_Usage_Entry_Status) + uint8_t clock_security_level; + uint8_t pst_length; + uint8_t padding; // make int64's word aligned. + int64_t seconds_since_license_received; // now - time_of_license_received + int64_t seconds_since_first_decrypt; // now - time_of_first_decrypt + int64_t seconds_since_last_decrypt; // now - time_of_last_decrypt + uint8_t pst[]; +} __attribute__((packed)) OEMCrypto_PST_Report; + +static void ClearTable(void) { + g_usage_table.table_size = 0; + memset(&g_usage_table.generation_numbers, 0, + sizeof(g_usage_table.generation_numbers)); + for (size_t i = 0; i < MAX_NUMBER_OF_USAGE_ENTRIES; i++) { + OPKI_FreeFromObjectTable(&usage_entry_table, g_usage_table.entries[i]); + g_usage_table.entries[i] = NULL; + } +} + +static UsageEntry* FindUsageEntry(OEMCrypto_SESSION session_id) { + for (size_t i = 0; i < MAX_NUMBER_OF_USAGE_ENTRIES; i++) { + if (g_usage_table.entries[i] && + g_usage_table.entries[i]->session_id == session_id) { + return g_usage_table.entries[i]; + } + } + return NULL; +} + +/* This serializes, encrypts, and signs the current header into the given + * buffer. It is an error if the buffer is not big enough. The header fields + * and the master generation number in the current header will be updated. */ +NO_IGNORE_RESULT static OEMCryptoResult EncryptAndSignHeader( + uint8_t* header_buffer, size_t header_buffer_length) { + SavedUsageHeader header = { + .common_info = + { + .file_type = USAGE_TABLE_HEADER, + .format_version = CURRENT_FILE_FORMAT_VERSION, + }, + + .master_generation_number = g_usage_table.master_generation_number, + .table_size = g_usage_table.table_size, + }; + memcpy(header.generation_numbers, g_usage_table.generation_numbers, + header.table_size * sizeof(uint64_t)); + SignedSavedUsageHeader signed_header = { + .common_info = + { + .file_type = SIGNED_USAGE_TABLE_HEADER, + .format_version = CURRENT_FILE_FORMAT_VERSION, + }, + .buffer_size = sizeof(signed_header.buffer), + .buffer = {0}, + }; + uint8_t temp_buffer[PADDED_HEADER_BUFFER_SIZE]; + OEMCryptoResult result = + OPKI_PackUsageHeader(temp_buffer, sizeof(temp_buffer), &header); + if (result != OEMCrypto_SUCCESS) return result; + + result = WTPI_EncryptAndSign(DEVICE_KEY_WRAP_USAGE_TABLE, temp_buffer, + sizeof(temp_buffer), signed_header.buffer, + &signed_header.buffer_size); + if (result != OEMCrypto_SUCCESS) return result; + result = OPKI_PackSignedUsageHeader(header_buffer, header_buffer_length, + &signed_header); + return result; +} + +/* This decrypts and deserializes usage table header from the given buffer in + * the legacy format. The signature of the buffer is verified. The generation + * number is verified by the calling function. It is an error if the buffer is + * not big enough. */ +NO_IGNORE_RESULT static OEMCryptoResult DecryptAndVerifyHeader_Legacy( + const uint8_t* header_buffer, size_t header_buffer_length, + SavedUsageHeader* header) { + if (!header_buffer || !header) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (header_buffer_length < sizeof(SignedSavedUsageHeaderLegacy)) { + return OEMCrypto_ERROR_SHORT_BUFFER; + } + SignedSavedUsageHeaderLegacy signed_header; + OEMCryptoResult result = OPKI_UnpackSignedUsageHeaderLegacy( + header_buffer, header_buffer_length, &signed_header); + if (result != OEMCrypto_SUCCESS) return result; + if (signed_header.common_info.file_type != SIGNED_USAGE_TABLE_HEADER) { + /* We were given the wrong file. */ + LOGE("Unknown signed usage header type: %u", + signed_header.common_info.file_type); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (signed_header.common_info.format_version != LEGACY_FILE_FORMAT_VERSION) { + LOGE("Bad signed usage header format version: %u. Expected version: %u", + signed_header.common_info.format_version, LEGACY_FILE_FORMAT_VERSION); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (signed_header.buffer_size != LEGACY_PADDED_HEADER_BUFFER_SIZE) { + LOGE("Invalid signed usage header buffer size: %u", + signed_header.buffer_size); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + uint8_t temp_buffer[LEGACY_PADDED_HEADER_BUFFER_SIZE]; + size_t temp_buffer_size = sizeof(temp_buffer); + result = WTPI_VerifyAndDecryptUsageData_Legacy( + signed_header.buffer, signed_header.buffer_size, signed_header.signature, + signed_header.iv, temp_buffer); + if (result != OEMCrypto_SUCCESS) return result; + result = OPKI_UnpackUsageHeader(temp_buffer, temp_buffer_size, header); + if (result != OEMCrypto_SUCCESS) return result; + if (header->common_info.file_type != USAGE_TABLE_HEADER) { + /* We were given the wrong file. */ + LOGE("Unknown usage header type: %u", header->common_info.file_type); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (header->common_info.format_version != LEGACY_FILE_FORMAT_VERSION) { + LOGE("Bad usage header format version: %u", + header->common_info.format_version); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (header->table_size > MAX_NUMBER_OF_USAGE_ENTRIES) { + LOGE("Too many usage entries: %u", header->table_size); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +/* This decrypts and deserializes usage table header from the given buffer. The + * signature of the buffer is verified. The generation number is verified by the + * calling function. It is an error if the buffer is not big enough. */ +NO_IGNORE_RESULT static OEMCryptoResult DecryptAndVerifyHeader( + const uint8_t* header_buffer, size_t header_buffer_length, + SavedUsageHeader* header) { + if (!header_buffer || !header) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (header_buffer_length < sizeof(SavedCommonInfo)) { + LOGE("Invalid header buffer size: %zu", header_buffer_length); + return OEMCrypto_ERROR_SHORT_BUFFER; + } + SavedCommonInfo common_info; + OEMCryptoResult result = OPKI_UnpackSavedCommonInfo( + header_buffer, header_buffer_length, &common_info); + if (result != OEMCrypto_SUCCESS) return result; + if (common_info.file_type != SIGNED_USAGE_TABLE_HEADER) { + /* We were given the wrong file. */ + LOGE("Unknown signed usage header type: %u", common_info.file_type); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (common_info.format_version == LEGACY_FILE_FORMAT_VERSION) { + LOGD("Legacy usage header format version %u detected.", + LEGACY_FILE_FORMAT_VERSION); + return DecryptAndVerifyHeader_Legacy(header_buffer, header_buffer_length, + header); + } + if (common_info.format_version != CURRENT_FILE_FORMAT_VERSION) { + LOGE("Bad signed usage header format version: %u", + common_info.format_version); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + SignedSavedUsageHeader signed_header; + result = OPKI_UnpackSignedUsageHeader(header_buffer, header_buffer_length, + &signed_header); + if (result != OEMCrypto_SUCCESS) return result; + if (signed_header.buffer_size > sizeof(signed_header.buffer)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + uint8_t temp_buffer[PADDED_HEADER_BUFFER_SIZE]; + size_t temp_buffer_size = sizeof(temp_buffer); + result = WTPI_VerifyAndDecrypt( + DEVICE_KEY_WRAP_USAGE_TABLE, signed_header.buffer, + signed_header.buffer_size, temp_buffer, &temp_buffer_size); + if (result != OEMCrypto_SUCCESS) return result; + result = OPKI_UnpackUsageHeader(temp_buffer, temp_buffer_size, header); + if (result != OEMCrypto_SUCCESS) return result; + if (header->common_info.file_type != USAGE_TABLE_HEADER) { + /* We were given the wrong file. */ + LOGE("Unknown usage header type: %u", header->common_info.file_type); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (header->common_info.format_version != CURRENT_FILE_FORMAT_VERSION) { + /* In the future, we might handle backwards compatible versions. */ + LOGE("Bad usage header format version: %u", + header->common_info.format_version); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (header->table_size > MAX_NUMBER_OF_USAGE_ENTRIES) { + LOGE("Too many usage entries: %u", header->table_size); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +/* This serializes, encrypts, and signs the specified entry into the given + * buffer. It is an error if the buffer is not big enough. The header fields in + * the entry will be updated, but the generation number will not. */ +NO_IGNORE_RESULT static OEMCryptoResult EncryptAndSignEntry( + SavedUsageEntry* entry, uint8_t* entry_buffer, size_t entry_buffer_length) { + entry->common_info.file_type = USAGE_TABLE_ENTRY; + entry->common_info.format_version = CURRENT_FILE_FORMAT_VERSION; + + SignedSavedUsageEntry signed_entry = { + .common_info = + { + .file_type = SIGNED_USAGE_TABLE_ENTRY, + .format_version = CURRENT_FILE_FORMAT_VERSION, + }, + .buffer_size = sizeof(signed_entry.buffer), + .buffer = {0}, + }; + uint8_t temp_buffer[PADDED_ENTRY_BUFFER_SIZE]; + OEMCryptoResult result = + OPKI_PackUsageEntry(temp_buffer, sizeof(temp_buffer), entry); + if (result != OEMCrypto_SUCCESS) return result; + result = WTPI_EncryptAndSign(DEVICE_KEY_WRAP_USAGE_TABLE, temp_buffer, + sizeof(temp_buffer), signed_entry.buffer, + &signed_entry.buffer_size); + if (result != OEMCrypto_SUCCESS) return result; + result = OPKI_PackSignedUsageEntry(entry_buffer, entry_buffer_length, + &signed_entry); + return result; +} + +/* This decrypts and deserializes a usage table entry from the given buffer in + * the legacy format. The signature of the buffer is verified. The generation + * number is verified by the calling function. If the buffer is not big enough + * the error OEMCrypto_ERROR_SHORT_BUFFER is returned. */ +NO_IGNORE_RESULT static OEMCryptoResult DecryptAndVerifyEntry_Legacy( + const uint8_t* entry_buffer, size_t entry_buffer_length, + SavedUsageEntry* entry) { + if (!entry_buffer || !entry) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (entry_buffer_length < sizeof(SignedSavedUsageEntryLegacy)) { + return OEMCrypto_ERROR_SHORT_BUFFER; + } + SignedSavedUsageEntryLegacy signed_entry; + OEMCryptoResult result = OPKI_UnpackSignedUsageEntryLegacy( + entry_buffer, entry_buffer_length, &signed_entry); + if (result != OEMCrypto_SUCCESS) return result; + if (signed_entry.common_info.file_type != SIGNED_USAGE_TABLE_ENTRY) { + /* We were given the wrong file. */ + LOGE("Unknown signed entry type: %u", signed_entry.common_info.file_type); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (signed_entry.common_info.format_version != LEGACY_FILE_FORMAT_VERSION) { + LOGE("Bad signed entry format version: %u. Expected version: %u", + signed_entry.common_info.format_version, LEGACY_FILE_FORMAT_VERSION); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (signed_entry.buffer_size > sizeof(signed_entry.buffer)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + uint8_t temp_buffer[LEGACY_PADDED_ENTRY_BUFFER_SIZE]; + size_t temp_buffer_size = sizeof(temp_buffer); + result = WTPI_VerifyAndDecryptUsageData_Legacy( + signed_entry.buffer, signed_entry.buffer_size, signed_entry.signature, + signed_entry.iv, temp_buffer); + if (result != OEMCrypto_SUCCESS) return result; + result = OPKI_UnpackUsageEntry(temp_buffer, temp_buffer_size, entry); + if (result != OEMCrypto_SUCCESS) return result; + if (entry->common_info.file_type != USAGE_TABLE_ENTRY) { + /* We were given the wrong file. */ + LOGE("Bad entry type: %u", entry->common_info.file_type); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (entry->common_info.format_version != LEGACY_FILE_FORMAT_VERSION) { + LOGE("Bad entry format version: %u", entry->common_info.format_version); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +/* This decrypts and deserializes a usage table entry from the given buffer. The + * signature of the buffer is verified. The generation number is verified by the + * calling function. If the buffer is not big enough the error + * OEMCrypto_ERROR_SHORT_BUFFER is returned. */ +NO_IGNORE_RESULT static OEMCryptoResult DecryptAndVerifyEntry( + const uint8_t* entry_buffer, size_t entry_buffer_length, + SavedUsageEntry* entry) { + if (!entry_buffer || !entry) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (entry_buffer_length < sizeof(SavedCommonInfo)) { + LOGE("Invalid entry buffer size: %zu", entry_buffer_length); + return OEMCrypto_ERROR_SHORT_BUFFER; + } + SavedCommonInfo common_info; + OEMCryptoResult result = OPKI_UnpackSavedCommonInfo( + entry_buffer, entry_buffer_length, &common_info); + if (result != OEMCrypto_SUCCESS) return result; + if (common_info.file_type != SIGNED_USAGE_TABLE_ENTRY) { + /* We were given the wrong file. */ + LOGE("Unknown signed entry type: %u", common_info.file_type); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (common_info.format_version == LEGACY_FILE_FORMAT_VERSION) { + LOGD("Legacy usage entry format version %u detected.", + LEGACY_FILE_FORMAT_VERSION); + return DecryptAndVerifyEntry_Legacy(entry_buffer, entry_buffer_length, + entry); + } + if (common_info.format_version != CURRENT_FILE_FORMAT_VERSION) { + LOGE("Bad signed entry format version: %u", common_info.format_version); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (entry_buffer_length < OPKI_SignedEntrySize()) { + return OEMCrypto_ERROR_SHORT_BUFFER; + } + SignedSavedUsageEntry signed_entry; + result = OPKI_UnpackSignedUsageEntry(entry_buffer, entry_buffer_length, + &signed_entry); + if (result != OEMCrypto_SUCCESS) return result; + if (signed_entry.buffer_size > sizeof(signed_entry.buffer)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + uint8_t temp_buffer[PADDED_ENTRY_BUFFER_SIZE]; + size_t temp_buffer_size = sizeof(temp_buffer); + result = WTPI_VerifyAndDecrypt(DEVICE_KEY_WRAP_USAGE_TABLE, + signed_entry.buffer, signed_entry.buffer_size, + temp_buffer, &temp_buffer_size); + if (result != OEMCrypto_SUCCESS) return result; + result = OPKI_UnpackUsageEntry(temp_buffer, temp_buffer_size, entry); + if (result != OEMCrypto_SUCCESS) return result; + if (entry->common_info.file_type != USAGE_TABLE_ENTRY) { + /* We were given the wrong file. */ + LOGE("Bad entry type: %u", entry->common_info.file_type); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (entry->common_info.format_version != CURRENT_FILE_FORMAT_VERSION) { + /* In the future, we might handle backwards compatible versions. */ + LOGE("Bad entry format version: %u", entry->common_info.format_version); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +NO_IGNORE_RESULT static OEMCryptoResult RollGenerationNumber( + UsageEntry* entry) { + if (!entry || entry->data.index >= MAX_NUMBER_OF_USAGE_ENTRIES) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + uint64_t new_generation_number = g_usage_table.master_generation_number + 1; + if (WTPI_SaveGenerationNumber(new_generation_number) != OEMCrypto_SUCCESS) { + LOGE("Failed to save generation number: %" PRIu64, new_generation_number); + g_usage_table_state = USAGE_TABLE_ERROR_STATE; + return OEMCrypto_ERROR_SYSTEM_INVALIDATED; + } + g_usage_table.master_generation_number = new_generation_number; + entry->data.generation_number++; + g_usage_table.generation_numbers[entry->data.index] = + entry->data.generation_number; + return OEMCrypto_SUCCESS; +} + +NO_IGNORE_RESULT static OEMCryptoResult UpdateClockValues( + UsageEntry* entry, ODK_ClockValues* clock_values) { + if (!entry || entry->data.index >= MAX_NUMBER_OF_USAGE_ENTRIES || + !clock_values) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (entry->recent_decrypt) { + uint64_t now; + OEMCryptoResult result = WTPI_GetTrustedTime(&now); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get trusted time with result: %u", result); + return result; + } + entry->data.time_of_last_decrypt = now; + entry->recent_decrypt = false; + } + entry->data.time_of_license_received = + clock_values->time_of_license_request_signed; + entry->data.time_of_first_decrypt = clock_values->time_of_first_decrypt; + /* Use the most recent time_of_last_decrypt. */ + if ((uint64_t)(entry->data.time_of_last_decrypt) < + clock_values->time_of_last_decrypt) { + entry->data.time_of_last_decrypt = clock_values->time_of_last_decrypt; + } else { + clock_values->time_of_last_decrypt = entry->data.time_of_last_decrypt; + } + entry->data.status = clock_values->status; + return OEMCrypto_SUCCESS; +} + +// Clear out memory for the usage table. +OEMCryptoResult OPKI_InitializeUsageTable(void) { + if (WTPI_PrepareGenerationNumber() != OEMCrypto_SUCCESS) { + LOGE("Could not load generation number."); + return OEMCrypto_ERROR_INIT_FAILED; + } + ClearTable(); + g_usage_table_state = USAGE_TABLE_INITIALIZED_BUT_NOT_LOADED_STATE; + return OEMCrypto_SUCCESS; +} + +// Erase data from usage table. +OEMCryptoResult OPKI_TerminateUsageTable(void) { + g_usage_table_state = USAGE_TABLE_NOT_INITIALIZED; + return OEMCrypto_SUCCESS; +} + +static NO_IGNORE_RESULT UsageEntryStatus +GetUsageEntryStatusFromEntry(UsageEntry* entry) { + if (entry->data.status == kInactive || entry->data.status == kInactiveUsed || + entry->data.status == kInactiveUnused) { + return USAGE_ENTRY_DEACTIVATED; + } + if (entry->is_loaded) { + return USAGE_ENTRY_LOADED; + } + return USAGE_ENTRY_NEW; +} + +NO_IGNORE_RESULT UsageEntryStatus +OPKI_GetUsageEntryStatus(OEMCrypto_SESSION session_id) { + UsageEntry* entry = FindUsageEntry(session_id); + if (!entry) return USAGE_ENTRY_NONE; + return GetUsageEntryStatusFromEntry(entry); +} + +/* Create a new usage table. */ +OEMCryptoResult OPKI_CreateUsageTableHeader(uint8_t* header_buffer, + size_t* header_buffer_length) { + if (!header_buffer_length) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + size_t size = OPKI_SignedHeaderSize(0); + if (*header_buffer_length < size) { + *header_buffer_length = size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *header_buffer_length = size; + if (!header_buffer) return OEMCrypto_ERROR_INVALID_CONTEXT; + /* Make sure there are no entries that are currently tied to an open session. + */ + for (size_t i = 0; i < MAX_NUMBER_OF_USAGE_ENTRIES; i++) { + if (g_usage_table.entries[i]) { + LOGE("OPKI_CreateUsageTableHeader: entry %zu used by session %u.", i, + g_usage_table.entries[i]->session_id); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } + /* Clear the table before we check the state -- we want to have an empty table + * after this function whether there was an error or not. */ + ClearTable(); + if (g_usage_table_state == USAGE_TABLE_ERROR_STATE) { + /* Something went wrong. The system should give up and re-init. */ + LOGE("Usage table is in error state"); + return OEMCrypto_ERROR_SYSTEM_INVALIDATED; + } else if (g_usage_table_state == USAGE_TABLE_ACTIVE_STATE) { + /* Creating a new header when one was already active. This is OK but + * rare. The system would do this to delete all the entries -- maybe on + * reprovisioning, but there are other reasons. However, we want to keep the + * same generation number. So we don't try to load it. */ + } else if (g_usage_table_state == + USAGE_TABLE_INITIALIZED_BUT_NOT_LOADED_STATE) { + /* We are creating a brand new table. Try to load the generation number. */ + if (WTPI_LoadGenerationNumber(&g_usage_table.master_generation_number) != + OEMCrypto_SUCCESS) { + LOGE("Failed to load generation number"); + g_usage_table_state = USAGE_TABLE_ERROR_STATE; + return OEMCrypto_ERROR_SYSTEM_INVALIDATED; + } + } else { + /* Only other valid state is not initialized, which is not valid for this + * function. */ + LOGE("Usage table is not initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + g_usage_table_state = USAGE_TABLE_ACTIVE_STATE; + return EncryptAndSignHeader(header_buffer, *header_buffer_length); +} + +/* Load a usage table header. */ +OEMCryptoResult OPKI_LoadUsageTableHeader(const uint8_t* buffer, + size_t buffer_length) { + /* Clear the table before we check the state -- we want to have an empty table + * before we load a new one, or we want an empty one if there is an error. */ + ClearTable(); + if (g_usage_table_state == USAGE_TABLE_ERROR_STATE) { + /* Something went wrong. The system should give up and re-init. */ + LOGE("Usage table is in error state"); + return OEMCrypto_ERROR_SYSTEM_INVALIDATED; + } else if (g_usage_table_state == USAGE_TABLE_ACTIVE_STATE) { + /* Loading a header when one was already active is an indication that the + * system was going to terminate, but changed its mind - e.g. because + * delayed termination was canceled. We keep the existing generation + * numbers. */ + } else if (g_usage_table_state == + USAGE_TABLE_INITIALIZED_BUT_NOT_LOADED_STATE) { + /* We are loading a brand new table. Try to load the generation number. */ + if (WTPI_LoadGenerationNumber(&g_usage_table.master_generation_number) != + OEMCrypto_SUCCESS) { + LOGE("Failed to load generation number"); + g_usage_table_state = USAGE_TABLE_ERROR_STATE; + return OEMCrypto_ERROR_SYSTEM_INVALIDATED; + } + } else { + /* Only other valid state is not initialized, which is not valid for this + * function. */ + LOGE("Usage table is not initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + SavedUsageHeader header; + OEMCryptoResult result = + DecryptAndVerifyHeader(buffer, buffer_length, &header); + if (result != OEMCrypto_SUCCESS) return result; + if (g_usage_table.master_generation_number + 1 == + header.master_generation_number || + g_usage_table.master_generation_number - 1 == + header.master_generation_number) { + /* Skew of 1 is a warning, but we continue on. */ + result = OEMCrypto_WARNING_GENERATION_SKEW; + } else if (g_usage_table.master_generation_number != + header.master_generation_number) { + /* Skew of more than 1 is an error. Clean the table and return an error. */ + ClearTable(); + /* Leave the state as active, so that the generation number is kept. It is + * not a security risk to leave an empty usage header in memory. */ + g_usage_table_state = USAGE_TABLE_ACTIVE_STATE; + return OEMCrypto_ERROR_GENERATION_SKEW; + } + g_usage_table.table_size = header.table_size; + memset(g_usage_table.generation_numbers, 0, + sizeof(g_usage_table.generation_numbers)); + memcpy(g_usage_table.generation_numbers, header.generation_numbers, + header.table_size * sizeof(uint64_t)); + g_usage_table_state = USAGE_TABLE_ACTIVE_STATE; + return result; +} + +/* Grabs a free active usage entry off of the free list of active entries. It + * updates the free list, the active_entry_map, and the session for a new + * entry. It does NOT update the entry's generation number and does NOT update + * the usage header. It does sanity checks. */ +NO_IGNORE_RESULT static OEMCryptoResult GrabEntry(OEMCrypto_SESSION session_id, + uint32_t usage_entry_number, + UsageEntry** entry_ptr) { + if (!entry_ptr || usage_entry_number >= MAX_NUMBER_OF_USAGE_ENTRIES) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + /* Check that the session doesn't have entry already. */ + if (FindUsageEntry(session_id) != NULL) { + return OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES; + } + /* Check that another session is not already using this entry. */ + if (g_usage_table.entries[usage_entry_number]) { + return OEMCrypto_ERROR_INVALID_SESSION; + } + UsageEntry* entry = OPKI_AllocFromObjectTable(&usage_entry_table, NULL); + if (!entry) return OEMCrypto_ERROR_INSUFFICIENT_RESOURCES; + + /* Initialize the entry. */ + memset(entry, 0, sizeof(UsageEntry)); + entry->data.index = usage_entry_number; + entry->session_id = session_id; + /* Update the active entry map. */ + g_usage_table.entries[usage_entry_number] = entry; + *entry_ptr = entry; + return OEMCrypto_SUCCESS; +} + +/** Release an active entry and put it back onto the free list. This does not + * save any data. It is usually done with the session is closing or when loading + * an entry generated an error. */ +void OPKI_ReleaseEntry(OEMCrypto_SESSION session_id) { + if (g_usage_table_state == USAGE_TABLE_ACTIVE_STATE) { + /* Remove from active entry map. */ + UsageEntry* entry = FindUsageEntry(session_id); + if (entry) { + uint32_t index = entry->data.index; + OPKI_FreeFromObjectTable(&usage_entry_table, entry); + g_usage_table.entries[index] = NULL; + } + } +} + +/* Create a new usage entry and tie it to |session|. The new entry will have an + * entry number at the end of the array of all entries in the header, but it + * could be anywhere in the array of active entries. */ +OEMCryptoResult OPKI_CreateNewUsageEntry(OEMCrypto_SESSION session_id, + uint32_t* usage_entry_number) { + if (!usage_entry_number) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) { + /* Usage table header must be loaded. */ + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (FindUsageEntry(session_id) != NULL) { + /* Can only have one entry per session. */ + return OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES; + } + /* Check that the header can grow. */ + if (g_usage_table.table_size >= MAX_NUMBER_OF_USAGE_ENTRIES) { + return OEMCrypto_ERROR_INSUFFICIENT_RESOURCES; + } + uint32_t new_index = g_usage_table.table_size; + UsageEntry* entry = NULL; + OEMCryptoResult result = GrabEntry(session_id, new_index, &entry); + if (result != OEMCrypto_SUCCESS) return result; + g_usage_table.table_size++; + /* Update the generation numbers. Increment the master GN, and then copy to + * the entry. Also copy to the header's array of entries. */ + g_usage_table.master_generation_number++; + entry->data.generation_number = g_usage_table.master_generation_number; + g_usage_table.generation_numbers[new_index] = + g_usage_table.master_generation_number; + *usage_entry_number = new_index; + return OEMCrypto_SUCCESS; +} + +/* Tie a |session| to an existing usage entry. The new entry will use the + * argument |usage_entry_number| for the entry number. */ +OEMCryptoResult OPKI_ReuseUsageEntry(OEMCrypto_SESSION session_id, + uint32_t usage_entry_number) { + if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) { + /* Usage table header must be loaded. */ + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (FindUsageEntry(session_id) != NULL) { + /* Can only have one entry per session. */ + return OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES; + } + /* Check that the |usage_entry_number| is valid. */ + if (usage_entry_number >= MAX_NUMBER_OF_USAGE_ENTRIES) { + return OEMCrypto_ERROR_INSUFFICIENT_RESOURCES; + } + if (usage_entry_number >= g_usage_table.table_size) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + UsageEntry* entry = NULL; + OEMCryptoResult result = GrabEntry(session_id, usage_entry_number, &entry); + if (result != OEMCrypto_SUCCESS) return result; + /* Update the generation numbers. */ + entry->data.generation_number = g_usage_table.master_generation_number; + g_usage_table.generation_numbers[usage_entry_number] = + g_usage_table.master_generation_number; + g_usage_table.master_generation_number++; + return OEMCrypto_SUCCESS; +} + +/* Load a usage entry that had been saved to the file system and tie it to + * |session|. */ +OEMCryptoResult OPKI_LoadUsageEntry(OEMCrypto_SESSION session_id, + ODK_ClockValues* clock_values, + uint32_t usage_entry_number, + const uint8_t* buffer, + size_t buffer_length) { + if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) { + /* Usage table header must be loaded. */ + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (FindUsageEntry(session_id) != NULL) { + /* Can only load one entry per session. */ + return OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES; + } + if (usage_entry_number >= MAX_NUMBER_OF_USAGE_ENTRIES) { + LOGE("Too many usage entries: %u/%u", usage_entry_number, + (unsigned int)MAX_NUMBER_OF_USAGE_ENTRIES); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (usage_entry_number >= g_usage_table.table_size) { + LOGE("Invalid usage entry number: %u", usage_entry_number); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + UsageEntry* entry = NULL; + OEMCryptoResult result = GrabEntry(session_id, usage_entry_number, &entry); + if (result != OEMCrypto_SUCCESS) return result; + /* Load the entry. */ + result = DecryptAndVerifyEntry(buffer, buffer_length, &entry->data); + if (result != OEMCrypto_SUCCESS) { + OPKI_ReleaseEntry(session_id); + return result; + } + if (entry->data.index != usage_entry_number) { + OPKI_ReleaseEntry(session_id); + return OEMCrypto_ERROR_INVALID_SESSION; + } + uint64_t now; + result = WTPI_GetTrustedTime(&now); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get trusted time with result: %u", result); + OPKI_ReleaseEntry(session_id); + return result; + } + result = ODK_ReloadClockValues( + clock_values, (uint64_t)(entry->data.time_of_license_received), + (uint64_t)(entry->data.time_of_first_decrypt), + (uint64_t)(entry->data.time_of_last_decrypt), entry->data.status, + (uint64_t)now); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to reload clock values with result: %u", result); + OPKI_ReleaseEntry(session_id); + return result; + } + /* check generation number against header's table of generation numbers. */ + uint64_t entry_gn = entry->data.generation_number; + uint64_t header_gn = g_usage_table.generation_numbers[usage_entry_number]; + if (entry_gn + 1 == header_gn || entry_gn - 1 == header_gn) { + /* Skew of 1 is a warning, but we continue on. */ + result = OEMCrypto_WARNING_GENERATION_SKEW; + } else if (entry_gn != header_gn) { + /* Skew of more than 1 is an error. Clean the table and return an error. */ + OPKI_ReleaseEntry(session_id); + return OEMCrypto_ERROR_GENERATION_SKEW; + } + entry->is_loaded = true; + return 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) { + if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!header_buffer_length || !entry_buffer_length) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + /* Check the size and let caller know if it's a short buffer. */ + size_t header_size = OPKI_SignedHeaderSize(g_usage_table.table_size); + size_t entry_size = OPKI_SignedEntrySize(); + if (*header_buffer_length < header_size || + *entry_buffer_length < entry_size) { + *header_buffer_length = header_size; + *entry_buffer_length = entry_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *header_buffer_length = header_size; + *entry_buffer_length = entry_size; + /* Check that the session has an entry, and it's state is valid. */ + UsageEntry* entry = FindUsageEntry(session_id); + if (!header_buffer || !entry_buffer || !entry) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + /* New clock values in the usage entry. */ + OEMCryptoResult result = UpdateClockValues(entry, clock_values); + if (result != OEMCrypto_SUCCESS) return result; + /* The new generation numbers. */ + result = RollGenerationNumber(entry); + if (result != OEMCrypto_SUCCESS) return result; + result = EncryptAndSignHeader(header_buffer, *header_buffer_length); + if (result != OEMCrypto_SUCCESS) return result; + result = + EncryptAndSignEntry(&entry->data, entry_buffer, *entry_buffer_length); + if (result == OEMCrypto_SUCCESS) { + entry->forbid_report = false; + entry->recent_decrypt = false; + } + return result; +} + +OEMCryptoResult OPKI_ForbidReportUsage(OEMCrypto_SESSION session_id) { + if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + UsageEntry* entry = FindUsageEntry(session_id); + if (!entry) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const OEMCryptoResult result = RollGenerationNumber(entry); + if (result == OEMCrypto_SUCCESS) entry->forbid_report = true; + return result; +} + +OEMCryptoResult OPKI_SetUsageEntryPST(OEMCrypto_SESSION session_id, + const uint8_t* pst, size_t pst_length) { + if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + UsageEntry* entry = FindUsageEntry(session_id); + if (!entry || GetUsageEntryStatusFromEntry(entry) != USAGE_ENTRY_NEW) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!pst || pst_length == 0 || pst_length > MAX_PST_LENGTH) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + memcpy(entry->data.pst, pst, pst_length); + entry->data.pst_length = pst_length; + + /* The PST is set when the license is loaded. This will be removed in v16 when + * we use the time of license signed instead of time of license loaded. */ + uint64_t now; + OEMCryptoResult result = WTPI_GetTrustedTime(&now); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get trusted time with result: %u", result); + return result; + } + entry->data.time_of_license_received = now; + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult OPKI_VerfiyUsageEntryPST(OEMCrypto_SESSION session_id, + const uint8_t* pst, + size_t pst_length) { + if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + UsageEntry* entry = FindUsageEntry(session_id); + if (!entry || GetUsageEntryStatusFromEntry(entry) != USAGE_ENTRY_LOADED) { + LOGE("Invalid usage entry status"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!pst || pst_length == 0 || pst_length > MAX_PST_LENGTH) { + LOGE("Invalid PST"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (entry->data.pst_length != pst_length || + crypto_memcmp(entry->data.pst, pst, pst_length)) { + return OEMCrypto_ERROR_WRONG_PST; + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult OPKI_SetUsageEntryMacKeys( + const OEMCrypto_SESSION session_id, + WTPI_K1_SymmetricKey_Handle mac_key_server, + WTPI_K1_SymmetricKey_Handle mac_key_client) { + if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + UsageEntry* entry = FindUsageEntry(session_id); + if (!entry || GetUsageEntryStatusFromEntry(entry) != USAGE_ENTRY_NEW) { + LOGE("Invalid usage entry status"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoResult result; + uint8_t wrapped_server_mac[WRAPPED_MAC_KEY_SIZE]; + uint8_t wrapped_client_mac[WRAPPED_MAC_KEY_SIZE]; + memset(wrapped_server_mac, 0, WRAPPED_MAC_KEY_SIZE); + memset(wrapped_client_mac, 0, WRAPPED_MAC_KEY_SIZE); + result = + WTPI_K1_WrapKey(DEVICE_KEY_WRAP_MAC_KEY, mac_key_server, MAC_KEY_SERVER, + wrapped_server_mac, WRAPPED_MAC_KEY_SIZE); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to wrap mac key server"); + return result; + } + result = + WTPI_K1_WrapKey(DEVICE_KEY_WRAP_MAC_KEY, mac_key_client, MAC_KEY_CLIENT, + wrapped_client_mac, WRAPPED_MAC_KEY_SIZE); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to wrap mac key client"); + return result; + } + memcpy(entry->data.mac_key_server, wrapped_server_mac, WRAPPED_MAC_KEY_SIZE); + memcpy(entry->data.mac_key_client, wrapped_client_mac, WRAPPED_MAC_KEY_SIZE); + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult OPKI_VerifyUsageEntryMacKeys( + OEMCrypto_SESSION session_id, WTPI_K1_SymmetricKey_Handle mac_key_server, + WTPI_K1_SymmetricKey_Handle mac_key_client) { + if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + UsageEntry* entry = FindUsageEntry(session_id); + if (!entry || GetUsageEntryStatusFromEntry(entry) != USAGE_ENTRY_LOADED) { + LOGE("Invalid usage entry status"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoResult result; + uint8_t wrapped_server_mac[WRAPPED_MAC_KEY_SIZE]; + uint8_t wrapped_client_mac[WRAPPED_MAC_KEY_SIZE]; + memset(wrapped_server_mac, 0, WRAPPED_MAC_KEY_SIZE); + memset(wrapped_client_mac, 0, WRAPPED_MAC_KEY_SIZE); + result = + WTPI_K1_WrapKey(DEVICE_KEY_WRAP_MAC_KEY, mac_key_server, MAC_KEY_SERVER, + wrapped_server_mac, WRAPPED_MAC_KEY_SIZE); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to wrap mac key server"); + return result; + } + result = + WTPI_K1_WrapKey(DEVICE_KEY_WRAP_MAC_KEY, mac_key_client, MAC_KEY_CLIENT, + wrapped_client_mac, WRAPPED_MAC_KEY_SIZE); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to wrap mac key client"); + return result; + } + if (crypto_memcmp(entry->data.mac_key_server, wrapped_server_mac, + WRAPPED_MAC_KEY_SIZE) || + crypto_memcmp(entry->data.mac_key_client, wrapped_client_mac, + WRAPPED_MAC_KEY_SIZE)) { + return OEMCrypto_ERROR_WRONG_PST; + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult OPKI_SetUsageEntryRecentDecrypt(OEMCrypto_SESSION session_id) { + if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + UsageEntry* entry = FindUsageEntry(session_id); + if (!entry || + GetUsageEntryStatusFromEntry(entry) == USAGE_ENTRY_DEACTIVATED) { + LOGE("Invalid usage entry status"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + entry->recent_decrypt = true; + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult OPKI_DeactivateUsageEntry(OEMCrypto_SESSION session_id, + ODK_ClockValues* clock_values, + const uint8_t* pst UNUSED, + size_t pst_length UNUSED) { + if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + /* Check that the session has an entry, and it's state is valid. */ + UsageEntry* entry = FindUsageEntry(session_id); + if (!entry) { + LOGE("Failed to deactivate usage entry. Invalid entry status."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoResult result = OPKI_ForbidReportUsage(session_id); + if (result != OEMCrypto_SUCCESS) return result; + result = ODK_DeactivateUsageEntry(clock_values); + return 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) { + if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + RETURN_INVALID_CONTEXT_IF_NULL(buffer_length); + UsageEntry* entry = FindUsageEntry(session_id); + if (!entry) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + size_t length_needed = sizeof(OEMCrypto_PST_Report) + pst_length; + if (*buffer_length < length_needed) { + *buffer_length = length_needed; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *buffer_length = length_needed; + + // We delay checking these to allow the above length-returning code to work + // without passing in these parameters. + RETURN_INVALID_CONTEXT_IF_NULL(pst); + RETURN_INVALID_CONTEXT_IF_NULL(buffer); + + if (entry->forbid_report || entry->recent_decrypt) { + return OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE; + } + if (pst_length == 0 || pst_length > MAX_PST_LENGTH || + pst_length != entry->data.pst_length) { + return OEMCrypto_ERROR_WRONG_PST; + } + if (crypto_memcmp(entry->data.pst, pst, pst_length)) { + return OEMCrypto_ERROR_WRONG_PST; + } + + uint64_t now; + OEMCryptoResult result = WTPI_GetTrustedTime(&now); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get trusted time with result: %u", result); + return result; + } + + OEMCrypto_PST_Report* report = (OEMCrypto_PST_Report*)buffer; + report->seconds_since_license_received = + oemcrypto_htobe64(now - entry->data.time_of_license_received); + report->seconds_since_first_decrypt = + oemcrypto_htobe64(now - entry->data.time_of_first_decrypt); + report->seconds_since_last_decrypt = + oemcrypto_htobe64(now - entry->data.time_of_last_decrypt); + report->status = entry->data.status; + report->clock_security_level = WTPI_GetClockType(); + report->pst_length = pst_length; + memcpy(report->pst, pst, pst_length); + if (mac_key_client) { + /* If the session has mac keys, use those. */ + result = + WTPI_C1_HMAC_SHA1(mac_key_client, buffer + SHA_DIGEST_LENGTH, + length_needed - SHA_DIGEST_LENGTH, report->signature); + if (OEMCrypto_SUCCESS != result) { + LOGE( + "Failed to compute HMAC-SHA1 signature with mac key client from " + "session, error: %u", + result); + } + } else { + /* Otherwise, we use the mac key from the entry. */ + WTPI_K1_SymmetricKey_Handle key = NULL; + result = WTPI_K1_UnwrapIntoKeyHandle( + DEVICE_KEY_WRAP_MAC_KEY, entry->data.mac_key_client, + WRAPPED_MAC_KEY_SIZE, MAC_KEY_CLIENT, &key); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to unwrap mac key client with result: %u", result); + return result; + } + + result = + WTPI_C1_HMAC_SHA1(key, buffer + SHA_DIGEST_LENGTH, + length_needed - SHA_DIGEST_LENGTH, report->signature); + if (OEMCrypto_SUCCESS != result) { + LOGE( + "Failed to compute HMAC-SHA1 signature with mac key client from " + "entry, error: %u", + result); + } + if (OEMCrypto_SUCCESS != WTPI_K1_FreeKeyHandle(key)) { + if (OEMCrypto_SUCCESS != result) return result; + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } + return result; +} + +OEMCryptoResult OPKI_SignReleaseRequest(OEMCrypto_SESSION session_id, + const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length UNUSED) { + if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + UsageEntry* entry = FindUsageEntry(session_id); + if (!entry) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + WTPI_K1_SymmetricKey_Handle key = NULL; + OEMCryptoResult result = WTPI_K1_UnwrapIntoKeyHandle( + DEVICE_KEY_WRAP_MAC_KEY, entry->data.mac_key_client, WRAPPED_MAC_KEY_SIZE, + MAC_KEY_CLIENT, &key); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to unwrap mac key client with result: %u", result); + return result; + } + + result = WTPI_C1_HMAC_SHA256(key, message, message_length, signature); + if (OEMCrypto_SUCCESS != result) { + LOGE("Failed to compute HMAC-SHA256 signature with result: %u", result); + } + if (OEMCrypto_SUCCESS != WTPI_K1_FreeKeyHandle(key)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return result; +} + +OEMCryptoResult OPKI_MoveEntry(OEMCrypto_SESSION session_id, + uint32_t new_index) { + if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (new_index >= MAX_NUMBER_OF_USAGE_ENTRIES) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + UsageEntry* entry = FindUsageEntry(session_id); + if (!entry) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + uint32_t index = entry->data.index; + if (g_usage_table.entries[new_index]) { + return OEMCrypto_ERROR_ENTRY_IN_USE; + } + /* Copy data from index to new_index. */ + entry->data.index = new_index; + g_usage_table.entries[new_index] = g_usage_table.entries[index]; + /* Update entry's generation number to be max generation number. */ + entry->data.generation_number = g_usage_table.master_generation_number; + g_usage_table.generation_numbers[new_index] = + g_usage_table.master_generation_number; + + /* Mark old index as unused. */ + g_usage_table.entries[index] = NULL; + + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult OPKI_ShrinkUsageTableHeader(uint32_t new_entry_count, + uint8_t* header_buffer, + size_t* header_buffer_length) { + RETURN_INVALID_CONTEXT_IF_NULL(header_buffer_length); + if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (new_entry_count >= g_usage_table.table_size) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + for (size_t i = new_entry_count; i < g_usage_table.table_size; i++) { + if (g_usage_table.entries[i]) { + return OEMCrypto_ERROR_ENTRY_IN_USE; + } + } + size_t header_size = OPKI_SignedHeaderSize(new_entry_count); + if (header_size > *header_buffer_length) { + *header_buffer_length = header_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *header_buffer_length = header_size; + RETURN_INVALID_CONTEXT_IF_NULL(header_buffer); + + for (size_t i = new_entry_count; i < g_usage_table.table_size; i++) { + g_usage_table.generation_numbers[i] = 0; + } + g_usage_table.table_size = new_entry_count; + return EncryptAndSignHeader(header_buffer, *header_buffer_length); +} diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_usage_table.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_usage_table.h new file mode 100644 index 0000000..41fc939 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_usage_table.h @@ -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 +#include +#include + +#include "OEMCryptoCENC.h" +#include "odk.h" +#include "oemcrypto_compiler_attributes.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" + +typedef enum UsageEntryStatus { + USAGE_ENTRY_NONE = (int)0xacbad562, + USAGE_ENTRY_NEW = (int)0x4545babc, + USAGE_ENTRY_LOADED = (int)0x766bca12, + USAGE_ENTRY_DEACTIVATED = (int)0xdacba37, +} UsageEntryStatus; + +/** + * Clear out memory for the usage table. No other usage table functions may be + * called before this. */ +NO_IGNORE_RESULT OEMCryptoResult OPKI_InitializeUsageTable(void); + +/** + * Erase data from usage table. No other usage table functions may be + * called without calling OPKI_InitializeUsageTable. */ +NO_IGNORE_RESULT OEMCryptoResult OPKI_TerminateUsageTable(void); + +/** Gets the usage entry status of the given session. */ +NO_IGNORE_RESULT UsageEntryStatus +OPKI_GetUsageEntryStatus(OEMCrypto_SESSION session_id); + +/** Create a new empty usage table header. */ +NO_IGNORE_RESULT OEMCryptoResult OPKI_CreateUsageTableHeader( + uint8_t* header_buffer, size_t* header_buffer_length); + +/** Load a usage table header. */ +NO_IGNORE_RESULT OEMCryptoResult +OPKI_LoadUsageTableHeader(const uint8_t* buffer, size_t buffer_length); + +/** + * Create a new usage table entry and attach it to the |session|. This may + * return an error if the usage table is full, or if too many open sessions + * have active usage entries. |session| must be open and not already have an + * entry associated with it. + * Pointers must be non-null and are owned by the caller. */ +NO_IGNORE_RESULT OEMCryptoResult OPKI_CreateNewUsageEntry( + OEMCrypto_SESSION session_id, uint32_t* usage_entry_number); + +/** + * Allows a |session| to take an existing usage entry. This may return an + * error if the usage table is full, or if too many open sessions have active + * usage entries. |session| must be open and not already have an entry + * associated with it. All information related to the previous entry should be + * cleared from the header. The new entry will be initialized with a + * generation number equal to the master generation number, which will also be + * stored in the header’s existing slot. Then the master generation number + * will be incremented. */ +NO_IGNORE_RESULT OEMCryptoResult +OPKI_ReuseUsageEntry(OEMCrypto_SESSION session_id, uint32_t usage_entry_number); + +/** + * Load a usage table entry and attach it to the |session|. This may return + * an error if too many open sessions have active usage entries. |session| + * must be open and not already have an entry associated with it. The + * usage_entry_number must match that in the loaded entry. + * Pointers must be non-null and are owned by the caller. */ +NO_IGNORE_RESULT OEMCryptoResult OPKI_LoadUsageEntry( + OEMCrypto_SESSION session_id, ODK_ClockValues* clock_values, + uint32_t usage_entry_number, const uint8_t* buffer, size_t buffer_length); + +/** + * Release the active usage entry associated with |session|. + * Pointers must be non-null and are owned by the caller. */ +void OPKI_ReleaseEntry(OEMCrypto_SESSION session_id); + +/** + * Update all values in the usage entry associated with |session|. After + * updating values, the generation numbers are all updated and the master + * generation number is saved to persistent storage. Then the entry and the + * usage table header are saved to the specified buffer. If the buffer lengths + * are not large enough, none of the work above is completed -- instead the + * lengths are updated and OEMCrypto_ERROR_SHORT_BUFFER is returned. + * Pointers must be non-null and are owned by the caller. */ +NO_IGNORE_RESULT OEMCryptoResult OPKI_UpdateUsageEntry( + OEMCrypto_SESSION session_id, ODK_ClockValues* clock_values, + uint8_t* header_buffer, size_t* header_buffer_length, uint8_t* entry_buffer, + size_t* entry_buffer_length); + +/** + * Set the provider session token in the usage entry associated with + * |session|. This is done when a license is first loaded. + * Pointers must be non-null and are owned by the caller. */ +NO_IGNORE_RESULT OEMCryptoResult OPKI_SetUsageEntryPST( + OEMCrypto_SESSION session_id, const uint8_t* pst, size_t pst_length); + +/** + * Verify the provider session token in the usage entry associated with + * |session|. This is done when a license is reloaded to verify the license + * matches the usage entry. + * Pointers must be non-null and are owned by the caller. */ +NO_IGNORE_RESULT OEMCryptoResult OPKI_VerfiyUsageEntryPST( + OEMCrypto_SESSION session_id, const uint8_t* pst, size_t pst_length); + +/** + * Set the mac keys in the usage entry associated with |session|. + * This is done when a license is first loaded. + * Pointers must be non-null and are owned by the caller. */ +NO_IGNORE_RESULT OEMCryptoResult OPKI_SetUsageEntryMacKeys( + OEMCrypto_SESSION session_id, WTPI_K1_SymmetricKey_Handle mac_key_server, + WTPI_K1_SymmetricKey_Handle mac_key_client); + +/** + * Verify the mac keys in the usage entry associated with + * |session|. This is done when a license is reloaded to verify the license + * matches the usage entry. + * Pointers must be non-null and are owned by the caller. */ +NO_IGNORE_RESULT OEMCryptoResult OPKI_VerifyUsageEntryMacKeys( + OEMCrypto_SESSION session_id, WTPI_K1_SymmetricKey_Handle mac_key_server, + WTPI_K1_SymmetricKey_Handle mac_key_client); + +/** + * Set the recent_decrypt flag in the usage entry associated with |session|. + * This is done when a continued playback occurs. + * Pointers must be non-null and are owned by the caller. */ +NO_IGNORE_RESULT OEMCryptoResult +OPKI_SetUsageEntryRecentDecrypt(OEMCrypto_SESSION session_id); + +/** + * Mark the usage entry associated with |session| as deactivated. After + * this, the license may not be used to decrypt content. Pointers must be + * non-null and are owned by the caller. */ +NO_IGNORE_RESULT OEMCryptoResult OPKI_DeactivateUsageEntry( + OEMCrypto_SESSION session_id, ODK_ClockValues* clock_values, + const uint8_t* pst, size_t pst_length); + +/** + * Generate a usage report from the entry associated with |session|. + * |mac_key_client| can be null to use the stored MAC key instead; + * otherwise, pointers must be non-null and are owned by the caller. */ +NO_IGNORE_RESULT OEMCryptoResult +OPKI_ReportUsage(OEMCrypto_SESSION session_id, + WTPI_K1_SymmetricKey_Handle mac_key_client, const uint8_t* pst, + size_t pst_length, uint8_t* buffer, size_t* buffer_length); + +/** + * Mark the entry associated with |session| as modified and forbid a usage + * report until the data has been saved. This is done on important events + * like first decrypt and deactivation. Pointers must be non-null and are + * owned by the caller. */ +NO_IGNORE_RESULT OEMCryptoResult +OPKI_ForbidReportUsage(OEMCrypto_SESSION session_id); + +/** + * Sign |buffer| with the client mac key in the entry associated with + * |session|. Pointers must be non-null and are owned by the caller. */ +NO_IGNORE_RESULT OEMCryptoResult OPKI_SignReleaseRequest( + OEMCrypto_SESSION session_id, const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length); + +/** + * Move the usage entry associated with |session| to the new index in the + * usage table header. The generation numbers are updated as specified in + * the OEMCrypto spec. Pointers must be non-null and are owned by the + * caller. */ +NO_IGNORE_RESULT OEMCryptoResult OPKI_MoveEntry(OEMCrypto_SESSION session_id, + uint32_t new_index); + +/** + * Shrink the usage table to the size specified. + * Pointers must be non-null and are owned by the caller. */ +NO_IGNORE_RESULT OEMCryptoResult +OPKI_ShrinkUsageTableHeader(uint32_t new_entry_count, uint8_t* header_buffer, + size_t* header_buffer_length); + +#endif /* OEMCRYPTO_TA_OEMCRYPTO_USAGE_TABLE_H_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_wall_clock.c b/oemcrypto/opk/oemcrypto_ta/oemcrypto_wall_clock.c new file mode 100644 index 0000000..60e337f --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_wall_clock.c @@ -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; +} diff --git a/oemcrypto/opk/oemcrypto_ta/oemcrypto_wall_clock.h b/oemcrypto/opk/oemcrypto_ta/oemcrypto_wall_clock.h new file mode 100644 index 0000000..58e4f5a --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/oemcrypto_wall_clock.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi/README.md b/oemcrypto/opk/oemcrypto_ta/wtpi/README.md new file mode 100644 index 0000000..1aaa299 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi/README.md @@ -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. diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_abort_interface.h b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_abort_interface.h new file mode 100644 index 0000000..5b5082b --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_abort_interface.h @@ -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 + +#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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_clock_interface_layer1.h b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_clock_interface_layer1.h new file mode 100644 index 0000000..6e7aa88 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_clock_interface_layer1.h @@ -0,0 +1,76 @@ +/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary + source code may only be used and distributed under the Widevine + License Agreement. */ + +#ifndef OEMCRYPTO_TA_WTPI_CLOCK_INTERFACE_LAYER1_H_ +#define OEMCRYPTO_TA_WTPI_CLOCK_INTERFACE_LAYER1_H_ + +#include "stdint.h" + +#include "OEMCryptoCENC.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup secure-clock Monotonic Secure Clock + + * Partners implementing a porting layer may either + * 1. Implement wtpi_persistent_storage_layer2.h and + * wtpi_clock_interface_layer2.h, and then use the reference implementation + * wtpi_clock_and_gn_layer1.c for the clock and generation interfaces. This + * is preferred if the hardware secure timer resets to 0 whenever the device + * is inactive. + * or + * 2. Implement both this wtpi_clock_interface_layer1.h and + * wtpi_generation_number_interface.h. This is preferred if the system has a + * hardware secure wall clock. + * + * @{ + */ + +/** + * Returns the trusted time at the time of call and modifies |time_in_s|. + * + * The trusted time should be from a clock that is at least monotonic across + * reboots, and is hardware protected while the TA is active. Ideally the + * TrustedTime is a wall-clock that is hardware protected at all times, even + * across reboots, if supported by the device. + * + * The zero time is not specified. For example, it may be seconds since initial + * boot or seconds since the epoch. It may not be seconds since current boot + * because that would not be increasing over system reboot. + * + * Caller retains ownership of all pointers. + * + * @param[out] time_in_s: pointer to trusted time, in seconds. + * + * @retval OEMCrypto_ERROR_INVALID_CONTEXT if time_in_s is a null pointer + * @retval OEMCrypto_SUCCESS on success + */ +OEMCryptoResult WTPI_GetTrustedTime(uint64_t* time_in_s); + +/** + * Initialize the system clock. + */ +OEMCryptoResult WTPI_InitializeClock(void); + +/** + * Terminate the system clock. + */ +OEMCryptoResult WTPI_TerminateClock(void); + +/** + * Returns the clock type. See OEMCrypto documentation, + * https://developers.google.com/widevine/drm/client/oemcrypto/v16/odk-timers + * for the definition of insecure clock, secure timer, and secure clock. + */ +OEMCrypto_Clock_Security_Level WTPI_GetClockType(void); + +/// @} + +#ifdef __cplusplus +} +#endif + +#endif /* OEMCRYPTO_TA_WTPI_CLOCK_INTERFACE_LAYER1_H_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_clock_interface_layer2.h b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_clock_interface_layer2.h new file mode 100644 index 0000000..8de99b5 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_clock_interface_layer2.h @@ -0,0 +1,53 @@ +/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary + source code may only be used and distributed under the Widevine + License Agreement. */ + +#ifndef OEMCRYPTO_TA_WTPI_CLOCK_INTERFACE_LAYER2_H_ +#define OEMCRYPTO_TA_WTPI_CLOCK_INTERFACE_LAYER2_H_ + +#include + +#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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_config_interface.h b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_config_interface.h new file mode 100644 index 0000000..8536376 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_config_interface.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_crc32_interface.h b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_crc32_interface.h new file mode 100644 index 0000000..40cf538 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_crc32_interface.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_crypto_and_key_management_interface_layer1.h b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_crypto_and_key_management_interface_layer1.h new file mode 100644 index 0000000..d1ec19f --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_crypto_and_key_management_interface_layer1.h @@ -0,0 +1,530 @@ +/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary + source code may only be used and distributed under the Widevine License + Agreement. */ + +#ifndef OEMCRYPTO_TA_CRYPTO_AND_KEY_MANAGEMENT_LAYER1_H_ +#define OEMCRYPTO_TA_CRYPTO_AND_KEY_MANAGEMENT_LAYER1_H_ + +#include "OEMCryptoCENCCommon.h" +#include "oemcrypto_key_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup crypto-key-1 Crypto and Key Management (Layer 1) + * + * This component handles loading encrypted keys, decrypting the keys, and using + * the resulting keys to perform cryptographic operations. + * + * Decrypted keys should be protected from user-space. Ideally, they should be + * protected even from a compromised TA, such as by only decrypting them inside + * a separate TA or dedicated cryptography hardware that does not allow the key + * material to be read back. + * + * Layer 1 of this component can be implemented directly. However, there are two + * reference implementations of this component that may be useful to some + * partners. + * + * * Layer 1 assumes it is possible to load every key up to the device’s + * maximum supported number of keys simultaneously. However, many devices have + * hardware that only supports having a small number of keys loaded + * simultaneously. To support such hardware, there is + * `crypto\_and\_key\_management\_layer1\_hw.c`. This reference + * implementation stores keys in a large key table in memory and dynamically + * loads and unloads keys from the crypto hardware’s smaller key table as + * needed. + * + * * If you don’t have crypto hardware at all or if your crypto hardware is + * very weak, you may instead use an OpenSSL-based reference implementation of + * Layer 1 that does all crypto operations in software. This does come at a + * security cost, as the decrypted key material is necessarily exposed to the + * OEMCrypto TA. + * + * @{ + */ + +/** + * Crypto and Key Management layer 1 serves as an abstraction of the + * communication between the OEMCrypto TA and the crypto implementation. The + * underlying crypto implementation can be either software-based such as + * OpenSSL, or a hardware-backed cryptography solution. + * + * Partners implementing the Crypto and Key Management porting layer may either + * 1. Implement wtpi_crypto_and_key_management_interface_layer2.h and + * key_mapping_interface.h, and then use the reference implementation + * wtpi_crypto_and_key_management_layer1_hw.c. This is preferred if there's a + * hardware-backed crypto. + * or + * 2. Implement their own wtpi_crypto_and_key_management_interface_layer1.h, or + * use the reference implementation + * wtpi_crypto_and_key_management_layer1_openssl.c and + * implement wtpi_device_key_access_interface.h and + * wtpi_secure_buffer_access_interface.h. This is preferred if a software-based + * crypto is used. + */ + +typedef struct wtpi_k1_symmetric_key_handle* WTPI_K1_SymmetricKey_Handle; + +/** + * Gets the key size from |key_handle| and places the result in |size|. + * + * Caller retains ownership of all pointers. + * + * @param[in] key_handle: key handle to query for size + * @param[out] size: output size value + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL, or + * |key_handle| is invalid + */ +OEMCryptoResult WTPI_K1_GetKeySize(WTPI_K1_SymmetricKey_Handle key_handle, + KeySize* size); + +/** + * Decrypts |in_buffer_length| bytes of |in_buffer| using AES CBC and |iv| and + * places the result in |out_buffer|. |key_handle| is a handle to the AES key + * used for decryption and |key_length| determines the number of bytes to use + * from |key_handle|. |out_buffer| must be >= |in_buffer_length| bytes. + * + * Caller retains ownership of all pointers. + * + * @param[in] key_handle: key handle to use for the decrypt operation + * @param[in] key_length: length of key in bytes + * @param[in] in_buffer: input data to decrypt + * @param[in] in_buffer_length: length of input data in bytes + * @param[in] iv: initialization vector for AES operation + * @param[out] out_buffer: output buffer for decrypted data, assumed to be the + * same size as the input data + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL, + * |key_length| is not either 16 or 32 bytes, |key_length| is greater than the + * size of the key, |in_buffer_length| is 0 or not a multiple of the AES block + * size, or |key_handle| is invalid + */ +OEMCryptoResult WTPI_C1_AESCBCDecrypt(WTPI_K1_SymmetricKey_Handle key_handle, + size_t key_length, + const uint8_t* in_buffer, + size_t in_buffer_length, + const uint8_t* iv, uint8_t* out_buffer); + +/** + * Encrypts |in_buffer_length| bytes of |in_buffer| using AES CBC and |iv| and + * places the result in |out_buffer|. |key_handle| is a handle to the AES key + * used for encryption. |out_buffer| must be >= |in_buffer_length| bytes. + * + * Caller retains ownership of all pointers. + * + * @param[in] key_handle: key handle to use for the encrypt operation + * @param[in] in_buffer: input data to encrypt + * @param[in] in_buffer_length: length of input data in bytes + * @param[in] iv: initialization vector for AES operation + * @param[out] out_buffer: output buffer for encrypted data, assumed to be the + * same size as the input data + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL, + * |in_buffer_length| is 0 or not a multiple of the AES block size, or + * |key_handle| is invalid + */ +OEMCryptoResult WTPI_C1_AESCBCEncrypt(WTPI_K1_SymmetricKey_Handle key_handle, + const uint8_t* in_buffer, + size_t in_buffer_length, + const uint8_t* iv, uint8_t* out_buffer); + +/** + * Calculates the HMAC of |input_length| bytes of |input| using SHA1 as the + * hash function and places the result in |out_buffer|. |key_handle| is a handle + * to the key used in the derivation. |out_buffer| must be >= 20 bytes. + * + * Caller retains ownership of all pointers. + * + * @param[in] key_handle: key handle for the crypto operation + * @param[in] input: input data + * @param[in] input_length: length of input data in bytes + * @param[out] out_buffer: output buffer, assumed to be >= 20 bytes + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL or + * |input_length| is 0, or |key_handle| is invalid + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures + */ +OEMCryptoResult WTPI_C1_HMAC_SHA1(WTPI_K1_SymmetricKey_Handle key_handle, + const uint8_t* input, size_t input_length, + uint8_t* out_buffer); + +/** + * Calculates the SHA256 hash of |input_length| bytes of |input|, placing + * the result in |out_buffer|. |out_buffer| must be >= 32 bytes. + * + * Caller retains ownership of all pointers. + * + * @param[in] input: input data + * @param[in] input_length: length of input data in bytes + * @param[out] out_buffer: output buffer, assumed to be >= 32 bytes + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL or + * |input_length| is 0 + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures + */ +OEMCryptoResult WTPI_C1_SHA256(const uint8_t* input, size_t input_length, + uint8_t* out_buffer); + +/** + * Calculates the HMAC of |input_length| bytes of |input| using SHA256 as + * the hash function and places the result in |out_buffer|. |key_handle| is a + * handle to the key used in the derivation. |out_buffer| must be >= 32 bytes. + * + * Caller retains ownership of all pointers. + * + * @param[in] key_handle: key handle for HMAC operation + * @param[in] input: input data + * @param[in] input_length: length of input data in bytes + * @param[out] out_buffer: output buffer, assumed to be >= 32 bytes + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL or + * |input_length| is 0, or |key_handle| is invalid + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures + */ +OEMCryptoResult WTPI_C1_HMAC_SHA256(WTPI_K1_SymmetricKey_Handle key_handle, + const uint8_t* input, size_t input_length, + uint8_t* out_buffer); + +/** + * Verifies that the HMAC of |input_length| bytes of |input| matches + * |signature| using SHA256 as the hash function. |key_handle| is a handle to + * the key used in the derivation. + * + * Caller retains ownership of all pointers. + * + * @param[in] key_handle: key handle for HMAC operation + * @param[in] input: input data + * @param[in] input_length: length of input data in bytes + * @param[in] signature: signature to compare with input data after HMAC + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL or + * |input_length| is 0, or |key_handle| is invalid + * @retval OEMCrypto_ERROR_SIGNATURE_FAILURE the signature isn't valid + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures + */ +OEMCryptoResult WTPI_C1_HMAC_SHA256_Verify( + WTPI_K1_SymmetricKey_Handle key_handle, const uint8_t* input, + size_t input_length, const uint8_t* signature); + +/** + * This enum and struct form a tagged union for passing output buffers to + * functions that write to them, such as decryption. Output buffers may be + * clear, insecure buffers that are represented as direct memory pointers or + * they may be secure buffers that are represented as a platform-specific + * handle. + */ +typedef enum OPK_OutputBuffer_Type { + OPK_CLEAR_INSECURE_OUTPUT_BUFFER = 0x77e790db, + OPK_SECURE_OUTPUT_BUFFER = 0x7f3b7fc9, +} OPK_OutputBuffer_Type; + +typedef struct { + /* Flag indicating whether the buffer is secure or not. */ + OPK_OutputBuffer_Type type; + + /* If |type| is OPK_CLEAR_INSECURE_OUTPUT_BUFFER, this is a pointer to memory + that can be written directly and should be accessed through the + |clear_insecure| member of the union. + + If |type| is OPK_SECURE_OUTPUT_BUFFER, this is a platform-specific handle + to a secure buffer and should be accessed through the |secure| member of + the union. */ + union { + uint8_t* clear_insecure; + void* secure; + } buffer; + + /* The total size of the buffer. */ + size_t size; +} OPK_OutputBuffer; + +/** + * Requests that the porting layer copy some data from |in| into the output + * buffer |out|, starting at offset |output_offset| and continuing for |size| + * bytes. It is possible that the memory ranges of |in| and |out| will overlap. + * The implementation of this function must behave correctly even if they + * overlap. + * + * Caller retains ownership of all pointers. + * + * @param[in] input: input data + * @param[in] input_length: length of input data to be copied, in bytes + * @param[in] out: output buffer + * @param[in] output_offset: destination offset in output buffer + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED |out| is a secure buffer and this + * device does not support secure buffers + * @retval OEMCrypto_ERROR_INVALID_CONTEXT |input_length| is 0 or any of the + * pointer parameters are NULL + * @retval OEMCrypto_ERROR_INVALID_CONTEXT |output_offset| + |input_length| is + * greater than |out.size| + * @retval OEMCrypto_ERROR_INVALID_CONTEXT the buffer is secure and the + * handle is invalid + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures + */ +OEMCryptoResult WTPI_C1_CopyToOutputBuffer(const uint8_t* input, + size_t input_length, + const OPK_OutputBuffer* out, + size_t output_offset); + +/** + * Generates |out_length| random bytes and places them in |out|. + * + * Caller retains ownership of all pointers. + * + * @param[out] out: output buffer + * @param[in] out_length: number of bytes of random data to be generated + * + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE |out_length| is too big + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures + * @retval OEMCrypto_SUCCESS otherwise + */ +OEMCryptoResult WTPI_C1_RandomBytes(uint8_t* out, size_t out_length); + +/** + * Initializes key management layer 1. + * + * @return OEMCrypto_SUCCESS or the result returned from the initialization of + * the layer down below, if it exists. + */ +OEMCryptoResult WTPI_K1_InitializeKeyManagement(void); + +/** + * Terminates key management layer 2. + * + * @return OEMCrypto_SUCCESS or the result returned from the termination of + * the layer down below, if it exists. + */ +OEMCryptoResult WTPI_K1_TerminateKeyManagement(void); + +/** + * Creates a layer 1 key handle from |size| bytes of |serialized_bytes| and the + * |key_type|, and places the result in |out_key_handle|. + * + * Caller retains ownership of all parameters. + * + * @param[in] input: input key data + * @param[in] input_length: length of input data in bytes + * @param[in] key_type: type of key + * @param[out] out_key_handle: output key handle + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL, or + * size is 0 + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures + */ +OEMCryptoResult WTPI_K1_CreateKeyHandle( + const uint8_t* input, size_t input_length, SymmetricKeyType key_type, + WTPI_K1_SymmetricKey_Handle* out_key_handle); + +/** + * Creates a device specific key handle that is to be used in the specified + * context. This key should be identical across reboots, but it should *not* be + * the same on different devices. The key should be unique per-context and + * per-key-type; however, MAC_KEY_CLIENT and MAC_KEY_SERVER must be the same + * key. This key will be used to encrypt data and tie it to the current device. + * |context| is the context this key will be used in the key derivation. + * |out_key_type| the type of the key to be derived. + * |out_key_size| is the size of the key to be derived, in bytes. + * |out_key_handle| should be filled with the desired key handle. + * + * Note that, in the event a device is compromised, repaired, and goes through + * keybox renewal, the device key used by this function should be changed as + * well at the same time the device's keybox is renewed. + * + * Caller retains ownership of all parameters. + * + * @param[in] context: 32-bit identifier providing context to the derivation + * step + * @param[in] out_key_type: type of key to produce + * @param[out] out_key_handle: output key handle + * @param[in] out_key_size: size of output key, in bytes + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures + */ +OEMCryptoResult WTPI_K1_DeriveDeviceKeyIntoHandle( + uint32_t context, SymmetricKeyType out_key_type, + WTPI_K1_SymmetricKey_Handle* out_key_handle, KeySize out_key_size); + +/** + * Decrypts |enc_key_length| bytes of |enc_key| using AES CBC with |iv| and the + * decrypting key handle |decrypt_key_handle|, creates a layer 1 key handle from + * the decrypted key with the specified type |key_type|, and places the result + * in |out_key_handle|. + * + * Caller retains ownership of all parameters. + * + * @param[in] decrypt_key_handle: key handle for AES decryption + * @param[in] enc_key: AES-encrypted key to be decrypted and turned into + * a handle + * @param[in] enc_key_length: length of AES-encrypted input key, in bytes + * @param[in] iv: initialization vector + * @param[in] key_type: type of key to produce from the AES-encrypted input + * @param[out] out_key_handle: output key handle + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL, + * |enc_key_length| is not either 16 or 32 bytes, or |decrypt_key_handle| is + * invalid + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures + */ +OEMCryptoResult WTPI_K1_AESDecryptAndCreateKeyHandle( + WTPI_K1_SymmetricKey_Handle decrypt_key_handle, const uint8_t* enc_key, + size_t enc_key_length, const uint8_t* iv, SymmetricKeyType key_type, + WTPI_K1_SymmetricKey_Handle* out_key_handle); + +/** + * Decrypts |enc_mac_keys_length| bytes of |enc_mac_keys| using AES CBC with + * |iv| and the decrypting key handle |decrypt_key_handle|, from the decrypted + * keys creates one MAC_KEY_SERVER layer 1 key handle, and one MAC_KEY_CLIENT + * layer 1 key handle, and places the result in |out_mac_key_server| and + * |out_mac_key_client|, respectively. + * + * Caller retains ownership of all parameters. + * + * @param[in] decrypt_key_handle: key handle for AES decryption + * @param[in] enc_mac_keys: AES-encrypted keys to be decrypted and turned into + * handlese + * @param[in] enc_mac_keys_length: length of AES-encrypted input, in bytes + * @param[in] iv: initialization vector + * @param[out] out_mac_key_server: output key handle for server + * @param[out] out_mac_key_client: output key handle for client + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL, + * |enc_key_length| is not 64 bytes, or |decrypt_key_handle| is invalid + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures + */ +OEMCryptoResult WTPI_K1_AESDecryptAndCreateKeyHandleForMacKeys( + WTPI_K1_SymmetricKey_Handle decrypt_key_handle, const uint8_t* enc_mac_keys, + size_t enc_mac_keys_length, const uint8_t* iv, + WTPI_K1_SymmetricKey_Handle* out_mac_key_server, + WTPI_K1_SymmetricKey_Handle* out_mac_key_client); + +/** + * Derives a layer 1 key handle from input |key_handle| with the specified + * context. The function derives either 128-bit key or 256-bit key. + * + * The derivation process for 128-bit key output: + * 1. Using the input key handle |key_handle|, prepare an AES_CMAC 128-bit + * operation. + * 2. Feed |counter| into the CMAC. + * 3. Feed |context_length| bytes from |context| into the CMAC. + * 4. Create |out_key_handle| with the same process as + * WTPI_K1_CreateKeyHandle(), using the result of the CMAC as the new input key + * data. + * + * The derivation process for 256-bit key output: + * 1. Using the input key handle |key_handle|, prepare an AES_CMAC 128-bit + * operation. + * 2. Feed |counter| into the CMAC. + * 3. Feed |context_length| bytes from |context| into the CMAC. + * 4. Prepare another AES_CMAC 128-bit operation with the same input key handle. + * 5. Feed |counter+1| into the second CMAC. + * 6. Feed |context_length| bytes from |context| into the second CMAC. + * 7. Create |out_key_handle| with the same process as + * WTPI_K1_CreateKeyHandle(), using the concatenated result of the first CMAC + * operation and the second CMAC operation as the new input key data. + * + * Caller retains ownership of all parameters. + * + * @param[in] key_handle: key handle for AES CMAC operation + * @param[in] counter: input value used for CMAC. If this function is being + * called multiple times to derive different keys from the same context, this + * counter should be incremented +2 each time. + * @param[in] context: input data for AES CMAC + * @param[in] context_length: length of context data in bytes + * @param[in] out_key_type: desired type of output key + * @param[in] out_key_size: desired size of output key + * @param[out] out_key_handle: output key handle + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL, + * |context_length| is 0, or |key_handle| is invalid + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures + */ +OEMCryptoResult WTPI_K1_DeriveKeyFromKeyHandle( + WTPI_K1_SymmetricKey_Handle key_handle, uint8_t counter, + const uint8_t* context, size_t context_length, + SymmetricKeyType out_key_type, KeySize out_key_size, + WTPI_K1_SymmetricKey_Handle* out_key_handle); + +/** + * Wraps the |key_handle| into a buffer that can be saved to the file system. + * The wrapping key must be device unique, and is derived with the specified + * |context|. Caller ensures that |wrapped_key_length| is equal to the wrapped + * key size specified in wtpi_config_macros.h. + * + * Caller retains ownership of all parameters. + * + * @param[in] context: 32-bit identifier to act as context + * @param[in] key_handle: key handle to be wrapped + * @param[in] key_type: type of input key + * @param[out] wrapped_key: output buffer + * @param[in] wrapped_key_length: length of output buffer + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL, or + * |key_handle| is invalid + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures + */ +OEMCryptoResult WTPI_K1_WrapKey(uint32_t context, + WTPI_K1_SymmetricKey_Handle key_handle, + SymmetricKeyType key_type, uint8_t* wrapped_key, + size_t wrapped_key_length); + +/** + * Unwraps and creates a layer 1 key handle from |wrapped_key_length| bytes of + * |wrapped_key| and the |key_type|, and places the result in |out_key_handle|. + * The unwrapping key must be device unique, and is derived with the specified + * |context|. + * + * Caller retains ownership of all parameters. + * + * @param[in] context: 32-bit identifier to act as context + * @param[in] wrapped_key: key to be unwrapped + * @param[in] wrapped_key_length: length of input key + * @param[in] key_type: type of input key + * @param[out] out_key_handle: output key handle + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL, or + * |wrapped_key_length| is not either 16 or 32 bytes + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures + */ +OEMCryptoResult WTPI_K1_UnwrapIntoKeyHandle( + uint32_t context, const uint8_t* wrapped_key, size_t wrapped_key_length, + SymmetricKeyType key_type, WTPI_K1_SymmetricKey_Handle* out_key_handle); + +/** + * Frees |key_handle| that was constructed from a previous call to + * WTPI_K1_CreateKeyHandle. + * + * Caller retains ownership of all pointers. + * + * @param[in] key_handle: key handle to be freed + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT |key_handle| is invalid + */ +OEMCryptoResult WTPI_K1_FreeKeyHandle(WTPI_K1_SymmetricKey_Handle key_handle); + +/// @} + +#ifdef __cplusplus +} +#endif + +#endif /* OEMCRYPTO_TA_CRYPTO_AND_KEY_MANAGEMENT_LAYER1_H_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_crypto_and_key_management_interface_layer2.h b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_crypto_and_key_management_interface_layer2.h new file mode 100644 index 0000000..e491a8f --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_crypto_and_key_management_interface_layer2.h @@ -0,0 +1,509 @@ +/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary + source code may only be used and distributed under the Widevine License + Agreement. */ + +#ifndef OEMCRYPTO_TA_WTPI_CRYPTO_AND_KEY_MANAGEMENT_INTERFACE_LAYER2_H_ +#define OEMCRYPTO_TA_WTPI_CRYPTO_AND_KEY_MANAGEMENT_INTERFACE_LAYER2_H_ + +#include +#include +#include + +#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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_crypto_asymmetric_interface.h b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_crypto_asymmetric_interface.h new file mode 100644 index 0000000..7de7ba7 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_crypto_asymmetric_interface.h @@ -0,0 +1,390 @@ +/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary + source code may only be used and distributed under the Widevine License + Agreement. */ + +#ifndef OEMCRYPTO_TA_WTPI_CRYPTO_ASYMMETRIC_INTERFACE_H_ +#define OEMCRYPTO_TA_WTPI_CRYPTO_ASYMMETRIC_INTERFACE_H_ + +#include "OEMCryptoCENC.h" + +#include "oemcrypto_key_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup asymmetric-crypto Asymmetric Cryptography + * + * This component handles loading decrypted asymmetric keys, using the keys to + * perform cryptographic operations, and wrapping the keys before saving to + * persistent storage. + * + * @{ + */ + +/** + * Forward declaration of a pointer to an implementation-specific struct holding + * all asymmetric key information. + */ +typedef struct tee_asymmetric_key_handle* WTPI_AsymmetricKey_Handle; + +/** + * Creates a key handle from |input_length| bytes of |input| and the + * |key_type|, and places the result in |key_handle|. + * + * If the key type is DRM_RSA_PRIVATE_KEY, then |input| must be a DER-encoded + * PKCS8 RSA private key. + * + * If the key type is DRM_ECC_PRIVATE_KEY, then |input| must be a DER-encoded + * PKCS8 ECPrivateKey. + * + * If the key type is PROV40_ED25519_PRIVATE_KEY, then |input| must be a raw + * key. + * + * Caller retains ownership of all parameters. + * + * @param[in] input: DER-encoded PKCS8 private key byte array + * @param[in] input_length: length of the input data + * @param[in] key_type: type of asymmetric key + * @param[out] key_handle: output key handle + * + * @retval OEMCrypto_ERROR_INVALID_CONTEXT if any of the parameters are NULL or + * |input_length| is 0 + * @retval OEMCrypto_ERROR_INVALID_KEY if |serialized_bytes| does not point + * to a valid private key of the specified |key_type| + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures/ + * @retval OEMCrypto_SUCCESS otherwise + */ +OEMCryptoResult WTPI_CreateAsymmetricKeyHandle( + const uint8_t* input, size_t input_length, AsymmetricKeyType key_type, + WTPI_AsymmetricKey_Handle* key_handle); + +/** + * Creates a key handle from |input_length| bytes of |input| and the + * |key_type|, and places the result in |key_handle|. + * + * |*allowed_schemes| should be filled with the padding schemes that can be used + * with the key. + * + * Caller retains ownership of all parameters. + * + * @param[in] input: wrapped asymmetric key + * @param[in] input_length: size of the wrapped asymmetric key + * @param[in] key_type: type of asymmetric key + * @param[out] key_handle: output key handle + * @param[out] allowed_schemes: allowed padding schemes + * + * @retval OEMCrypto_ERROR_INVALID_CONTEXT if any of the parameters are NULL or + * |input_length| is 0 + * @retval OEMCrypto_ERROR_INVALID_KEY if |key_type| is DRM_PRIVATE_RSA_KEY + * and |serialized_bytes| is an invalid PKCS8 RSA private key; or if |key_type| + * DRM_ECC_PRIVATE_KEY and |serialized_bytes| is not a valid ECPrivateKey + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures + * @retval OEMCrypto_SUCCESS otherwise + */ +OEMCryptoResult WTPI_UnwrapIntoAsymmetricKeyHandle( + const uint8_t* input, size_t input_length, AsymmetricKeyType key_type, + WTPI_AsymmetricKey_Handle* key_handle, uint32_t* allowed_schemes); + +/** + * Frees |key_handle| that was constructed from a previous call to + * WTPI_CreateAsymmetricKeyHandle. + * + * Caller retains ownership of all pointers. + * + * @param[in] key_handle: previously allocated key handle + * + * @retval OEMCrypto_ERROR_INVALID_CONTEXT if |key_handle| is NULL + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures + * @retval OEMCrypto_SUCCESS otherwise + */ +OEMCryptoResult WTPI_FreeAsymmetricKeyHandle( + WTPI_AsymmetricKey_Handle key_handle); + +/** + * Calculates the buffer size needed to wrap the given private key. This is + * given the number of bytes in the encrypted private key. + * + * Caller retains ownership of all pointers. + * + * @param[in] enc_private_key_length: length of encrypted private key to be + * wrapped + * @param[in] key_type: type of asymmetric key + * @param[out] buffer_size: output result with required wrapping size + * + * @retval OEMCrypto_ERROR_INVALID_CONTEXT if |buffer_size| is NULL, or if there + * is an overflow when computing the |buffer_size| + * @retval OEMCrypto_SUCCESS otherwise + */ +OEMCryptoResult WTPI_GetWrappedAsymmetricKeySize(size_t enc_private_key_length, + AsymmetricKeyType key_type, + size_t* buffer_size); + +// TODO(b/185149406): Consider using WTPI_AsymmetricKey_Handle instead to avoid +// passing clear keys around. +/** + * Wraps the key data into a buffer that can be saved to the file system. The + * wrapping must be device unique. + * + * Caller retains ownership of |clear_key| and |output| and they must + * not be NULL. Caller ensures that size is at least as big as the wrapped key + * size specified in WTPI_GetWrappedAsymmetricKeySize. + * + * This is given the clear, PKCS8-padded key and the key may be prefixed with + * "SIGN" and a 4-byte code for the padding schemes. + * + * @param[out] output: destination buffer that will contain the wrapped key data + * @param[in] output_length: length of destination buffer + * @param[in] key_type: type of asymmetric key + * @param[in] clear_key: DER-encoded PKCS8 RSA private key data with 8 bytes of + * prefix data or PKCS8 ECPrivateKey (no prefix data). + * @param[in] clear_key_length: length of input data + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL, + * |clear_key_length| is 0 + * @retval OEMCrypto_ERROR_SHORT_BUFFER output_length is too small + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE otherwise + */ +OEMCryptoResult WTPI_WrapAsymmetricKey(uint8_t* output, size_t output_length, + AsymmetricKeyType key_type, + const uint8_t* clear_key, + size_t clear_key_length); + +/** + * Sign |message_length| bytes of |message| with the given RSA key handle using + * the given |padding scheme| and place the result in |signature|. + * |key| is a handle to the RSA key used for signing. + * + * Caller retains ownership of all pointers. + * + * @param[in] key: handle with RSA key required to sign + * @param[in] message: input data to be signed + * @param[in] message_length: length of data to be signed + * @param[out] signature: destination buffer for output signature + * @param[in,out] signature_length: size of destination buffer + * @param[in] padding_scheme: RSA padding scheme used for signing + * + * @retval OEMCrypto_ERROR_SHORT_BUFFER if |signature_length| is too small or if + * |signature| is NULL, in which case it sets |signature_length| to the + * appropriate length + * @retval OEMCrypto_ERROR_INVALID_CONTEXT if |message_length| is 0 or if any of + * the pointers except |signature| are NULL + * @retval OEMCrypto_ERROR_INVALID_KEY if the padding_scheme provided is not + * supported + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures + * @retval OEMCrypto_SUCCESS otherwise + */ +OEMCryptoResult WTPI_RSASign(WTPI_AsymmetricKey_Handle key, + const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length, + RSA_Padding_Scheme padding_scheme); + +/** + * Decrypts |input_length| bytes of |input| and places it in |out|. The padding + * scheme shall only be PKCS1 OAEP. |key| is a handle to the RSA key used for + * decryption. + * + * Caller retains ownership of all pointers. + * + * @param[in] key: handle with RSA key required to decrypt + * @param[in] input: input data to be decrypted + * @param[in] input_length: length of data to be decrypted + * @param[out] out: destination buffer for decrypted data + * @param[in,out] out_length: size of destination buffer + * + * @retval OEMCrypto_ERROR_INVALID_CONTEXT if any of the pointers are NULL or + * |input_length| is 0 + * @retval OEMCrypto_ERROR_SHORT_BUFFER if |out_length| is too small, in which + * case it sets |out_length| to the appropriate length + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures + * @retval OEMCrypto_SUCCESS otherwise + */ +OEMCryptoResult WTPI_RSADecrypt(WTPI_AsymmetricKey_Handle key, + const uint8_t* input, size_t input_length, + uint8_t* out, size_t* out_length); + +/** + * Sign |message_length| bytes of |message| with the given ECC key handle using + * the ECDSA with a curve specific hashing algorithm and place the result in + * |signature|. |key| is a handle to the ECC key used for signing. The + * resulting signature must be ASN.1 encoded as specified in RFC 3279 2.2.3. + * + * Caller retains ownership of all pointers. + * + * @param[in] key: handle with ECC key, required for signing. + * @param[in] message: input data to be signed + * @param[in] message_length: length of input data in bytes + * @param[out] signature: destination buffer for signature + * @param[in,out] signature_length: size of |signature| buffer, may be + * modified based on used/required space of output. + * + * @retval OEMCrypto_ERROR_SHORT_BUFFER if |signature_length| is too small or if + * |signature| is NULL, in which case it sets |signature_length| to the + * appropriate length + * @retval OEMCrypto_ERROR_INVALID_CONTEXT if |message_length| is 0 or if any of + * the pointers except |signature| are NULL + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures + * @retval OEMCrypto_SUCCESS otherwise + */ +OEMCryptoResult WTPI_ECCSign(WTPI_AsymmetricKey_Handle key, + const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length); + +/** + * Derives the ECC-based session key using Widevine ECDH. The derived + * key is placed into |session_key|. This key is a AES-256 to be used + * as the key in the CMAC function used in the MAC and ENC key session + * processes. + * + * Caller retains ownership of all pointers. + * + * @param[in] key: handle with ECC key, required for derivation. + * @param[in] key_source: an ephemeral ECC public key used in ECDH. + * @param[in] key_source_length: length of |key_source| + * @param[out] session_key: destination buffer for derivated session key + * @param[in,out] session_key_length: size of |session_key| buffer, may + * be modified based on used/required space of output. + * + * @retval OEMCrypto_ERROR_SHORT_BUFFER if |session_key_length| is too small or + * if |session_key| is NULL, in which case it sets |session_key_length| + * to the appropriate length + * @retval OEMCrypto_ERROR_INVALID_CONTEXT if |key_source_length| is 0 or if + * any of the pointers except |session_key| are NULL + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures + * @retval OEMCrypto_SUCCESS otherwise + */ +OEMCryptoResult WTPI_ECCDeriveSessionKey(WTPI_AsymmetricKey_Handle key, + const uint8_t* key_source, + size_t key_source_length, + uint8_t* session_key, + size_t* session_key_length); + +/** + * Gets the maximum RSA, ECC or ED25519 signature size from |key| and places the + * result in |signature_length|. + * Note, it is possible that the signature generate by this key be a few bytes + * shorter depending on the actual signature value. + * + * Caller retains ownership of all pointers. + * + * @param[in] key: input key handle + * @param[out] signature_length: max size of the signature generated + * by |key|. + * + * @retval OEMCrypto_ERROR_INVALID_CONTEXT if any of the pointers are NULL + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures + * @retval OEMCrypto_SUCCESS otherwise + */ +OEMCryptoResult WTPI_GetSignatureSize(WTPI_AsymmetricKey_Handle key, + size_t* signature_length); + +/** + * Sign |message_length| bytes of |message| with the given ED25519 key handle + * and place the result in |signature|. |key| is a handle to the ED25519 key + * used for signing. + * + * Caller retains ownership of all pointers. + * + * @param[in] key: handle with ED25519 key, required for signing. + * @param[in] message: input data to be signed + * @param[in] message_length: length of input data in bytes + * @param[out] signature: destination buffer for signature + * @param[in,out] signature_length: size of |signature| buffer, may be + * modified based on used/required space of output. + * + * @retval OEMCrypto_ERROR_SHORT_BUFFER if |signature_length| is too small or if + * |signature| is NULL, in which case it sets |signature_length| to the + * appropriate length + * @retval OEMCrypto_ERROR_INVALID_CONTEXT if |message_length| is 0 or if any of + * the pointers except |signature| are NULL + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures + * @retval OEMCrypto_SUCCESS otherwise + */ +OEMCryptoResult WTPI_ED25519Sign(WTPI_AsymmetricKey_Handle key, + const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length); + +/** + * Writes the boot certificate chain (BCC) in provisioning 4.0 to |out|, with + * number of bytes specified in |out_length|. + * + * Caller retains ownership of all pointers. + * + * @param[out] out: output buffer to write BCC + * @param[in,out] out_length: size of bcc buffer, may be modified based on + * used/required space of output. + * + * @retval OEMCrypto_ERROR_INVALID_CONTEXT if |out_length| is NULL + * @retval OEMCrypto_ERROR_SHORT_BUFFER if |out_length| is too small, or |out| + * is NULL, in which case it sets |out_length| to the appropriate length + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures + * @retval OEMCrypto_SUCCESS otherwise + */ +OEMCryptoResult WTPI_GetBootCertificateChain(uint8_t* out, size_t* out_length); + +/** + * Generate a pair of RSA or ECC key pair, which will be used in the certificate + * signing request as specified in provisioning 4. The output key type must be + * either RSA or ECC, which should be specified in |key_type|. + * + * Returns + * OEMCrypto_ERROR_SHORT_BUFFER if |wrapped_private_key_length| or + * |public_key_length| is too small, or if |wrapped_private_key| or |public_key| + * is NULL, in which case the appropriate length should be returned. + * OEMCrypto_ERROR_INVALID_CONTEXT if |key_type|, |wrapped_private_key_length| + * or |public_key_length| is NULL. + * OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures. + * OEMCrypto_SUCCESS otherwise. + * + * Caller retains ownership of all pointers. + * + * @param[out] key_type: Type (RSA or EC) of the generated key pair. + * @param[out] wrapped_private_key: The generated private key, wrapped with + * encryption key for storage. + * @param[in,out] wrapped_private_key_length: size of |wrapped_private_key| + * buffer, may be modified based on used/required space of output. + * @param[out] public_key: The generated public key. + * @param[in,out] public_key_length: size of |public_key_length| buffer, may be + * modified based on used/required space of output. + */ +OEMCryptoResult WTPI_GenerateRandomCertificateKeyPair( + AsymmetricKeyType* key_type, uint8_t* wrapped_private_key, + size_t* wrapped_private_key_length, uint8_t* public_key, + size_t* public_key_length); + +/** + * Used the device specific asymmetric key pair as specified in provisioning 4 + * to sign |message| and write the COSE_SIGN1 format signature to |signature|. + * Note that the device key must be unique per individual device. The key must + * be identical across reboots. + * + * Caller retains ownership of all parameters. + * + * @param[in] message: input data to be signed + * @param[in] message_length: length of input data in bytes + * @param[out] signature: destination buffer for signature + * @param[in,out] signature_length: size of |signature| buffer, may be + * modified based on used/required space of output. + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_SHORT_BUFFER if |signature_length| is too small or if + * |signature| is NULL, in which case it sets |signature_length| to the + * appropriate length. + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE if |message_length| is too large. + * @retval OEMCrypto_ERROR_INVALID_CONTEXT if |message_length| is 0 or if any of + * the pointers except |signature| are NULL. + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures. + */ +OEMCryptoResult WTPI_DeviceKeyCoseSign1(const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length); + +/// @} + +#ifdef __cplusplus +} +#endif + +#endif /* OEMCRYPTO_TA_WTPI_CRYPTO_ASYMMETRIC_INTERFACE_H_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_decrypt_sample_interface.h b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_decrypt_sample_interface.h new file mode 100644 index 0000000..d9c1abf --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_decrypt_sample_interface.h @@ -0,0 +1,74 @@ +/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary + source code may only be used and distributed under the Widevine License + Agreement. */ + +#ifndef OEMCRYPTO_TA_WTPI_DECRYPT_SAMPLE_INTERFACE_H_ +#define OEMCRYPTO_TA_WTPI_DECRYPT_SAMPLE_INTERFACE_H_ + +#include "OEMCryptoCENCCommon.h" +#include "oemcrypto_output.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup decrypt-sample Sample Decrypt + * + * Data arrives in OEMCrypto in the form of samples made up of smaller + * subsamples. Interpreting the subsamples to know which bytes should be left + * clear or decrypted and what IV to use for the latter is non-trivial. Some + * devices have dedicated hardware for doing decryption of a full sample in one + * pass, skipping and decrypting bytes as appropriate. On such devices, + * decrypting the subsamples piecemeal is inefficient. But other devices can + * only decrypt one portion of a subsample at a time. + * + * The Sample Decryption interface defines decryption in terms of full + * samples. If your device has hardware support for full-sample decryption, you + * will get the best performance by implementing this layer yourself. + * + * However, if your device does not have support for hardware full-sample + * decryption, you may find it simpler to use the reference implementation of + * this layer. This implementation will handle interpreting the subsamples for + * you and send the decryption to the WTPI’s other cryptography interfaces one + * subsample at a time. + * + * @{ + */ + +/** + * Decrypt Sample layer defines the interface between the OEMCrypto TA and the + * implementation of sample decryption. + * + * Partners implementing the Decrypt Sample porting layer may either + * 1. Implement their own wtpi_decrypt_sample_interface.h. This is preferred if + * the device has hardware support for full-sample decryption, or + * 2. Use the reference implementation wtpi_decrypt_sample.c. This is preferred + * when there is no hardware support for full-sample decryption. The reference + * implementation will split the subsamples and decrypt them individually using + * the crypto_and_key_management_interface_layer1 component. + */ + +/** + * Decrypts one sample |sample| with the specified |pattern| and |cipher_mode| + * using the |key_handle| to the current content key, into buffer + * |output_buffer| starting at an offset |output_offset|. + * Returns OEMCrypto_ERROR_INVALID_CONTEXT if any of the parameters are NULL or + * invalid, or the result of DecryptSubsample if failing to decrypt any + * subsample, and OEMCrypto_SUCCESS otherwise. + * Caller retains ownership of all parameters. + */ +OEMCryptoResult WTPI_DecryptSample( + WTPI_K1_SymmetricKey_Handle key_handle, + const OEMCrypto_SampleDescription* sample, + const OEMCrypto_CENCEncryptPatternDesc* pattern, + const OPK_OutputBuffer* output_buffer, size_t output_offset, + OEMCryptoCipherMode cipher_mode); + +/// @} + +#ifdef __cplusplus +} +#endif + +#endif /* OEMCRYPTO_TA_WTPI_DECRYPT_SAMPLE_INTERFACE_H_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_device_key_access_interface.h b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_device_key_access_interface.h new file mode 100644 index 0000000..54bddae --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_device_key_access_interface.h @@ -0,0 +1,49 @@ +/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary + source code may only be used and distributed under the Widevine License + Agreement. */ + +#ifndef OEMCRYPTO_TA_WTPI_DEVICE_KEY_ACCESS_INTERFACE_H_ +#define OEMCRYPTO_TA_WTPI_DEVICE_KEY_ACCESS_INTERFACE_H_ + +#include + +#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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_device_key_interface.h b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_device_key_interface.h new file mode 100644 index 0000000..c401960 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_device_key_interface.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_device_renewal_interface_layer1.h b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_device_renewal_interface_layer1.h new file mode 100644 index 0000000..c63f085 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_device_renewal_interface_layer1.h @@ -0,0 +1,123 @@ +/* Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary + source code may only be used and distributed under the Widevine + License Agreement. */ + +#ifndef OEMCRYPTO_TA_WTPI_DEVICE_RENEWAL_INTERFACE_LAYER_1_H_ +#define OEMCRYPTO_TA_WTPI_DEVICE_RENEWAL_INTERFACE_LAYER_1_H_ + +#include + +#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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_device_renewal_interface_layer2.h b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_device_renewal_interface_layer2.h new file mode 100644 index 0000000..2872888 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_device_renewal_interface_layer2.h @@ -0,0 +1,73 @@ +/* Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary + source code may only be used and distributed under the Widevine + License Agreement. */ + +#ifndef OEMCRYPTO_TA_WTPI_DEVICE_RENEWAL_INTERFACE_LAYER_2_H_ +#define OEMCRYPTO_TA_WTPI_DEVICE_RENEWAL_INTERFACE_LAYER_2_H_ + +#include +#include + +#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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_generation_number_interface.h b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_generation_number_interface.h new file mode 100644 index 0000000..8ff1097 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_generation_number_interface.h @@ -0,0 +1,102 @@ +/* Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary + source code may only be used and distributed under the Widevine + License Agreement. */ + +#ifndef OEMCRYPTO_TA_WTPI_GENERATION_NUMBER_INTERFACE_H_ +#define OEMCRYPTO_TA_WTPI_GENERATION_NUMBER_INTERFACE_H_ + +#include + +#include "OEMCryptoCENC.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup gen-number Generation Number + * + * OEMCrypto needs to store the usage table’s generation number in persistent + * storage with antirollback protection. Persistent storage for the generation + * number should have antirollback protection, such as RPMB. + * + * Partners implementing a porting layer may either + * 1. Implement wtpi_persistent_storage.h and wtpi_clock_interface_layer2.h, + * and then use the reference implementation wtpi_clock_and_gn_layer1.c for + * the clock and generation interfaces. This is preferred if the hardware + * secure timer resets to 0 whenever the device is inactive. + * or + * 2. Implement both wtpi_clock_interface_layer1.h and + * this wtpi_generation_number_interface.h. This is preferred if the system + * has a hardware secure wall clock. + * @{ + */ + +/** + * Prepare to load the generation number. If the generation number is loaded + * asynchronously, this should initialize that process so that the next call to + * WTPI_LoadGenerationNumber does not block for too long. + * + * By "too long" we mean that initialization should not be blocked for more than + * about 3 seconds. In a normal situation, we expect initialization to take on + * the order of 10 to 100 milliseconds or less. This is a soft requirement. If + * there is a good reason for a device to take much longer to initialize, then + * the implementer may allow this to block as long as needed. It is the + * implementer's responsibility to consider the user experience. + * + * The generation number should be stored in secure persistent storage. By + * *persistent* we mean that the generation number should not be changed by + * shutting down and later restarting the system. By *secure* we mean that the + * user should not be able to modify the generation number. In particular, the + * user should not be able to revert the generation number to a previous value. + * + * Returns OEMCrypto_SUCCESS on success. On failure, initialization of the TA + * will fail. + */ +OEMCryptoResult WTPI_PrepareGenerationNumber(void); + +/** + * Load the usage table generation number. This is called once, on first use of + * the usage table. This is expected to block until a value is available. It is + * the porting interface's responsibility to fail if this blocks too + * long. Returns OEMCrypto_SUCCESS on success. Other return values will fail the + * OEMCrypto initialization. If the generation number has never been saved + * before, a random number should be generated -- this is NOT an error. + * + * @param[out] value: pointer to generation number. + */ +OEMCryptoResult WTPI_LoadGenerationNumber(uint64_t* value); + +/** + * Save the generation number. + * + * If the generation number is saved asynchronously, then it is OK for the first + * call to this function to begin a save process and then return + * OEMCrypto_SUCCESS. Subsequent calls will verify that the previous save has + * completed successfully. If the previous save resulted in an error, then this + * call will return an error. If the previous call has not completed, then this + * call should block until the previous save finishes before initializing a new + * save. If the previous call was successful, then this call will initialize a + * new save and return OEMCrypto_SUCCESS. It is the porting interface's + * responsibility to fail if this blocks too long. + * + * If the generation number is saved synchronously, then this function should + * attempt to save the generation number and return OEMCrypto_SUCCESS if the + * save was successful. + * + * Returns OEMCrypto_SUCCESS on success. Other return values will fail the + * current attempt to save the usage table. If a failure actually indicates that + * the previous save had failed, then the usage table will be saved with a + * generation number skew of 1. When the usage table is loaded with a generation + * skew of 1, it will result in a warning, but not a failure. + * + * @param[in] value: generation number. + */ +OEMCryptoResult WTPI_SaveGenerationNumber(uint64_t value); + +/// @} + +#ifdef __cplusplus +} +#endif + +#endif /* OEMCRYPTO_TA_WTPI_GENERATION_NUMBER_INTERFACE_H_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_idle_interface.h b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_idle_interface.h new file mode 100644 index 0000000..d25aa58 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_idle_interface.h @@ -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 + +#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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_initialize_terminate_interface.h b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_initialize_terminate_interface.h new file mode 100644 index 0000000..935602c --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_initialize_terminate_interface.h @@ -0,0 +1,55 @@ +/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary + source code may only be used and distributed under the Widevine + License Agreement. */ + +#ifndef OEMCRYPTO_TA_WTPI_INITIALIZE_TERMINATE_INTERFACE_H_ +#define OEMCRYPTO_TA_WTPI_INITIALIZE_TERMINATE_INTERFACE_H_ + +#include "OEMCryptoCENCCommon.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @mainpage Widevine TEE Porting Layer API + * + * The Widevine TEE Porting Interface (WTPI) is a collection of headers that + * define the interface between the OPK TA code and system-specific or + * hardware-specific features. Together, they define the abstraction layer + * between the TA code and the hardware. Each header file defines a set of + * related functions. It is your responsibility to provide an implementation for + * each WTPI function. The TA will not compile without implementations of all + * the WTPI components. + * + * See the [integration guide](../../../integration#wtpi-components) for a list + * of all WTPI components. + */ + +/** @defgroup init-term Initialization/Termination + * + * This interface defines functions that will be called when the OEMCrypto is + * initialized or terminated. They can be used to do any initial setup or + * cleanup that you need to do for the TA to be operational. It is also + * acceptable for these functions to do nothing. + * + * @{ + */ + +/** + * Set up for any work needed for initializing the TA-specific components of the + * TEE. Returns OEMCrypto_SUCCESS on success and an error, which will be logged, + * on failure. */ +OEMCryptoResult WTPI_Initialize(void); + +/** + * Set up for any work needed for terminating the TA-specific components of the + * TEE. Returns OEMCrypto_SUCCESS on success and an error, which will be logged, + * on failure. */ +OEMCryptoResult WTPI_Terminate(void); + +/// @} +#ifdef __cplusplus +} +#endif + +#endif /* OEMCRYPTO_TA_WTPI_INITIALIZE_TERMINATE_INTERFACE_H_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_logging_interface.h b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_logging_interface.h new file mode 100644 index 0000000..b377a7a --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_logging_interface.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_persistent_storage.h b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_persistent_storage.h new file mode 100644 index 0000000..d8a071a --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_persistent_storage.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_root_of_trust_interface_layer1.h b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_root_of_trust_interface_layer1.h new file mode 100644 index 0000000..7815cd4 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_root_of_trust_interface_layer1.h @@ -0,0 +1,129 @@ +/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary + source code may only be used and distributed under the Widevine + License Agreement. */ + +#ifndef OEMCRYPTO_TA_WTPI_ROOT_OF_TRUST_INTERFACE_LAYER1_H_ +#define OEMCRYPTO_TA_WTPI_ROOT_OF_TRUST_INTERFACE_LAYER1_H_ + +#include "stddef.h" +#include "stdint.h" + +#include "OEMCryptoCENC.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup layer1-rot Layer 1 Root Of Trust + * Keybox access functions called directly by OPK code. + * + * This is the top layer of the porting layer. The OPK directly calls + * functions in this file. Partners have the option to implement these functions + * directly, or use the reference version of the layer 1 functions, and instead + * implement the layer 2 functions. + * + * @{ + */ + +/** Initialize all necessary data. */ +OEMCryptoResult WTPI_InitializeKeybox(void); + +/** Terminate anything that needs terminating. */ +OEMCryptoResult WTPI_TerminateKeybox(void); + +/** + * Unwrap and store the buffer in non-persistent memory reserved for ROT. This + * is used once in the factory to install the real keybox. If possible, in order + * to avoid buffer overflow attacks, we recommend partners to keep this separate + * from the device key in the keybox, so that when this accessed, the device key + * is not exposed. It may be wise to make this function a no-op when the device + * is not in factory mode. + * + * @param[in] input: keybox to be installed + * @param[in] length: size of the keybox + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT if |input| is NULL + * @retval OEMCrypto_ERROR_SHORT_BUFFER if the input length is too small + */ +OEMCryptoResult WTPI_UnwrapValidateAndInstallKeybox(const uint8_t* input, + size_t length); + +/** + * Attempt to validate the current keybox loaded. + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_BAD_MAGIC if magic field is not "kbox" + * @retval OEMCrypto_ERROR_BAD_CRC if computed CRC is not equivalent to stored + * CRC + */ +OEMCryptoResult WTPI_ValidateKeybox(void); + +/** + * Get the 72 byte encrypted key data from the current keybox. |key_data| must + * be >= 72 bytes. + * + * Caller retains ownership of all pointers. + * + * @param[out] key_data: output test key data + * @param[in] length: size of the key data + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT if |key_data| is NULL + */ +OEMCryptoResult WTPI_GetKeyDataFromKeybox(uint8_t* key_data, size_t length); + +/** + * Load a test keybox to be used until the next OEMCrypto_Terminate call. + * |test_keybox| must be >= 128 bytes. + * + * Caller retains ownership of all pointers. + * + * @param[out] test_keybox: output test keybox + * @param[in] length: size of the keybox + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT if |test_keybox| is NULL + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE otherwise + */ +OEMCryptoResult WTPI_LoadTestKeybox(const uint8_t* test_keybox, size_t length); + +/** + * Create a key handle from the device key in the keybox. The caller is + * responsible for calling WTPI_K1_FreeKeyHandle on out when the key is no + * longer needed. + * + * Caller retains ownership of all pointers. + * + * @param[out] out: output key handle + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT if |out| is NULL + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE otherwise + */ +OEMCryptoResult WTPI_K1_CreateKeyHandleFromKeybox( + WTPI_K1_SymmetricKey_Handle* out); + +/** + * Gets the device id from the current provisioning method. If the keybox is + * used, sets |device_id| with the device id in keybox. + * + * Caller retains ownership of all pointers. + * + * @param[out] device_id: output device ID + * @param[in] device_id_length: size of device ID + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT if |device_id| is NULL, or + * |device_id_length| is smaller than KEYBOX_DEVICE_ID_SIZE + */ +OEMCryptoResult WTPI_GetDeviceIDFromKeybox(uint8_t* device_id, + size_t device_id_length); + +/// @} +#ifdef __cplusplus +} +#endif + +#endif /* OEMCRYPTO_TA_WTPI_ROOT_OF_TRUST_INTERFACE_LAYER1_H_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_root_of_trust_interface_layer2.h b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_root_of_trust_interface_layer2.h new file mode 100644 index 0000000..15ecbe9 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_root_of_trust_interface_layer2.h @@ -0,0 +1,73 @@ +/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary + source code may only be used and distributed under the Widevine + License Agreement. */ + +#ifndef OEMCRYPTO_TA_WTPI_ROOT_OF_TRUST_INTERFACE_LAYER2_H_ +#define OEMCRYPTO_TA_WTPI_ROOT_OF_TRUST_INTERFACE_LAYER2_H_ + +#include "stddef.h" +#include "stdint.h" + +#include "OEMCryptoCENC.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup layer2-rot Layer 2 Root Of Trust + * Hardware level access to root of trust. + * + * These functions provide a simple interface to the hardware secure persistent + * storage. Partners may choose to implement these functions and use the layer + * 1 code provided by Widevine to present a higher level interface. + * + * These functions should be easier to implement, but using the layer 1 + * functions may reduce performance or security. + * + * @{ + */ + +/** Use the system specific key to unwrap the root of trust buffer. This + * function is used in the factory. + * + * @param[in] input: buffer with encrypted root of trust. + * @param[in] input_length: the length of the input. + * @param[out] output: buffer where clear root of trust will be written. + * @param[in,out] output_length: on input, it's the buffer size, on output it's + * how much was unwrapped. If output_length is too short for the entire root + * of trust, the error code OEMCrypto_ERROR_SHORT_BUFFER shall be returned. + */ +OEMCryptoResult WTPI_UnwrapRootOfTrust(const uint8_t* input, + size_t input_length, uint8_t* output, + size_t* output_length); + +/** Save the buffer to secure permanent storage. + * To prevent accidentally destroying the keybox, production devices should + * return an error code if this function is called when the device is not in + * "factory mode". + * + *@param[in] keybox: buffer of the keybox. + *@param[in] length: on input, it's the buffer size, on output it's the actual + * size loaded into the buffer. If length is too short for the entire root + * of trust, the error code OEMCrypto_ERROR_SHORT_BUFFER shall be returned. + * Otherwise, set length to the size of the root of trust loaded. + */ +OEMCryptoResult WTPI_SaveRootOfTrust(const uint8_t* keybox, size_t length); + +/** Read the buffer from secure permanent storage. + * + *@param[in] keybox: buffer of the keybox. + *@param[in,out] length: on input, it's the buffer size, on output it's the + * actual size loaded into the buffer. If length is too short for the entire + * root of trust, the error code OEMCrypto_ERROR_SHORT_BUFFER shall be + * returned. Otherwise, set length to the size of the root of trust loaded. + */ +OEMCryptoResult WTPI_LoadRootOfTrust(uint8_t* keybox, size_t* length); + +/// @} + +#ifdef __cplusplus +} +#endif + +#endif /* OEMCRYPTO_TA_WTPI_ROOT_OF_TRUST_INTERFACE_LAYER2_H_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_secure_buffer_access_interface.h b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_secure_buffer_access_interface.h new file mode 100644 index 0000000..142d86c --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_secure_buffer_access_interface.h @@ -0,0 +1,45 @@ +/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary + source code may only be used and distributed under the Widevine License + Agreement. */ + +#ifndef OEMCRYPTO_TA_SECURE_BUFFER_ACCESS_H_ +#define OEMCRYPTO_TA_SECURE_BUFFER_ACCESS_H_ + +#include "OEMCryptoCENCCommon.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup secure-buffer Secure Buffer Access + * + * Interface used by the reference + * [sample decryption interface](@ref decrypt-sample) to access secure buffers. + * + * @{ + */ + +/** + * Get the secure buffer address given a pointer to the secure memory with an + * offset, and places it in |out_addr|. + * + * Caller retains ownership of all pointers. + * + * @param[in] secure: handle to the secure buffer + * @param[in] offset: offset in the secure buffer + * @param[out] out_addr: output secure address + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL or + * invalid + */ +OEMCryptoResult WTPI_GetSecureBufferAddress(void* secure, size_t offset, + uint8_t** out_addr); + +/// @} + +#ifdef __cplusplus +} +#endif + +#endif /* OEMCRYPTO_TA_SECURE_BUFFER_ACCESS_H_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/cose_util.c b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/cose_util.c new file mode 100644 index 0000000..2b84ebb --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/cose_util.c @@ -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; +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/cose_util.h b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/cose_util.h new file mode 100644 index 0000000..3b16323 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/cose_util.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/crypto_util.c b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/crypto_util.c new file mode 100644 index 0000000..fd4b67e --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/crypto_util.c @@ -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 + +#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; +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/crypto_util.h b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/crypto_util.h new file mode 100644 index 0000000..5466a46 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/crypto_util.h @@ -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 +#include +#include + +#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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/ecc_util.c b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/ecc_util.c new file mode 100644 index 0000000..b446c6d --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/ecc_util.c @@ -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 + +#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; +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/ecc_util.h b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/ecc_util.h new file mode 100644 index 0000000..ddc5481 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/ecc_util.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/key_mapping_interface.h b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/key_mapping_interface.h new file mode 100644 index 0000000..2daf9fb --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/key_mapping_interface.h @@ -0,0 +1,91 @@ +/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary + source code may only be used and distributed under the Widevine + License Agreement. */ + +#ifndef OEMCRYPTO_TA_KEY_MAPPING_INTERFACE_H_ +#define OEMCRYPTO_TA_KEY_MAPPING_INTERFACE_H_ + +#include "wtpi_crypto_and_key_management_interface_layer1.h" +#include "wtpi_crypto_and_key_management_interface_layer2.h" + +#include +#include +#include + +#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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/renewal_util.c b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/renewal_util.c new file mode 100644 index 0000000..ceb30f0 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/renewal_util.c @@ -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 +#include +#include + +#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); +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/renewal_util.h b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/renewal_util.h new file mode 100644 index 0000000..86dab77 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/renewal_util.h @@ -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 +#include + +#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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/rsa_util.c b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/rsa_util.c new file mode 100644 index 0000000..e428d2d --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/rsa_util.c @@ -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; +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/rsa_util.h b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/rsa_util.h new file mode 100644 index 0000000..654ab4f --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/rsa_util.h @@ -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 +#include + +#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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_abort.c b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_abort.c new file mode 100644 index 0000000..6881016 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_abort.c @@ -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 + +void WTPI_Abort(void) { abort(); } diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_clock_and_gn_layer1.c b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_clock_and_gn_layer1.c new file mode 100644 index 0000000..4169d2c --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_clock_and_gn_layer1.c @@ -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 +#include +#include + +#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; +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_config_macros.h b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_config_macros.h new file mode 100644 index 0000000..343a779 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_config_macros.h @@ -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_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_crc32.c b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_crc32.c new file mode 100644 index 0000000..1f3d1d8 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_crc32.c @@ -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; +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_crypto_and_key_management_layer1_hw.c b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_crypto_and_key_management_layer1_hw.c new file mode 100644 index 0000000..b141e12 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_crypto_and_key_management_layer1_hw.c @@ -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 +#include /* THIS IS TEMPORARY UNTIL WE GET AN ALLOCATOR. */ +#include + +#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); +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_crypto_and_key_management_layer1_openssl.c b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_crypto_and_key_management_layer1_openssl.c new file mode 100644 index 0000000..3220a0a --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_crypto_and_key_management_layer1_openssl.c @@ -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 +#include /* THIS IS TEMPORARY UNTIL WE GET AN ALLOCATOR. */ +#include + +#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; +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_crypto_asymmetric.c b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_crypto_asymmetric.c new file mode 100644 index 0000000..3910709 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_crypto_asymmetric.c @@ -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 +#include +#include + +#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; +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_crypto_wrap_asymmetric.c b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_crypto_wrap_asymmetric.c new file mode 100644 index 0000000..44ca231 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_crypto_wrap_asymmetric.c @@ -0,0 +1,90 @@ +/* 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_crypto_asymmetric_interface.h" + +#include + +#include "odk_endian.h" +#include "odk_util.h" +#include "oemcrypto_check_macros.h" +#include "oemcrypto_key_types.h" +#include "oemcrypto_overflow.h" +#include "wtpi_device_key_interface.h" + +static bool IsSupportedAsymmetricKeyType(AsymmetricKeyType key_type) { + return key_type == DRM_RSA_PRIVATE_KEY || key_type == DRM_ECC_PRIVATE_KEY; +} + +OEMCryptoResult WTPI_GetWrappedAsymmetricKeySize( + size_t enc_private_key_length, UNUSED AsymmetricKeyType key_type, + size_t* buffer_size) { + return WTPI_GetEncryptAndSignSize(DEVICE_KEY_WRAP_DRM_CERT, + enc_private_key_length, buffer_size); +} + +static OEMCryptoResult LoadRSAKeyIntoAsymmetricKeyHandle( + const uint8_t* unwrapped_key, size_t unwrapped_key_length, + WTPI_AsymmetricKey_Handle* key_handle, uint32_t* allowed_schemes) { + size_t offset = 0; + if (unwrapped_key_length >= 8 && + crypto_memcmp(unwrapped_key, "SIGN", 4) == 0) { + memcpy(allowed_schemes, unwrapped_key + 4, 4); + *allowed_schemes = oemcrypto_be32toh(*allowed_schemes); + offset = 8; + } else { + *allowed_schemes = kSign_RSASSA_PSS; + } + return WTPI_CreateAsymmetricKeyHandle(unwrapped_key + offset, + unwrapped_key_length - offset, + DRM_RSA_PRIVATE_KEY, key_handle); +} + +static OEMCryptoResult LoadECCKeyInfoAsymmetricKeyHandle( + const uint8_t* unwrapped_key, size_t unwrapped_key_length, + WTPI_AsymmetricKey_Handle* key_handle, uint32_t* allowed_schemes) { + *allowed_schemes = 0; + return WTPI_CreateAsymmetricKeyHandle(unwrapped_key, unwrapped_key_length, + DRM_ECC_PRIVATE_KEY, key_handle); +} + +OEMCryptoResult WTPI_UnwrapIntoAsymmetricKeyHandle( + const uint8_t* wrapped_key, size_t wrapped_key_length, + AsymmetricKeyType key_type, WTPI_AsymmetricKey_Handle* key_handle, + uint32_t* allowed_schemes) { + if (wrapped_key == NULL || wrapped_key_length == 0 || + !IsSupportedAsymmetricKeyType(key_type)) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + RETURN_INVALID_CONTEXT_IF_NULL(key_handle); + RETURN_INVALID_CONTEXT_IF_NULL(allowed_schemes); + /* Unwrap encrypted key. */ + uint8_t unwrapped_key[PKCS8_DRM_KEY_MAX_SIZE]; + size_t unwrapped_key_length = sizeof(unwrapped_key); + const OEMCryptoResult result = WTPI_VerifyAndDecrypt( + DEVICE_KEY_WRAP_DRM_CERT, wrapped_key, wrapped_key_length, unwrapped_key, + &unwrapped_key_length); + if (result == OEMCrypto_ERROR_SHORT_BUFFER) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } else if (result != OEMCrypto_SUCCESS) { + return result; + } + /* Load unwrapped key */ + if (key_type == DRM_ECC_PRIVATE_KEY) { + return LoadECCKeyInfoAsymmetricKeyHandle( + unwrapped_key, unwrapped_key_length, key_handle, allowed_schemes); + } + return LoadRSAKeyIntoAsymmetricKeyHandle(unwrapped_key, unwrapped_key_length, + key_handle, allowed_schemes); +} + +OEMCryptoResult WTPI_WrapAsymmetricKey(uint8_t* wrapped_key, + size_t wrapped_key_length, + UNUSED AsymmetricKeyType key_type, + const uint8_t* clear_key, + size_t clear_key_length) { + return WTPI_EncryptAndSign(DEVICE_KEY_WRAP_DRM_CERT, clear_key, + clear_key_length, wrapped_key, + &wrapped_key_length); +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_decrypt_sample.c b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_decrypt_sample.c new file mode 100644 index 0000000..1ce44a9 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_decrypt_sample.c @@ -0,0 +1,271 @@ +/* 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_decrypt_sample_interface.h" + +#include +#include + +#include "odk_endian.h" +#include "oemcrypto_compiler_attributes.h" +#include "oemcrypto_key_types.h" +#include "oemcrypto_math.h" +#include "oemcrypto_output.h" +#include "oemcrypto_overflow.h" +#include "wtpi_abort_interface.h" +#include "wtpi_logging_interface.h" +#include "wtpi_secure_buffer_access_interface.h" + +// Forward declaration of AESCTRDecryptWithKeyHandle +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); + +NO_IGNORE_RESULT static OEMCryptoResult DecryptToOutputBuffer_CBC( + WTPI_K1_SymmetricKey_Handle key_handle, const uint8_t* initial_iv, + const OEMCrypto_CENCEncryptPatternDesc* pattern, const uint8_t* cipher_data, + size_t cipher_data_length, const OPK_OutputBuffer* output_buffer, + size_t output_offset) { + if (key_handle == NULL || initial_iv == NULL || pattern == NULL || + cipher_data == NULL || cipher_data_length == 0 || output_buffer == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + OEMCryptoResult result = + OPK_CheckOutputBounds(output_buffer, output_offset, cipher_data_length); + if (result != OEMCrypto_SUCCESS) { + LOGE("Output bounds check failed with result: %u", result); + return result; + } + // TODO(b/146581957): OEMCrypto v16 requires that we reject the (0, 0) + // pattern. We hope to remove this requirement in a future + // release. + if (pattern->encrypt == 0) { + LOGE("No encrypted blocks specified in pattern"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + uint8_t* dest = NULL; + if (output_buffer->type == OPK_SECURE_OUTPUT_BUFFER) { + 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; + } + const size_t pattern_length = pattern->encrypt + pattern->skip; + + uint8_t iv[AES_BLOCK_SIZE]; + uint8_t next_iv[AES_BLOCK_SIZE]; + memcpy(iv, &initial_iv[0], AES_BLOCK_SIZE); + + size_t offset = 0; + while (offset < cipher_data_length) { + const size_t next_block_size = + OPK_MinUX(cipher_data_length - offset, AES_BLOCK_SIZE); + const size_t pattern_offset = + (pattern_length > 0) ? (offset / AES_BLOCK_SIZE) % pattern_length : 0; + const bool is_skip_block = + (pattern_length > 0) && (pattern_offset >= pattern->encrypt); + if (is_skip_block || (next_block_size < AES_BLOCK_SIZE)) { + memmove(&dest[offset], &cipher_data[offset], next_block_size); + } else { + uint8_t aes_output[AES_BLOCK_SIZE]; + /* Save the iv for the next block, in case |cipher_data| is in the same + buffer as |dest|. */ + memcpy(next_iv, &cipher_data[offset], AES_BLOCK_SIZE); + KeySize key_size; + result = WTPI_K1_GetKeySize(key_handle, &key_size); + if (result != OEMCrypto_SUCCESS) return result; + result = WTPI_C1_AESCBCDecrypt(key_handle, (size_t)key_size, + &cipher_data[offset], AES_BLOCK_SIZE, iv, + aes_output); + if (result != OEMCrypto_SUCCESS) return result; + memcpy(dest + offset, aes_output, AES_BLOCK_SIZE); + memcpy(iv, next_iv, AES_BLOCK_SIZE); + } + offset += next_block_size; + } + return OEMCrypto_SUCCESS; +} + +NO_IGNORE_RESULT static OEMCryptoResult DecryptToOutputBuffer_CTR( + WTPI_K1_SymmetricKey_Handle key_handle, const uint8_t* initial_iv, + size_t block_offset, const uint8_t* cipher_data, size_t cipher_data_length, + const OPK_OutputBuffer* output_buffer, size_t output_offset) { + if (key_handle == NULL || initial_iv == NULL || + block_offset >= AES_BLOCK_SIZE || cipher_data == NULL || + cipher_data_length == 0 || output_buffer == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + OEMCryptoResult result = + OPK_CheckOutputBounds(output_buffer, output_offset, cipher_data_length); + if (result != OEMCrypto_SUCCESS) { + LOGE("Output bounds check failed with result: %u", result); + return result; + } + uint8_t* dest = NULL; + if (output_buffer->type == OPK_SECURE_OUTPUT_BUFFER) { + 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; + } + return AESCTRDecryptWithKeyHandle(key_handle, cipher_data, cipher_data_length, + initial_iv, block_offset, dest); +} + +static OEMCryptoResult DecryptSubsample( + WTPI_K1_SymmetricKey_Handle key_handle, + const OEMCrypto_SubSampleDescription* subsample, + const OEMCrypto_CENCEncryptPatternDesc* pattern, const uint8_t* cipher_data, + const uint8_t* iv, const OPK_OutputBuffer* output_buffer, + size_t output_offset, OEMCryptoCipherMode cipher_mode) { + if (key_handle == NULL || subsample == NULL || pattern == NULL || + cipher_data == NULL || iv == NULL || output_buffer == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + size_t subsample_length; + if (OPK_AddOverflowUX(subsample->num_bytes_clear, + subsample->num_bytes_encrypted, &subsample_length)) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + OEMCryptoResult result = OEMCrypto_SUCCESS; + /* Handle the clear portion of the subsample. */ + if (subsample->num_bytes_clear > 0) { + result = WTPI_C1_CopyToOutputBuffer(cipher_data, subsample->num_bytes_clear, + output_buffer, output_offset); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to copy clear data to output buffer with result: %u", + result); + return result; + } + } + /* Handle the encrypted portion of the subsample. */ + if (subsample->num_bytes_encrypted > 0) { + const uint8_t* source = cipher_data + subsample->num_bytes_clear; + const size_t source_length = subsample->num_bytes_encrypted; + size_t decrypt_offset; + if (OPK_AddOverflowUX(output_offset, subsample->num_bytes_clear, + &decrypt_offset)) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + if (cipher_mode == OEMCrypto_CipherMode_CBCS) { + if (subsample->block_offset > 0 || pattern->encrypt == 0) { + LOGE( + "Invalid block offset or pattern encrypt for CBC mode, " + "block_offset " + "= %zu, encrypt = %zu", + subsample->block_offset, pattern->encrypt); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + result = DecryptToOutputBuffer_CBC(key_handle, iv, pattern, source, + source_length, output_buffer, + decrypt_offset); + } else { + if (pattern->skip != 0 || pattern->encrypt != 0) { + LOGE( + "Invalid pattern skip or encrypt for CTR mode, skip = %zu, encrypt " + "= " + "%zu", + pattern->skip, pattern->encrypt); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + result = DecryptToOutputBuffer_CTR( + key_handle, iv, subsample->block_offset, source, source_length, + output_buffer, decrypt_offset); + } + } + return result; +} + +/* Advance an IV according to ISO-CENC's CTR modes. The lower half of the IV is + * split off and treated as an unsigned 64-bit integer, then incremented by the + * number of complete crypto blocks decrypted. The resulting value is then + * copied back into the IV over the previous lower half. */ +UBSAN_IGNORE_UNSIGNED_OVERFLOW static void AdvanceIVandCounter( + uint8_t (*subsample_iv)[AES_BLOCK_SIZE], size_t bytes) { + static const size_t kCounterIndex = KEY_IV_SIZE / 2; + static const size_t kCounterSize = KEY_IV_SIZE / 2; + uint64_t counter; + /* Defensive copy because the elements of the array may not be properly + * aligned. */ + memcpy(&counter, &(*subsample_iv)[kCounterIndex], kCounterSize); + /* The truncation here is intentional. */ + const size_t increment = bytes / AES_BLOCK_SIZE; + /* The potential overflow here is intentional. */ + counter = oemcrypto_htobe64(oemcrypto_be64toh(counter) + increment); + memcpy(&(*subsample_iv)[kCounterIndex], &counter, kCounterSize); +} + +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) { + if (key_handle == NULL || sample == NULL || pattern == NULL || + output_buffer == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + OEMCryptoResult result = OEMCrypto_SUCCESS; + + size_t starting_output_offset = output_offset; + /* Iterate through all the subsamples and decrypt each one. */ + uint8_t subsample_iv[KEY_IV_SIZE]; + ABORT_IF(sizeof(sample->iv) != KEY_IV_SIZE, + "The IV in OEMCrypto_SampleDescription has the wrong length."); + memcpy(subsample_iv, sample->iv, KEY_IV_SIZE); + size_t offset = 0; + for (size_t subsample_index = 0; subsample_index < sample->subsamples_length; + ++subsample_index) { + const OEMCrypto_SubSampleDescription* subsample = + &(sample->subsamples[subsample_index]); + size_t subsample_length; + if (OPK_AddOverflowUX(subsample->num_bytes_clear, + subsample->num_bytes_encrypted, &subsample_length)) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + size_t current_offset; + if (OPK_AddOverflowUX(starting_output_offset, offset, ¤t_offset)) { + LOGE("Output buffer overflow"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + result = + OPK_CheckOutputBounds(output_buffer, current_offset, subsample_length); + if (result != OEMCrypto_SUCCESS) { + LOGE("Output bounds check failed with result: %u", result); + return result; + } + + const uint8_t* const subsample_source = sample->buffers.input_data + offset; + result = DecryptSubsample(key_handle, subsample, pattern, subsample_source, + subsample_iv, output_buffer, current_offset, + cipher_mode); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failure %u at subsample index %zu", result, subsample_index); + return result; + } + + /* Advance the offset and (if necessary) the IV. */ + if (OPK_AddOverflowUX(offset, subsample_length, &offset)) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (subsample->num_bytes_encrypted > 0 && + cipher_mode == OEMCrypto_CipherMode_CENC) { + size_t distance; + if (OPK_AddOverflowUX(subsample->block_offset, + subsample->num_bytes_encrypted, &distance)) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + AdvanceIVandCounter(&subsample_iv, distance); + } + } /* Subsample loop */ + return result; +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_device_key.c b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_device_key.c new file mode 100644 index 0000000..137535f --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_device_key.c @@ -0,0 +1,183 @@ +/* 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_device_key_interface.h" + +#include + +#include "oemcrypto_overflow.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" + +#define UUID_LENGTH 16 + +// If changing the WrappedData struct family, make sure to update +// ENCRYPT_AND_SIGN_EXTRA in wtpi_config_macros.h if needed. + +// This struct represents a wrapped blob that we do not yet know how to +// interpret. It contains only the fields that we expect every versioned blob to +// have. +typedef struct WrappedData { + uint8_t signature[SHA256_DIGEST_LENGTH]; + uint8_t magic[UUID_LENGTH]; + uint8_t version[sizeof(uint32_t)]; + uint8_t data[]; +} WrappedData; + +// The randomly-generated UUID that identifies a blob as a WrappedData struct, +// in network byte order. +static const uint8_t kMagicUuid[UUID_LENGTH] = { + 0xb5, 0x76, 0x3b, 0xad, 0x84, 0x05, 0x40, 0xfd, + 0xa0, 0x88, 0x3b, 0x6c, 0x69, 0x97, 0xfc, 0x74}; + +// 1 in network byte order +static const uint8_t kVersionOne[sizeof(uint32_t)] = {0x00, 0x00, 0x00, 0x01}; + +// This is the layout of the |data| field of a WrappedData structure when its +// |version| field is 1. +typedef struct WrappedData_V1 { + uint8_t iv[KEY_IV_SIZE]; + uint8_t enc_data[]; +} WrappedData_V1; + +OEMCryptoResult WTPI_GetEncryptAndSignSize(UNUSED uint32_t context, + size_t in_size, + size_t* wrapped_size) { + if (OPK_AddOverflowUX(in_size, sizeof(WrappedData) + sizeof(WrappedData_V1), + wrapped_size)) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_EncryptAndSign(uint32_t context, const uint8_t* data, + size_t data_size, uint8_t* out, + size_t* out_size) { + if (!out_size) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + size_t needed_size; + OEMCryptoResult result = + WTPI_GetEncryptAndSignSize(context, data_size, &needed_size); + if (result != OEMCrypto_SUCCESS) return result; + if (*out_size < needed_size) { + *out_size = needed_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + // Allow querying size without these buffers. + if (!data || !out) return OEMCrypto_ERROR_INVALID_CONTEXT; + *out_size = needed_size; + + WrappedData* const wrapped_header = (WrappedData*)out; + memcpy(wrapped_header->magic, kMagicUuid, sizeof(wrapped_header->magic)); + memcpy(wrapped_header->version, kVersionOne, sizeof(wrapped_header->version)); + + WrappedData_V1* const wrapped_data = (WrappedData_V1*)wrapped_header->data; + /* Pick a random IV for generating keys. */ + result = WTPI_C1_RandomBytes(wrapped_data->iv, sizeof(wrapped_data->iv)); + if (result != OEMCrypto_SUCCESS) return result; + + // Encrypt the buffer. + WTPI_K1_SymmetricKey_Handle encryption_key = NULL; + result = WTPI_K1_DeriveDeviceKeyIntoHandle(context, ENCRYPTION_KEY, + &encryption_key, KEY_SIZE_128); + if (result != OEMCrypto_SUCCESS) return result; + result = WTPI_C1_AESCBCEncrypt(encryption_key, data, data_size, + wrapped_data->iv, wrapped_data->enc_data); + WTPI_K1_FreeKeyHandle(encryption_key); + 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_K1_SymmetricKey_Handle signing_key = NULL; + result = WTPI_K1_DeriveDeviceKeyIntoHandle(context, MAC_KEY_CLIENT, + &signing_key, KEY_SIZE_256); + if (result != OEMCrypto_SUCCESS) return result; + const size_t offset = sizeof(wrapped_header->signature); + result = WTPI_C1_HMAC_SHA256(signing_key, out + offset, needed_size - offset, + wrapped_header->signature); + WTPI_K1_FreeKeyHandle(signing_key); + return result; +} + +static OEMCryptoResult VerifyAndDecrypt_V1(uint32_t context, + const uint8_t* data, + size_t data_size, uint8_t* out, + size_t* out_size) { + // We explicitly defer checking |out| until later, to allow querying the size + // without an out buffer. + if (data == NULL || + data_size < sizeof(WrappedData) + sizeof(WrappedData_V1) || + out_size == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + const WrappedData* const wrapped_header = (const WrappedData*)data; + + // Verify the signature first, before interpreting. + WTPI_K1_SymmetricKey_Handle signing_key = NULL; + OEMCryptoResult result = WTPI_K1_DeriveDeviceKeyIntoHandle( + context, MAC_KEY_SERVER, &signing_key, KEY_SIZE_256); + if (result != OEMCrypto_SUCCESS) return result; + const size_t offset = sizeof(wrapped_header->signature); + result = + WTPI_C1_HMAC_SHA256_Verify(signing_key, data + offset, data_size - offset, + wrapped_header->signature); + WTPI_K1_FreeKeyHandle(signing_key); + if (result != OEMCrypto_SUCCESS) return result; + + // If someday we support multiple versions, we'll need to decode + // wrapped_header->version and switch our behavior depending on the version + // we find. But for now, since only version 1 is valid, we can just verify + // that wrapped_header->version is 1. + if (memcmp(wrapped_header->magic, kMagicUuid, + sizeof(wrapped_header->magic)) != 0 || + memcmp(wrapped_header->version, kVersionOne, + sizeof(wrapped_header->version)) != 0) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + const size_t needed_size = + data_size - (sizeof(WrappedData) + sizeof(WrappedData_V1)); + if (*out_size < needed_size) { + *out_size = needed_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + // We defer this check to allow querying the size without an out buffer. + if (out == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT; + *out_size = needed_size; + + const WrappedData_V1* const wrapped_data = + (const WrappedData_V1*)wrapped_header->data; + + // Decrypt the buffer. + WTPI_K1_SymmetricKey_Handle encryption_key = NULL; + result = WTPI_K1_DeriveDeviceKeyIntoHandle(context, ENCRYPTION_KEY, + &encryption_key, KEY_SIZE_128); + if (result != OEMCrypto_SUCCESS) return result; + KeySize key_size; + result = WTPI_K1_GetKeySize(encryption_key, &key_size); + if (result != OEMCrypto_SUCCESS) return result; + result = WTPI_C1_AESCBCDecrypt(encryption_key, (size_t)key_size, + wrapped_data->enc_data, needed_size, + wrapped_data->iv, out); + WTPI_K1_FreeKeyHandle(encryption_key); + return result; +} + +OEMCryptoResult WTPI_VerifyAndDecrypt(uint32_t context, const uint8_t* data, + size_t data_size, uint8_t* out, + size_t* out_size) { + // This function is broken out separately in order to ease maintenance for + // those who also need to maintain backwards-compatibility with other, older + // wrapping formats. + return VerifyAndDecrypt_V1(context, data, data_size, out, out_size); +} + +OEMCryptoResult WTPI_VerifyAndDecryptUsageData_Legacy(const uint8_t* wrapped, + size_t wrapped_size, + const uint8_t* signature, + const uint8_t* iv, + uint8_t* out) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_device_renewal_layer1.c b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_device_renewal_layer1.c new file mode 100644 index 0000000..cb1b1c2 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_device_renewal_layer1.c @@ -0,0 +1,56 @@ +/* Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary + source code may only be used and distributed under the Widevine + License Agreement. */ + +/** + * On some devices, accessing the renewal info is expensive or should otherwise + * only be done once per initialization of OEMCrypto. For these devices, this + * reference implementation of Device Renewal Layer 1 is provided, which calls + * the Device Renewal Layer 2 component. This reference implementation caches + * the results of calling the Layer 2 API and only calls it once per + * intialization. + */ + +#include "wtpi_device_renewal_interface_layer1.h" + +#include +#include +#include + +#include "oemcrypto_check_macros.h" +#include "oemcrypto_key_types.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" +#include "wtpi_device_renewal_interface_layer2.h" + +static bool perform_renewal; +static uint32_t renewal_system_id; +static uint8_t renewal_key_bytes[KEY_SIZE_128]; + +OEMCryptoResult WTPI_InitializeDeviceRenewal(uint32_t old_system_id) { + OEMCryptoResult result = + WTPI_GetRenewalInfo(old_system_id, &renewal_system_id, renewal_key_bytes, + sizeof(renewal_key_bytes)); + perform_renewal = (result == OEMCrypto_SUCCESS); + return result; +} + +OEMCryptoResult WTPI_TerminateDeviceRenewal(void) { + perform_renewal = false; + renewal_system_id = 0; + memset(renewal_key_bytes, 0, sizeof(renewal_key_bytes)); + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_GetRenewalSystemId(uint32_t* new_system_id) { + ABORT_IF_NULL(new_system_id); + if (!perform_renewal) return OEMCrypto_ERROR_NOT_IMPLEMENTED; + *new_system_id = renewal_system_id; + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_LoadRenewalKey(WTPI_K1_SymmetricKey_Handle* renewal_key) { + ABORT_IF_NULL(renewal_key); + if (!perform_renewal) return OEMCrypto_ERROR_NOT_IMPLEMENTED; + return WTPI_K1_CreateKeyHandle(renewal_key_bytes, sizeof(renewal_key_bytes), + DERIVING_KEY, renewal_key); +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_device_renewal_layer2.c b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_device_renewal_layer2.c new file mode 100644 index 0000000..829c0dd --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_device_renewal_layer2.c @@ -0,0 +1,19 @@ +/* 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 "wtpi_device_renewal_interface_layer2.h" + +#include "oemcrypto_check_macros.h" +#include "oemcrypto_compiler_attributes.h" +#include "oemcrypto_key_types.h" + +OEMCryptoResult WTPI_GetRenewalInfo(uint32_t old_system_id UNUSED, + uint32_t* new_system_id UNUSED, + uint8_t* renewal_key UNUSED, + size_t renewal_key_length UNUSED) { + ABORT_IF_NULL(new_system_id); + ABORT_IF_NULL(renewal_key); + ABORT_IF(renewal_key_length != KEY_SIZE_128, "Unexpected renewal key size"); + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_idle.c b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_idle.c new file mode 100644 index 0000000..5e57bcf --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_idle.c @@ -0,0 +1,12 @@ +/* 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 "wtpi_idle_interface.h" + +OEMCryptoResult WTPI_Idle(OEMCrypto_IdleState state, + uint32_t os_specific_code) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult WTPI_Wake(void) { return OEMCrypto_ERROR_NOT_IMPLEMENTED; } diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_logging.c b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_logging.c new file mode 100644 index 0000000..d4c3ac6 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_logging.c @@ -0,0 +1,60 @@ +/* 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_logging_interface.h" + +#include +#include +#include +#include +#include + +#include "wtpi_abort_interface.h" + +#if !defined(OPK_LOG_LEVEL) +# define OPK_LOG_LEVEL LOG_DEBUG +#endif + +static size_t LogPriorityToOrdering(LogPriority priority) { + switch (priority) { + case LOG_NONE: + return 0; + case LOG_ERROR: + return 1; + case LOG_DEBUG: + return 2; + } + ABORT("invalid log priority"); +} + +static const char* LogPriorityToName(LogPriority priority) { + static const char* const kPriorityNames[] = {"NONE", "ERROR", "DEBUG"}; + return kPriorityNames[LogPriorityToOrdering(priority)]; +} + +/* A test implementation for logging. Outputs logs to stdout. */ +void WTPI_Log(const char* file, const char* function, int line, + LogPriority level, const char* fmt, ...) { + if (level == LOG_NONE) { + fprintf(stderr, "[FATAL:%s(%d)] Cannot log at LOG_NONE level.\n", file, + line); + fflush(stderr); + return; + } + if (LogPriorityToOrdering(level) > LogPriorityToOrdering(OPK_LOG_LEVEL)) { + // Log message is below the threshold for logging. + return; + } + + fprintf(stderr, "[%s:%s(%d):%s] ", LogPriorityToName(level), file, line, + function); + + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + fputc('\n', stderr); + fflush(stderr); +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_reference.gyp b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_reference.gyp new file mode 100644 index 0000000..f407e4e --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_reference.gyp @@ -0,0 +1,140 @@ +# 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%': '.', + # TODO(b/207176111): add test scripts to cover both reference crypto impl + 'reference_crypto_impl%': 'software', + 'test_renewal%': 0, + }, + 'includes': [ + '../../strict_compiler_flags.gypi', + ], + 'target_defaults': { + 'type': 'static_library', + 'standalone_static_library': 1, + 'dependencies': [ + '../oemcrypto_ta.gyp:oemcrypto_ta', + ], + }, + 'targets': [ + { + 'target_name': 'oemcrypto_ta_reference_root_of_trust', + 'sources': [ + 'renewal_util.c', + 'wtpi_crypto_wrap_asymmetric.c', + 'wtpi_device_key.c', + 'wtpi_root_of_trust_layer1.c', + ], + 'include_dirs': [ + '.', + ], + 'dependencies': [ + '../../../odk/src/odk.gyp:odk', + 'oemcrypto_ta_reference_renewal', + ], + }, + { + 'target_name': 'oemcrypto_ta_reference_renewal', + 'type': 'static_library', + 'sources': [ + 'wtpi_device_renewal_layer1.c', + ], + 'conditions': [ + ['not test_renewal', { + 'sources': [ + 'wtpi_device_renewal_layer2.c', + ], + }], + ], + 'dependencies': [ + '../../../odk/src/odk.gyp:odk', + ], + }, + { + 'target_name': 'oemcrypto_ta_reference_clock', + 'sources': [ + 'wtpi_clock_and_gn_layer1.c', + ], + 'dependencies': [ + '../../../odk/src/odk.gyp:odk', + ], + }, + { + 'target_name': 'oemcrypto_ta_reference_abort', + 'sources': [ + 'wtpi_abort.c', + ], + }, + { + 'target_name': 'oemcrypto_ta_reference_logging', + 'sources': [ + 'wtpi_logging.c', + ], + }, + { + 'target_name': 'oemcrypto_ta_reference_idle', + 'sources': [ + 'wtpi_idle.c', + ], + }, + { + 'target_name': 'oemcrypto_ta_reference_crypto', + 'include_dirs': [ + '<(config_macros_header_dir)', + '.', + ], + # The reference implementation of the crypto interface uses + # BoringSSL/OpenSSL, which requires C11. These flags effectively do the + # opposite of the default flags, filtering out the C99 flag and + # un-filtering-out the C11 flag. + 'cflags_c': [ + '-std=c11', + ], + 'cflags_c/': [ + ['exclude', '-std=*'], + ['include', '-std=c11'], + ], + 'sources': [ + 'crypto_util.c', + 'cose_util.c', + 'ecc_util.c', + 'rsa_util.c', + 'wtpi_crc32.c', + 'wtpi_crypto_asymmetric.c', + 'wtpi_decrypt_sample.c', + ], + 'conditions': [ + ['reference_crypto_impl=="hardware"', { + 'sources': [ + 'wtpi_crypto_and_key_management_layer1_hw.c', + ], + }], + ['reference_crypto_impl=="software"', { + 'sources': [ + 'wtpi_crypto_and_key_management_layer1_openssl.c', + ], + }], + ], + 'variables': { + # Needed for BoringSSL dependency build files. These SHOULD already be + # defined by a higher-level configuration, but sometimes the OPK TA + # gets included in targets that don't define them, so we define them + # again here defensively. + + 'privacy_crypto_impl%': 'boringssl', + 'boringssl_libcrypto_path%': '<(DEPTH)/third_party/boringssl/boringssl.gyp:crypto', + }, + 'includes': [ + '../../../../util/libcrypto_dependency.gypi', + ], + 'dependencies': [ + '../../../odk/src/odk.gyp:odk', + '../../../../third_party/open-dice.gyp:cbor', + ], + }, + ], +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_root_of_trust_layer1.c b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_root_of_trust_layer1.c new file mode 100644 index 0000000..689d32e --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_root_of_trust_layer1.c @@ -0,0 +1,235 @@ +/* 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_root_of_trust_interface_layer1.h" +#include "wtpi_root_of_trust_interface_layer2.h" + +#include +#include +#include +#include + +#include "odk_endian.h" +#include "oemcrypto_key_types.h" +#include "renewal_util.h" +#include "wtpi_abort_interface.h" +#include "wtpi_config_interface.h" +#include "wtpi_crc32_interface.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" +#include "wtpi_device_renewal_interface_layer1.h" +#include "wtpi_logging_interface.h" + +// This layer keeps a copy of the keybox in volatile memory. A more secure +// implementation of this would only keep the device key in memory while it is +// in use, or keep it obscured. +static WidevineKeybox gKeybox; // The current keybox. + +// If the device has been renewed, we need to perform different steps in certain +// functions and need to store a renewed block of key data. +static bool perform_renewal; +static uint8_t renewed_key_data[sizeof(gKeybox.data)]; + +static uint32_t GetSystemId(const uint8_t* key_data) { + uint32_t nbo_system_id; + memcpy(&nbo_system_id, &key_data[4], sizeof(nbo_system_id)); + return oemcrypto_be32toh(nbo_system_id); +} + +static OEMCryptoResult CalculateRenewedKeyData(void) { + // Keybox renewal is split into two phases. We renew the key data here and + // store the renewed data in renewed_key_data. However, deriving the renewed + // key must be done every time the TA requests that it be loaded into a key + // handle, and so that step is deferred until + // WTPI_CreateKeyHandleFromKeybox(). + + uint32_t new_system_id; + OEMCryptoResult result = WTPI_GetRenewalSystemId(&new_system_id); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get renewal system ID with %u", result); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + // The new key data is the same as the old key data, but with the version + // field at offset 0 and the system ID field at offset 4 modified. + memcpy(renewed_key_data, gKeybox.data, sizeof(renewed_key_data)); + static const uint8_t kVersion3[4] = {0x00, 0x00, 0x00, 0x03}; + memcpy(&renewed_key_data[0], &kVersion3, sizeof(kVersion3)); + const uint32_t nbo_system_id = oemcrypto_htobe32(new_system_id); + memcpy(&renewed_key_data[4], &nbo_system_id, sizeof(nbo_system_id)); + return OEMCrypto_SUCCESS; +} + +static OEMCryptoResult InitializeRenewal(void) { + OEMCryptoResult result = + WTPI_InitializeDeviceRenewal(GetSystemId(gKeybox.data)); + if (result == OEMCrypto_SUCCESS) { + // Success indicates that renewal is needed. + perform_renewal = true; + result = CalculateRenewedKeyData(); + } else if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED) { + // This is a benign error indicating renewal is not necessary. + perform_renewal = false; + result = OEMCrypto_SUCCESS; + } + return result; +} + +OEMCryptoResult WTPI_InitializeKeybox(void) { + memset(&gKeybox, 0, sizeof(gKeybox)); + size_t length = sizeof(gKeybox); + OEMCryptoResult result = WTPI_LoadRootOfTrust((uint8_t*)&gKeybox, &length); + if (result != OEMCrypto_SUCCESS) { + LOGD("Trouble loading the keybox: %u", result); + goto cleanup; + } + if (length == 0) { + LOGD("No keybox file loaded."); + result = OEMCrypto_ERROR_NO_DEVICE_KEY; + goto cleanup; + } + if (length != sizeof(gKeybox)) { + LOGD("That's odd. Keybox was wrong size: %zu != %zu", length, + sizeof(gKeybox)); + result = OEMCrypto_ERROR_KEYBOX_INVALID; + goto cleanup; + } + result = InitializeRenewal(); +cleanup: + if (result != OEMCrypto_SUCCESS) { + memset(&gKeybox, 0, sizeof(gKeybox)); + perform_renewal = false; + memset(&renewed_key_data, 0, sizeof(renewed_key_data)); + } + return result; +} + +OEMCryptoResult WTPI_TerminateKeybox(void) { + memset(&gKeybox, 0, sizeof(gKeybox)); + perform_renewal = false; + memset(&renewed_key_data, 0, sizeof(renewed_key_data)); + return WTPI_TerminateDeviceRenewal(); +} + +OEMCryptoResult WTPI_UnwrapValidateAndInstallKeybox(const uint8_t* input, + size_t length) { + size_t output_length = sizeof(gKeybox); + OEMCryptoResult result; + if (length < output_length) { + result = OEMCrypto_ERROR_SHORT_BUFFER; + goto cleanup; + } + // First, try the buffer as if is a clear keybox. + memcpy(&gKeybox, input, output_length); + result = WTPI_ValidateKeybox(); + if (result != OEMCrypto_SUCCESS) { + // If that didn't work, we unwrap the encrypted data that was in input, and + // store it in gKeybox. + result = WTPI_UnwrapRootOfTrust(input, length, (uint8_t*)&gKeybox, + &output_length); + if (result != OEMCrypto_SUCCESS) goto cleanup; + // Keyboxes have a fixed size. This might not be true in the future if we + // use a certificate. + if (output_length != sizeof(gKeybox)) { + result = OEMCrypto_ERROR_SHORT_BUFFER; + goto cleanup; + } + if (result != OEMCrypto_SUCCESS) goto cleanup; + // Use the layer above to validate the keybox. + result = WTPI_ValidateKeybox(); + if (result != OEMCrypto_SUCCESS) goto cleanup; + } + // Since the keybox changed, the renewal info may have changed as well. We + // shut down the renewal component and reinitialize it to pick up any changes + // to gKeybox. + perform_renewal = false; + WTPI_TerminateDeviceRenewal(); + result = InitializeRenewal(); + if (result != OEMCrypto_SUCCESS) goto cleanup; + // If the keybox was valid, then we ask the layer below us to store the keybox + // into persistent memory. + result = WTPI_SaveRootOfTrust((uint8_t*)&gKeybox, sizeof(gKeybox)); +cleanup: + if (result != OEMCrypto_SUCCESS) { + WTPI_TerminateKeybox(); + } + return result; +} + +OEMCryptoResult WTPI_ValidateKeybox(void) { + if (memcmp(gKeybox.magic, "kbox", 4) != 0) { + return OEMCrypto_ERROR_BAD_MAGIC; + } + uint32_t crc_computed; + uint32_t crc_stored; + memcpy(&crc_stored, gKeybox.crc, sizeof(uint32_t)); + crc_stored = oemcrypto_be32toh(crc_stored); + OEMCryptoResult result = WTPI_Crc32Init(&crc_computed); + if (OEMCrypto_SUCCESS != result) return result; + // Compute the CRC-32 of everything but the crc itself. + result = + WTPI_Crc32Cont((uint8_t*)&gKeybox, sizeof(gKeybox) - sizeof(uint32_t), + crc_computed, &crc_computed); + if (result != OEMCrypto_SUCCESS) return result; + if (crc_computed != crc_stored) { + LOGD("CRC Computed = %08x, stored = %08x", crc_computed, crc_stored); + return OEMCrypto_ERROR_BAD_CRC; + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_GetDeviceIDFromKeybox(uint8_t* device_id, + size_t device_id_length) { + if (device_id == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT; + if (device_id_length < KEYBOX_DEVICE_ID_SIZE) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + memcpy(device_id, gKeybox.device_id, KEYBOX_DEVICE_ID_SIZE); + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_GetKeyDataFromKeybox(uint8_t* key_data, size_t length) { + if (key_data == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT; + if (length != sizeof(gKeybox.data)) return OEMCrypto_ERROR_INVALID_CONTEXT; + if (perform_renewal) { + memcpy(key_data, renewed_key_data, length); + } else { + memcpy(key_data, gKeybox.data, length); + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_LoadTestKeybox(const uint8_t* test_keybox, size_t length) { + if (test_keybox == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT; + if (length != sizeof(gKeybox)) { + LOGD("Wrong keybox size: %zu", length); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + memcpy(&gKeybox, test_keybox, length); + // Renewal is not used with the test keybox. + perform_renewal = false; + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_K1_CreateKeyHandleFromKeybox( + WTPI_K1_SymmetricKey_Handle* out) { + static const size_t kKeyboxKeySize = sizeof(gKeybox.device_key); + ABORT_IF(kKeyboxKeySize != KEY_SIZE_128, "Keybox key is an impossible size."); + if (perform_renewal) { + WTPI_K1_SymmetricKey_Handle renewal_key = NULL; + OEMCryptoResult result = WTPI_LoadRenewalKey(&renewal_key); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to load renewal key with %u", result); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + result = OPKI_DeriveRenewedKeyboxKey( + renewal_key, gKeybox.device_key, kKeyboxKeySize, renewed_key_data, + sizeof(renewed_key_data), kKeyboxKeySize, DERIVING_KEY, out); + WTPI_K1_FreeKeyHandle(renewal_key); + return result; + } else { + return WTPI_K1_CreateKeyHandle(gKeybox.device_key, kKeyboxKeySize, + DERIVING_KEY, out); + } +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/clock_interface_test.cpp b/oemcrypto/opk/oemcrypto_ta/wtpi_test/clock_interface_test.cpp new file mode 100644 index 0000000..5847d78 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/clock_interface_test.cpp @@ -0,0 +1,112 @@ +// 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 +#include +#include + +#include "OEMCryptoCENC.h" +#include "log.h" +#include "opk_init.h" +#include "wtpi_clock_interface_layer1.h" + +namespace { + +constexpr uint32_t kSecondsElapsed = 5; +constexpr uint32_t kSecondsTolerance = 1; + +// temporary file to store the trusted time before reboot +const char* kSavedTrustedTime = "saved_trusted_time"; + +template +std::ostream& binary_write(std::ostream& stream, const T& value) { + return stream.write(reinterpret_cast(&value), sizeof(T)); +} + +template +std::istream& binary_read(std::istream& stream, T& value) { + return stream.read(reinterpret_cast(&value), sizeof(T)); +} + +void SaveTrustedTime(uint64_t time) { + std::ofstream stream(kSavedTrustedTime, std::ios::binary); + binary_write(stream, time); +} + +uint64_t LoadTrustedTime() { + uint64_t time = ULLONG_MAX; + std::ifstream stream(kSavedTrustedTime, std::ios::binary); + binary_read(stream, time); + return time; +} + +bool IsTrustedTimeSaved() { + std::ifstream stream(kSavedTrustedTime); + return stream.good(); +} + +}; // namespace + +class ClockInterfaceTest : public ::testing::Test { + protected: + ClockInterfaceTest() {} + + void SetUp() override { + ::testing::Test::SetUp(); + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + LOGD("Running test %s.%s", test_info->test_case_name(), test_info->name()); + ASSERT_TRUE(OPK_Initialize()); + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_InitializeClock()); + } + + void TearDown() override { + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_TerminateClock()); + OPK_Terminate(); + ::testing::Test::TearDown(); + } +}; + +TEST_F(ClockInterfaceTest, TrustedTimeNULL) { + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, WTPI_GetTrustedTime(NULL)); +} + +TEST_F(ClockInterfaceTest, ClockBasic) { + uint64_t time_in_s; + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_GetTrustedTime(&time_in_s)); + printf("Sleep %us before validating the trusted time...\n", + kSecondsElapsed + kSecondsTolerance); + sleep(kSecondsElapsed + kSecondsTolerance); + uint64_t new_time_in_s; + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_GetTrustedTime(&new_time_in_s)); + ASSERT_TRUE(new_time_in_s > time_in_s + kSecondsElapsed); +} + +class ClockRebootTest : public ClockInterfaceTest {}; + +// The reboot tests verify that the trusted time returned by +// WTPI_GetTrustedTime() never goes backward after a device reboot +TEST_F(ClockRebootTest, SaveClockBeforeReboot) { + printf("Sleep %us before saving the trusted time...\n", kSecondsElapsed); + sleep(kSecondsElapsed); + // This is to simulate that the device is up for at least kSecondsElapsed + // before reboot. The test CheckClockAfterReboot below will validate the + // trusted time immediately after reboot. If WTPI_GetTrustedTime() is + // implemented on top of a timer which gets reset to 0 after every reboot, the + // trusted time could potentially go backward compared with the last recorded + // trusted time before reboot in a problematic implementation. This test is to + // make sure the trusted stays monotonic. + uint64_t current_time; + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_GetTrustedTime(¤t_time)); + SaveTrustedTime(current_time); +} + +TEST_F(ClockRebootTest, CheckClockAfterReboot) { + ASSERT_EQ(true, IsTrustedTimeSaved()); + uint64_t current_time; + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_GetTrustedTime(¤t_time)); + uint64_t saved_time = LoadTrustedTime(); + ASSERT_TRUE(current_time >= saved_time); + remove(kSavedTrustedTime); +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/common/GEN_common_serializer.c b/oemcrypto/opk/oemcrypto_ta/wtpi_test/common/GEN_common_serializer.c new file mode 100644 index 0000000..1913e81 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/common/GEN_common_serializer.c @@ -0,0 +1,440 @@ +/* + * Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine License + * Agreement. + */ + +/* + * This code is auto-generated, do not edit + */ +#include "GEN_common_serializer.h" + +#include + +#include "GEN_common_serializer.h" +#include "bump_allocator.h" +#include "common_special_cases.h" +#include "log_macros.h" +#include "marshaller_base.h" +#include "odk_overflow.h" +#include "opk_serialization_base.h" +#include "shared_buffer_allocator.h" +#include "tos_transport_interface.h" +#include "wtpi_crc32_interface.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" +#include "wtpi_crypto_asymmetric_interface.h" + +bool SuccessResult(OEMCryptoResult result) { + switch (result) { + case OEMCrypto_SUCCESS: + case OEMCrypto_WARNING_GENERATION_SKEW: + case OEMCrypto_LOCAL_DISPLAY_ONLY: + return true; + default: + return false; + } +} +bool Is_Valid_OEMCryptoResult(uint32_t value) { + switch (value) { + case 0: /* OEMCrypto_SUCCESS */ + case 1: /* OEMCrypto_ERROR_INIT_FAILED */ + case 2: /* OEMCrypto_ERROR_TERMINATE_FAILED */ + case 3: /* OEMCrypto_ERROR_OPEN_FAILURE */ + case 4: /* OEMCrypto_ERROR_CLOSE_FAILURE */ + case 5: /* OEMCrypto_ERROR_ENTER_SECURE_PLAYBACK_FAILED */ + case 6: /* OEMCrypto_ERROR_EXIT_SECURE_PLAYBACK_FAILED */ + case 7: /* OEMCrypto_ERROR_SHORT_BUFFER */ + case 8: /* OEMCrypto_ERROR_NO_DEVICE_KEY */ + case 9: /* OEMCrypto_ERROR_NO_ASSET_KEY */ + case 10: /* OEMCrypto_ERROR_KEYBOX_INVALID */ + case 11: /* OEMCrypto_ERROR_NO_KEYDATA */ + case 12: /* OEMCrypto_ERROR_NO_CW */ + case 13: /* OEMCrypto_ERROR_DECRYPT_FAILED */ + case 14: /* OEMCrypto_ERROR_WRITE_KEYBOX */ + case 15: /* OEMCrypto_ERROR_WRAP_KEYBOX */ + case 16: /* OEMCrypto_ERROR_BAD_MAGIC */ + case 17: /* OEMCrypto_ERROR_BAD_CRC */ + case 18: /* OEMCrypto_ERROR_NO_DEVICEID */ + case 19: /* OEMCrypto_ERROR_RNG_FAILED */ + case 20: /* OEMCrypto_ERROR_RNG_NOT_SUPPORTED */ + case 21: /* OEMCrypto_ERROR_SETUP */ + case 22: /* OEMCrypto_ERROR_OPEN_SESSION_FAILED */ + case 23: /* OEMCrypto_ERROR_CLOSE_SESSION_FAILED */ + case 24: /* OEMCrypto_ERROR_INVALID_SESSION */ + case 25: /* OEMCrypto_ERROR_NOT_IMPLEMENTED */ + case 26: /* OEMCrypto_ERROR_NO_CONTENT_KEY */ + case 27: /* OEMCrypto_ERROR_CONTROL_INVALID */ + case 28: /* OEMCrypto_ERROR_UNKNOWN_FAILURE */ + case 29: /* OEMCrypto_ERROR_INVALID_CONTEXT */ + case 30: /* OEMCrypto_ERROR_SIGNATURE_FAILURE */ + case 31: /* OEMCrypto_ERROR_TOO_MANY_SESSIONS */ + case 32: /* OEMCrypto_ERROR_INVALID_NONCE */ + case 33: /* OEMCrypto_ERROR_TOO_MANY_KEYS */ + case 34: /* OEMCrypto_ERROR_DEVICE_NOT_RSA_PROVISIONED */ + case 35: /* OEMCrypto_ERROR_INVALID_RSA_KEY */ + case 36: /* OEMCrypto_ERROR_KEY_EXPIRED */ + case 37: /* OEMCrypto_ERROR_INSUFFICIENT_RESOURCES */ + case 38: /* OEMCrypto_ERROR_INSUFFICIENT_HDCP */ + case 39: /* OEMCrypto_ERROR_BUFFER_TOO_LARGE */ + case 40: /* OEMCrypto_WARNING_GENERATION_SKEW */ + case 41: /* OEMCrypto_ERROR_GENERATION_SKEW */ + case 42: /* OEMCrypto_LOCAL_DISPLAY_ONLY */ + case 43: /* OEMCrypto_ERROR_ANALOG_OUTPUT */ + case 44: /* OEMCrypto_ERROR_WRONG_PST */ + case 45: /* OEMCrypto_ERROR_WRONG_KEYS */ + case 46: /* OEMCrypto_ERROR_MISSING_MASTER */ + case 47: /* OEMCrypto_ERROR_LICENSE_INACTIVE */ + case 48: /* OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE */ + case 49: /* OEMCrypto_ERROR_ENTRY_IN_USE */ + case 50: /* OEMCrypto_ERROR_USAGE_TABLE_UNRECOVERABLE */ + case 51: /* OEMCrypto_KEY_NOT_LOADED */ + case 52: /* OEMCrypto_KEY_NOT_ENTITLED */ + case 53: /* OEMCrypto_ERROR_BAD_HASH */ + case 54: /* OEMCrypto_ERROR_OUTPUT_TOO_LARGE */ + case 55: /* OEMCrypto_ERROR_SESSION_LOST_STATE */ + case 56: /* OEMCrypto_ERROR_SYSTEM_INVALIDATED */ + case 57: /* OEMCrypto_ERROR_LICENSE_RELOAD */ + case 58: /* OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES */ + case 59: /* OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION */ + case 60: /* OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION */ + case 61: /* OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING */ + case 62: /* OEMCrypto_ERROR_UNSUPPORTED_CIPHER */ + case 63: /* OEMCrypto_ERROR_DVR_FORBIDDEN */ + case 64: /* OEMCrypto_ERROR_INSUFFICIENT_PRIVILEGE */ + case 65: /* OEMCrypto_ERROR_INVALID_KEY */ + case 1000: /* ODK_ERROR_BASE */ + case 1001: /* ODK_SET_TIMER */ + case 1002: /* ODK_DISABLE_TIMER */ + case 1003: /* ODK_TIMER_EXPIRED */ + case 1004: /* ODK_UNSUPPORTED_API */ + case 1005: /* ODK_STALE_RENEWAL */ + case 2000: /* OPK_ERROR_BASE */ + case 2001: /* OPK_ERROR_INCOMPATIBLE_VERSION */ + case 2002: /* OPK_ERROR_NO_PERSISTENT_DATA */ + return true; + default: + return false; + } +} + +bool Is_Valid_OEMCrypto_Usage_Entry_Status(uint32_t value) { + switch (value) { + case 0: /* kUnused */ + case 1: /* kActive */ + case 2: /* kInactive */ + case 3: /* kInactiveUsed */ + case 4: /* kInactiveUnused */ + return true; + default: + return false; + } +} + +bool Is_Valid_OEMCrypto_LicenseType(uint32_t value) { + switch (value) { + case 0: /* OEMCrypto_ContentLicense */ + case 1: /* OEMCrypto_EntitlementLicense */ + return true; + default: + return false; + } +} + +bool Is_Valid_OEMCrypto_PrivateKeyType(uint32_t value) { + switch (value) { + case 0: /* OEMCrypto_RSA_Private_Key */ + case 1: /* OEMCrypto_ECC_Private_Key */ + return true; + default: + return false; + } +} + +bool Is_Valid_OPK_OutputBuffer_Type(uint32_t value) { + switch (value) { + case 2011664603: /* OPK_CLEAR_INSECURE_OUTPUT_BUFFER */ + case 2134605769: /* OPK_SECURE_OUTPUT_BUFFER */ + return true; + default: + return false; + } +} + +void OPK_Pack_OEMCrypto_Substring(ODK_Message* msg, + OEMCrypto_Substring const* obj) { + OPK_Pack_size_t(msg, (const size_t*)&obj->offset); + OPK_Pack_size_t(msg, (const size_t*)&obj->length); +} + +void OPK_Pack_OEMCrypto_DTCP2_CMI_Descriptor_0( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_0 const* obj) { + OPK_Pack_uint8_t(msg, (const uint8_t*)&obj->id); + OPK_Pack_uint8_t(msg, (const uint8_t*)&obj->extension); + OPK_Pack_uint16_t(msg, (const uint16_t*)&obj->length); + OPK_Pack_uint8_t(msg, (const uint8_t*)&obj->data); +} + +void OPK_Pack_OEMCrypto_DTCP2_CMI_Descriptor_1( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_1 const* obj) { + OPK_Pack_uint8_t(msg, (const uint8_t*)&obj->id); + OPK_Pack_uint8_t(msg, (const uint8_t*)&obj->extension); + OPK_Pack_uint16_t(msg, (const uint16_t*)&obj->length); + OPK_PackArray(msg, (const uint8_t*)&obj->data[0], 3); +} + +void OPK_Pack_OEMCrypto_DTCP2_CMI_Descriptor_2( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_2 const* obj) { + OPK_Pack_uint8_t(msg, (const uint8_t*)&obj->id); + OPK_Pack_uint8_t(msg, (const uint8_t*)&obj->extension); + OPK_Pack_uint16_t(msg, (const uint16_t*)&obj->length); + OPK_PackArray(msg, (const uint8_t*)&obj->data[0], 3); +} + +void OPK_Pack_OEMCrypto_DTCP2_CMI_Packet( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Packet const* obj) { + OPK_Pack_uint8_t(msg, (const uint8_t*)&obj->dtcp2_required); + OPK_Pack_OEMCrypto_DTCP2_CMI_Descriptor_0( + msg, (const OEMCrypto_DTCP2_CMI_Descriptor_0*)&obj->cmi_descriptor_0); + OPK_Pack_OEMCrypto_DTCP2_CMI_Descriptor_1( + msg, (const OEMCrypto_DTCP2_CMI_Descriptor_1*)&obj->cmi_descriptor_1); + OPK_Pack_OEMCrypto_DTCP2_CMI_Descriptor_2( + msg, (const OEMCrypto_DTCP2_CMI_Descriptor_2*)&obj->cmi_descriptor_2); +} + +void OPK_Pack_OEMCrypto_KeyObject(ODK_Message* msg, + OEMCrypto_KeyObject const* obj) { + OPK_Pack_OEMCrypto_Substring(msg, (const OEMCrypto_Substring*)&obj->key_id); + OPK_Pack_OEMCrypto_Substring(msg, + (const OEMCrypto_Substring*)&obj->key_data_iv); + OPK_Pack_OEMCrypto_Substring(msg, (const OEMCrypto_Substring*)&obj->key_data); + OPK_Pack_OEMCrypto_Substring( + msg, (const OEMCrypto_Substring*)&obj->key_control_iv); + OPK_Pack_OEMCrypto_Substring(msg, + (const OEMCrypto_Substring*)&obj->key_control); +} + +void OPK_Unpack_OEMCrypto_Substring(ODK_Message* msg, + OEMCrypto_Substring* obj) { + OPK_Unpack_size_t(msg, &obj->offset); + OPK_Unpack_size_t(msg, &obj->length); +} + +void OPK_Unpack_OEMCrypto_DTCP2_CMI_Descriptor_0( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_0* obj) { + OPK_Unpack_uint8_t(msg, &obj->id); + OPK_Unpack_uint8_t(msg, &obj->extension); + OPK_Unpack_uint16_t(msg, &obj->length); + OPK_Unpack_uint8_t(msg, &obj->data); +} + +void OPK_Unpack_OEMCrypto_DTCP2_CMI_Descriptor_1( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_1* obj) { + OPK_Unpack_uint8_t(msg, &obj->id); + OPK_Unpack_uint8_t(msg, &obj->extension); + OPK_Unpack_uint16_t(msg, &obj->length); + OPK_UnpackArray(msg, &obj->data[0], sizeof(obj->data)); +} + +void OPK_Unpack_OEMCrypto_DTCP2_CMI_Descriptor_2( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_2* obj) { + OPK_Unpack_uint8_t(msg, &obj->id); + OPK_Unpack_uint8_t(msg, &obj->extension); + OPK_Unpack_uint16_t(msg, &obj->length); + OPK_UnpackArray(msg, &obj->data[0], sizeof(obj->data)); +} + +void OPK_Unpack_OEMCrypto_DTCP2_CMI_Packet(ODK_Message* msg, + OEMCrypto_DTCP2_CMI_Packet* obj) { + OPK_Unpack_uint8_t(msg, &obj->dtcp2_required); + OPK_Unpack_OEMCrypto_DTCP2_CMI_Descriptor_0(msg, &obj->cmi_descriptor_0); + OPK_Unpack_OEMCrypto_DTCP2_CMI_Descriptor_1(msg, &obj->cmi_descriptor_1); + OPK_Unpack_OEMCrypto_DTCP2_CMI_Descriptor_2(msg, &obj->cmi_descriptor_2); +} + +void OPK_Unpack_OEMCrypto_KeyObject(ODK_Message* msg, + OEMCrypto_KeyObject* obj) { + OPK_Unpack_OEMCrypto_Substring(msg, &obj->key_id); + OPK_Unpack_OEMCrypto_Substring(msg, &obj->key_data_iv); + OPK_Unpack_OEMCrypto_Substring(msg, &obj->key_data); + OPK_Unpack_OEMCrypto_Substring(msg, &obj->key_control_iv); + OPK_Unpack_OEMCrypto_Substring(msg, &obj->key_control); +} + +void OPK_PackNullable_uint64_t(ODK_Message* msg, const uint64_t* value) { + OPK_PackBoolValue(msg, value == NULL); + if (value) { + OPK_Pack_uint64_t(msg, value); + } +} +void OPK_UnpackNullable_uint64_t(ODK_Message* msg, uint64_t** value) { + if (OPK_UnpackIsNull(msg)) { + *value = NULL; + } else { + OPK_Unpack_uint64_t(msg, *value); + } +} +void OPK_UnpackAlloc_uint64_t(ODK_Message* msg, uint64_t** value) { + *value = (uint64_t*)OPK_UnpackAlloc(msg, sizeof(uint64_t)); + if (*value) { + OPK_Unpack_uint64_t(msg, *value); + } +} +void OPK_PackNullable_KeySize(ODK_Message* msg, const KeySize* value) { + OPK_PackBoolValue(msg, value == NULL); + if (value) { + OPK_Pack_KeySize(msg, value); + } +} +void OPK_UnpackNullable_KeySize(ODK_Message* msg, KeySize** value) { + if (OPK_UnpackIsNull(msg)) { + *value = NULL; + } else { + OPK_Unpack_KeySize(msg, *value); + } +} +void OPK_UnpackAlloc_KeySize(ODK_Message* msg, KeySize** value) { + *value = (KeySize*)OPK_UnpackAlloc(msg, sizeof(KeySize)); + if (*value) { + OPK_Unpack_KeySize(msg, *value); + } +} +void OPK_PackNullable_uint8_t(ODK_Message* msg, const uint8_t* value) { + OPK_PackBoolValue(msg, value == NULL); + if (value) { + OPK_Pack_uint8_t(msg, value); + } +} +void OPK_UnpackNullable_uint8_t(ODK_Message* msg, uint8_t** value) { + if (OPK_UnpackIsNull(msg)) { + *value = NULL; + } else { + OPK_Unpack_uint8_t(msg, *value); + } +} +void OPK_UnpackAlloc_uint8_t(ODK_Message* msg, uint8_t** value) { + *value = (uint8_t*)OPK_UnpackAlloc(msg, sizeof(uint8_t)); + if (*value) { + OPK_Unpack_uint8_t(msg, *value); + } +} +void OPK_PackNullable_OPK_OutputBuffer(ODK_Message* msg, + const OPK_OutputBuffer* value) { + OPK_PackBoolValue(msg, value == NULL); + if (value) { + OPK_Pack_OPK_OutputBuffer(msg, value); + } +} +void OPK_UnpackNullable_OPK_OutputBuffer(ODK_Message* msg, + OPK_OutputBuffer** value) { + if (OPK_UnpackIsNull(msg)) { + *value = NULL; + } else { + OPK_Unpack_OPK_OutputBuffer(msg, *value); + } +} +void OPK_PackNullable_WTPI_K1_SymmetricKey_Handle( + ODK_Message* msg, const WTPI_K1_SymmetricKey_Handle* value) { + OPK_PackBoolValue(msg, value == NULL); + if (value) { + OPK_Pack_WTPI_K1_SymmetricKey_Handle(msg, value); + } +} +void OPK_UnpackNullable_WTPI_K1_SymmetricKey_Handle( + ODK_Message* msg, WTPI_K1_SymmetricKey_Handle** value) { + if (OPK_UnpackIsNull(msg)) { + *value = NULL; + } else { + OPK_Unpack_WTPI_K1_SymmetricKey_Handle(msg, *value); + } +} +void OPK_UnpackAlloc_WTPI_K1_SymmetricKey_Handle( + ODK_Message* msg, WTPI_K1_SymmetricKey_Handle** value) { + *value = (WTPI_K1_SymmetricKey_Handle*)OPK_UnpackAlloc( + msg, sizeof(WTPI_K1_SymmetricKey_Handle)); + if (*value) { + OPK_Unpack_WTPI_K1_SymmetricKey_Handle(msg, *value); + } +} +void OPK_PackNullable_WTPI_AsymmetricKey_Handle( + ODK_Message* msg, const WTPI_AsymmetricKey_Handle* value) { + OPK_PackBoolValue(msg, value == NULL); + if (value) { + OPK_Pack_WTPI_AsymmetricKey_Handle(msg, value); + } +} +void OPK_UnpackNullable_WTPI_AsymmetricKey_Handle( + ODK_Message* msg, WTPI_AsymmetricKey_Handle** value) { + if (OPK_UnpackIsNull(msg)) { + *value = NULL; + } else { + OPK_Unpack_WTPI_AsymmetricKey_Handle(msg, *value); + } +} +void OPK_UnpackAlloc_WTPI_AsymmetricKey_Handle( + ODK_Message* msg, WTPI_AsymmetricKey_Handle** value) { + *value = (WTPI_AsymmetricKey_Handle*)OPK_UnpackAlloc( + msg, sizeof(WTPI_AsymmetricKey_Handle)); + if (*value) { + OPK_Unpack_WTPI_AsymmetricKey_Handle(msg, *value); + } +} +void OPK_PackNullable_uint32_t(ODK_Message* msg, const uint32_t* value) { + OPK_PackBoolValue(msg, value == NULL); + if (value) { + OPK_Pack_uint32_t(msg, value); + } +} +void OPK_UnpackNullable_uint32_t(ODK_Message* msg, uint32_t** value) { + if (OPK_UnpackIsNull(msg)) { + *value = NULL; + } else { + OPK_Unpack_uint32_t(msg, *value); + } +} +void OPK_UnpackAlloc_uint32_t(ODK_Message* msg, uint32_t** value) { + *value = (uint32_t*)OPK_UnpackAlloc(msg, sizeof(uint32_t)); + if (*value) { + OPK_Unpack_uint32_t(msg, *value); + } +} +void OPK_PackNullable_size_t(ODK_Message* msg, const size_t* value) { + OPK_PackBoolValue(msg, value == NULL); + if (value) { + OPK_Pack_size_t(msg, value); + } +} +void OPK_UnpackNullable_size_t(ODK_Message* msg, size_t** value) { + if (OPK_UnpackIsNull(msg)) { + *value = NULL; + } else { + OPK_Unpack_size_t(msg, *value); + } +} +void OPK_UnpackAlloc_size_t(ODK_Message* msg, size_t** value) { + *value = (size_t*)OPK_UnpackAlloc(msg, sizeof(size_t)); + if (*value) { + OPK_Unpack_size_t(msg, *value); + } +} +void OPK_PackNullable_AsymmetricKeyType(ODK_Message* msg, + const AsymmetricKeyType* value) { + OPK_PackBoolValue(msg, value == NULL); + if (value) { + OPK_Pack_AsymmetricKeyType(msg, value); + } +} +void OPK_UnpackNullable_AsymmetricKeyType(ODK_Message* msg, + AsymmetricKeyType** value) { + if (OPK_UnpackIsNull(msg)) { + *value = NULL; + } else { + OPK_Unpack_AsymmetricKeyType(msg, *value); + } +} +void OPK_UnpackAlloc_AsymmetricKeyType(ODK_Message* msg, + AsymmetricKeyType** value) { + *value = (AsymmetricKeyType*)OPK_UnpackAlloc(msg, sizeof(AsymmetricKeyType)); + if (*value) { + OPK_Unpack_AsymmetricKeyType(msg, *value); + } +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/common/GEN_common_serializer.h b/oemcrypto/opk/oemcrypto_ta/wtpi_test/common/GEN_common_serializer.h new file mode 100644 index 0000000..75bb085 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/common/GEN_common_serializer.h @@ -0,0 +1,91 @@ +/* + * Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine License + * Agreement. + */ + +/* + * This code is auto-generated, do not edit + */ +#ifndef OPK_COMMON_SERIALIZER_H_ +#define OPK_COMMON_SERIALIZER_H_ + +#include "log_macros.h" +#include "opk_serialization_base.h" +#include "wtpi_crc32_interface.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" +#include "wtpi_crypto_asymmetric_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool SuccessResult(OEMCryptoResult result); +bool Is_Valid_OEMCryptoResult(uint32_t value); +bool Is_Valid_OEMCrypto_Usage_Entry_Status(uint32_t value); +bool Is_Valid_OEMCrypto_LicenseType(uint32_t value); +bool Is_Valid_OEMCrypto_PrivateKeyType(uint32_t value); +bool Is_Valid_OPK_OutputBuffer_Type(uint32_t value); +void OPK_Pack_OEMCrypto_Substring(ODK_Message* msg, + OEMCrypto_Substring const* obj); +void OPK_Pack_OEMCrypto_DTCP2_CMI_Descriptor_0( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_0 const* obj); +void OPK_Pack_OEMCrypto_DTCP2_CMI_Descriptor_1( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_1 const* obj); +void OPK_Pack_OEMCrypto_DTCP2_CMI_Descriptor_2( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_2 const* obj); +void OPK_Pack_OEMCrypto_DTCP2_CMI_Packet(ODK_Message* msg, + OEMCrypto_DTCP2_CMI_Packet const* obj); +void OPK_Pack_OEMCrypto_KeyObject(ODK_Message* msg, + OEMCrypto_KeyObject const* obj); +void OPK_Unpack_OEMCrypto_Substring(ODK_Message* msg, OEMCrypto_Substring* obj); +void OPK_Unpack_OEMCrypto_DTCP2_CMI_Descriptor_0( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_0* obj); +void OPK_Unpack_OEMCrypto_DTCP2_CMI_Descriptor_1( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_1* obj); +void OPK_Unpack_OEMCrypto_DTCP2_CMI_Descriptor_2( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_2* obj); +void OPK_Unpack_OEMCrypto_DTCP2_CMI_Packet(ODK_Message* msg, + OEMCrypto_DTCP2_CMI_Packet* obj); +void OPK_Unpack_OEMCrypto_KeyObject(ODK_Message* msg, OEMCrypto_KeyObject* obj); +void OPK_PackNullable_uint64_t(ODK_Message* msg, const uint64_t* value); +void OPK_UnpackNullable_uint64_t(ODK_Message* msg, uint64_t** value); +void OPK_UnpackAlloc_uint64_t(ODK_Message* msg, uint64_t** value); +void OPK_PackNullable_KeySize(ODK_Message* msg, const KeySize* value); +void OPK_UnpackNullable_KeySize(ODK_Message* msg, KeySize** value); +void OPK_UnpackAlloc_KeySize(ODK_Message* msg, KeySize** value); +void OPK_PackNullable_uint8_t(ODK_Message* msg, const uint8_t* value); +void OPK_UnpackNullable_uint8_t(ODK_Message* msg, uint8_t** value); +void OPK_UnpackAlloc_uint8_t(ODK_Message* msg, uint8_t** value); +void OPK_PackNullable_OPK_OutputBuffer(ODK_Message* msg, + const OPK_OutputBuffer* value); +void OPK_UnpackNullable_OPK_OutputBuffer(ODK_Message* msg, + OPK_OutputBuffer** value); +void OPK_PackNullable_WTPI_K1_SymmetricKey_Handle( + ODK_Message* msg, const WTPI_K1_SymmetricKey_Handle* value); +void OPK_UnpackNullable_WTPI_K1_SymmetricKey_Handle( + ODK_Message* msg, WTPI_K1_SymmetricKey_Handle** value); +void OPK_UnpackAlloc_WTPI_K1_SymmetricKey_Handle( + ODK_Message* msg, WTPI_K1_SymmetricKey_Handle** value); +void OPK_PackNullable_WTPI_AsymmetricKey_Handle( + ODK_Message* msg, const WTPI_AsymmetricKey_Handle* value); +void OPK_UnpackNullable_WTPI_AsymmetricKey_Handle( + ODK_Message* msg, WTPI_AsymmetricKey_Handle** value); +void OPK_UnpackAlloc_WTPI_AsymmetricKey_Handle( + ODK_Message* msg, WTPI_AsymmetricKey_Handle** value); +void OPK_PackNullable_uint32_t(ODK_Message* msg, const uint32_t* value); +void OPK_UnpackNullable_uint32_t(ODK_Message* msg, uint32_t** value); +void OPK_UnpackAlloc_uint32_t(ODK_Message* msg, uint32_t** value); +void OPK_PackNullable_size_t(ODK_Message* msg, const size_t* value); +void OPK_UnpackNullable_size_t(ODK_Message* msg, size_t** value); +void OPK_UnpackAlloc_size_t(ODK_Message* msg, size_t** value); +void OPK_PackNullable_AsymmetricKeyType(ODK_Message* msg, + const AsymmetricKeyType* value); +void OPK_UnpackNullable_AsymmetricKeyType(ODK_Message* msg, + AsymmetricKeyType** value); +void OPK_UnpackAlloc_AsymmetricKeyType(ODK_Message* msg, + AsymmetricKeyType** value); +#ifdef __cplusplus +} // extern "C" +#endif +#endif /* OPK_COMMON_SERIALIZER_H_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/common/common_special_cases.c b/oemcrypto/opk/oemcrypto_ta/wtpi_test/common/common_special_cases.c new file mode 100644 index 0000000..d53728e --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/common/common_special_cases.c @@ -0,0 +1,215 @@ +/* + * 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 "common_special_cases.h" +#include "log_macros.h" +#include "opk_serialization_base.h" + +void OPK_Pack_WTPI_K1_SymmetricKey_Handle( + ODK_Message* message, const WTPI_K1_SymmetricKey_Handle* value) { + if (value == NULL) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_NULL_POINTER_ERROR); + return; + } + + OPK_Pack_size_t(message, (const size_t*)value); +} + +void OPK_Pack_WTPI_AsymmetricKey_Handle( + ODK_Message* message, const WTPI_AsymmetricKey_Handle* value) { + if (value == NULL) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_NULL_POINTER_ERROR); + return; + } + + OPK_Pack_size_t(message, (const size_t*)value); +} + +void OPK_Unpack_WTPI_K1_SymmetricKey_Handle( + ODK_Message* message, WTPI_K1_SymmetricKey_Handle* value) { + if (value == NULL) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_NULL_POINTER_ERROR); + return; + } + + OPK_Unpack_size_t(message, (size_t*)value); +} + +void OPK_Unpack_WTPI_AsymmetricKey_Handle(ODK_Message* message, + WTPI_AsymmetricKey_Handle* value) { + if (value == NULL) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_NULL_POINTER_ERROR); + return; + } + + OPK_Unpack_size_t(message, (size_t*)value); +} + +void OPK_Pack_OPK_OutputBuffer(ODK_Message* message, + const OPK_OutputBuffer* value) { + if (value == NULL) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_NULL_POINTER_ERROR); + return; + } + + OPK_Pack_uint32_t(message, (const uint32_t*)&value->type); + OPK_Pack_size_t(message, &value->size); + + switch (value->type) { + case OPK_CLEAR_INSECURE_OUTPUT_BUFFER: + OPK_PackSharedBuffer(message, value->buffer.clear_insecure, + OPK_ToLengthType(value->size), + /* map */ true, /* copy in */ true, + /* is_output */ true); + break; + case OPK_SECURE_OUTPUT_BUFFER: + // TODO: actually implement + break; + default: + break; + } +} + +void OPK_Unpack_OPK_OutputBuffer(ODK_Message* message, + OPK_OutputBuffer* value) { + if (value == NULL) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_NULL_POINTER_ERROR); + return; + } + + OPK_Unpack_uint32_t(message, (uint32_t*)&value->type); + OPK_Unpack_size_t(message, &value->size); + + switch (value->type) { + case OPK_CLEAR_INSECURE_OUTPUT_BUFFER: + OPK_UnpackSharedBuffer(message, &value->buffer.clear_insecure, + OPK_ToLengthType(value->size), + /* map */ true, /* is output */ true); + break; + case OPK_SECURE_OUTPUT_BUFFER: + // TODO: actually implement + break; + default: + break; + } +} + +void OPK_Pack_OEMCrypto_CENCEncryptPatternDesc( + ODK_Message* message, const OEMCrypto_CENCEncryptPatternDesc* value) {} +void OPK_Unpack_OEMCrypto_CENCEncryptPatternDesc( + ODK_Message* message, OEMCrypto_CENCEncryptPatternDesc* value) {} + +void OPK_Pack_SymmetricKeyType(ODK_Message* message, + const SymmetricKeyType* value) { + if (value == NULL) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_NULL_POINTER_ERROR); + return; + } + + OPK_Pack_int(message, (const int*)value); +} + +void OPK_Unpack_SymmetricKeyType(ODK_Message* message, + SymmetricKeyType* value) { + if (value == NULL) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_NULL_POINTER_ERROR); + return; + } + + OPK_Unpack_int(message, (int*)value); +} + +void OPK_Pack_AsymmetricKeyType(ODK_Message* message, + const AsymmetricKeyType* value) { + if (value == NULL) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_NULL_POINTER_ERROR); + return; + } + + OPK_Pack_int(message, (const int*)value); +} + +void OPK_Unpack_AsymmetricKeyType(ODK_Message* message, + AsymmetricKeyType* value) { + if (value == NULL) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_NULL_POINTER_ERROR); + return; + } + + OPK_Unpack_int(message, (int*)value); +} + +void OPK_Pack_RSA_Padding_Scheme(ODK_Message* message, + const RSA_Padding_Scheme* value) { + if (value == NULL) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_NULL_POINTER_ERROR); + return; + } + + OPK_Pack_uint8_t(message, (const uint8_t*)value); +} + +void OPK_Unpack_RSA_Padding_Scheme(ODK_Message* message, + RSA_Padding_Scheme* value) { + if (value == NULL) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_NULL_POINTER_ERROR); + return; + } + + OPK_Unpack_uint8_t(message, (uint8_t*)value); +} + +void OPK_Pack_KeySize(ODK_Message* message, const KeySize* value) { + if (value == NULL) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_NULL_POINTER_ERROR); + return; + } + + OPK_Pack_int(message, (const int*)value); +} + +void OPK_Unpack_KeySize(ODK_Message* message, KeySize* value) { + if (value == NULL) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_NULL_POINTER_ERROR); + return; + } + + OPK_Unpack_int(message, (int*)value); +} + +void OPK_Pack_OEMCrypto_SharedMemory(ODK_Message* message, + const OEMCrypto_SharedMemory* value) { + (void)message; + (void)value; + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_NULL_POINTER_ERROR); +} + +void OPK_Unpack_OEMCrypto_SharedMemory(ODK_Message* message, + OEMCrypto_SharedMemory* value) { + (void)message; + (void)value; + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_NULL_POINTER_ERROR); +} + +void OPK_Pack_OEMCrypto_Clock_Security_Level( + ODK_Message* message, const OEMCrypto_Clock_Security_Level* value) { + if (value == NULL) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_NULL_POINTER_ERROR); + return; + } + + OPK_Pack_uint32_t(message, (const uint32_t*)value); +} + +void OPK_Unpack_OEMCrypto_Clock_Security_Level( + ODK_Message* message, OEMCrypto_Clock_Security_Level* value) { + if (value == NULL) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_NULL_POINTER_ERROR); + return; + } + + OPK_Unpack_uint32_t(message, (uint32_t*)value); +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/common/common_special_cases.h b/oemcrypto/opk/oemcrypto_ta/wtpi_test/common/common_special_cases.h new file mode 100644 index 0000000..48b67a2 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/common/common_special_cases.h @@ -0,0 +1,58 @@ +/* + * 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 WTPI_TEST_COMMON_SPECIAL_CASES_H_ +#define WTPI_TEST_COMMON_SPECIAL_CASES_H_ + +#include "odk_message.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" +#include "wtpi_crypto_asymmetric_interface.h" + +void OPK_Pack_WTPI_K1_SymmetricKey_Handle( + ODK_Message* message, const WTPI_K1_SymmetricKey_Handle* value); +void OPK_Unpack_WTPI_K1_SymmetricKey_Handle(ODK_Message* message, + WTPI_K1_SymmetricKey_Handle* value); + +void OPK_Pack_WTPI_AsymmetricKey_Handle(ODK_Message* message, + const WTPI_AsymmetricKey_Handle* value); +void OPK_Unpack_WTPI_AsymmetricKey_Handle(ODK_Message* message, + WTPI_AsymmetricKey_Handle* value); + +void OPK_Pack_OPK_OutputBuffer(ODK_Message* message, + const OPK_OutputBuffer* value); +void OPK_Unpack_OPK_OutputBuffer(ODK_Message* message, OPK_OutputBuffer* value); + +void OPK_Pack_OEMCrypto_CENCEncryptPatternDesc( + ODK_Message* message, const OEMCrypto_CENCEncryptPatternDesc* value); +void OPK_Unpack_OEMCrypto_CENCEncryptPatternDesc( + ODK_Message* message, OEMCrypto_CENCEncryptPatternDesc* value); +void OPK_Pack_SymmetricKeyType(ODK_Message* msg, + const SymmetricKeyType* key_type); +void OPK_Unpack_SymmetricKeyType(ODK_Message* msg, SymmetricKeyType* key_type); + +void OPK_Pack_AsymmetricKeyType(ODK_Message* msg, + const AsymmetricKeyType* key_type); +void OPK_Unpack_AsymmetricKeyType(ODK_Message* msg, + AsymmetricKeyType* key_type); + +void OPK_Pack_RSA_Padding_Scheme(ODK_Message* msg, + const RSA_Padding_Scheme* padding_scheme); +void OPK_Unpack_RSA_Padding_Scheme(ODK_Message* msg, + RSA_Padding_Scheme* padding_scheme); + +void OPK_Pack_KeySize(ODK_Message* msg, const KeySize* key_size); +void OPK_Unpack_KeySize(ODK_Message* msg, KeySize* key_size); + +void OPK_Pack_OEMCrypto_SharedMemory(ODK_Message* message, + const OEMCrypto_SharedMemory* value); +void OPK_Unpack_OEMCrypto_SharedMemory(ODK_Message* message, + OEMCrypto_SharedMemory* value); + +void OPK_Pack_OEMCrypto_Clock_Security_Level( + ODK_Message* msg, const OEMCrypto_Clock_Security_Level* value); +void OPK_Unpack_OEMCrypto_Clock_Security_Level( + ODK_Message* msg, OEMCrypto_Clock_Security_Level* value); + +#endif diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/crypto_test.cpp b/oemcrypto/opk/oemcrypto_ta/wtpi_test/crypto_test.cpp new file mode 100644 index 0000000..188949c --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/crypto_test.cpp @@ -0,0 +1,1653 @@ +// 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 + +#include "OEMCryptoCENC.h" +#include "log.h" +#include "oemcrypto_key_types.h" +#include "openssl/curve25519.h" +#include "openssl/ecdsa.h" +#include "openssl/sha.h" +#include "opk_init.h" +#include "ssl_util.h" +#include "tos_shared_memory_interface.h" +#include "wtpi_crc32_interface.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" +#include "wtpi_crypto_asymmetric_interface.h" + +#define TEST_RSA_KEY_DER_LEN 1216 +#define HELLO_WORLD_ENC_LEN 256 +extern uint8_t test_rsa_key_der[TEST_RSA_KEY_DER_LEN]; +extern uint8_t hello_world_encrypted[HELLO_WORLD_ENC_LEN]; + +using wtpi_test::EcKeyPtr; +using wtpi_test::MakeEccPublicKey; +using wtpi_test::NewEccPrivateKey; +using wtpi_test::SerializeEccPrivateKey; +using wtpi_test::SerializeEccPublicKey; + +class CryptoTest : public ::testing::Test { + protected: + CryptoTest() {} + + void SetUp() override { + ::testing::Test::SetUp(); + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + LOGD("Running test %s.%s", test_info->test_case_name(), test_info->name()); + ASSERT_EQ(true, OPK_Initialize()); + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_K1_InitializeKeyManagement()); + } + + void TearDown() override { + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_K1_TerminateKeyManagement()); + OPK_Terminate(); + ::testing::Test::TearDown(); + } +}; + +TEST_F(CryptoTest, CreateKeyHandleWorksWithTypicalKeySize) { + std::vector key; + for (int i = 0; i < 32; i++) { + key.push_back(i); + } + + WTPI_K1_SymmetricKey_Handle key_handle; + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_K1_CreateKeyHandle(key.data(), KEY_SIZE_128, CONTENT_KEY, + &key_handle)); + ASSERT_NE(nullptr, key_handle); + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_K1_CreateKeyHandle(key.data(), KEY_SIZE_256, CONTENT_KEY, + &key_handle)); + ASSERT_NE(nullptr, key_handle); +} + +TEST_F(CryptoTest, CreateKeyHandleFailsWithBadParams) { + std::vector key; + for (int i = 0; i < 32; i++) { + key.push_back(i); + } + + WTPI_K1_SymmetricKey_Handle key_handle; + + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_K1_CreateKeyHandle(key.data(), UNKNOWN_KEY_SIZE, CONTENT_KEY, + &key_handle)); + ASSERT_EQ( + OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_K1_CreateKeyHandle(NULL, KEY_SIZE_128, CONTENT_KEY, &key_handle)); + ASSERT_EQ( + OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_K1_CreateKeyHandle(key.data(), KEY_SIZE_128, CONTENT_KEY, NULL)); +} + +TEST_F(CryptoTest, AESCBCEncryptHelloWorld) { + std::vector key; + for (int i = 0; i < 32; i++) { + key.push_back(i); + } + + SymmetricKeyType key_type = CONTENT_KEY; + WTPI_K1_SymmetricKey_Handle key_handle; + + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_K1_CreateKeyHandle(key.data(), KEY_SIZE_128, + key_type, &key_handle)); + ASSERT_NE(nullptr, key_handle); + + // Encrypt + std::string message = "Hello world!______"; + std::vector input(message.begin(), message.end()); + std::vector iv = {99, 0, 23, 18, 75, 4, 92, 115, + 24, 70, 56, 57, 12, 43, 15, 29}; + + std::vector output(16, 0); + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_C1_AESCBCEncrypt(key_handle, input.data(), 16, iv.data(), + output.data())); + + std::vector expected = {72, 148, 193, 81, 175, 242, 38, 26, + 247, 167, 88, 96, 223, 94, 41, 95}; + + for (int i = 0; i < 16; i++) { + ASSERT_EQ(expected[i], output[i]); + } +} + +TEST_F(CryptoTest, AESCBCEncryptFailsForBadInput) { + std::vector key; + for (int i = 0; i < 32; i++) { + key.push_back(i); + } + + SymmetricKeyType key_type = CONTENT_KEY; + WTPI_K1_SymmetricKey_Handle key_handle; + + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_K1_CreateKeyHandle(key.data(), KEY_SIZE_128, + key_type, &key_handle)); + + std::string message = "Hello world!______"; + std::vector input(message.begin(), message.end()); + std::vector iv = {99, 0, 23, 18, 75, 4, 92, 115, + 24, 70, 56, 57, 12, 43, 15, 29}; + + std::vector output(16, 0); + ASSERT_EQ( + OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_AESCBCEncrypt(NULL, input.data(), 16, iv.data(), output.data())); + ASSERT_EQ( + OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_AESCBCEncrypt(key_handle, NULL, 16, iv.data(), output.data())); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_AESCBCEncrypt(key_handle, input.data(), 0, iv.data(), + output.data())); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_AESCBCEncrypt(key_handle, input.data(), 15, iv.data(), + output.data())); + + // TODO(b/205751866): serializer allocates iv array on TEE side regardless if + // REE iv ptr is NULL, so the NULL never propagates to the TEE + // + // ASSERT_EQ( + // OEMCrypto_ERROR_INVALID_CONTEXT, + // WTPI_C1_AESCBCEncrypt(key_handle, input.data(), 16, NULL, output.data())); + + ASSERT_EQ( + OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_AESCBCEncrypt(key_handle, input.data(), 16, iv.data(), NULL)); +} + +TEST_F(CryptoTest, AESCBCDecryptHelloWorld) { + std::vector key; + for (int i = 0; i < 32; i++) { + key.push_back(i); + } + + SymmetricKeyType key_type = CONTENT_KEY; + WTPI_K1_SymmetricKey_Handle key_handle; + + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_K1_CreateKeyHandle(key.data(), KEY_SIZE_128, + key_type, &key_handle)); + ASSERT_NE(nullptr, key_handle); + + // Decrypt + // Uses encrypted "hello world" input from the Encrypt test, same IV + std::vector input = {72, 148, 193, 81, 175, 242, 38, 26, + 247, 167, 88, 96, 223, 94, 41, 95}; + std::vector iv = {99, 0, 23, 18, 75, 4, 92, 115, + 24, 70, 56, 57, 12, 43, 15, 29}; + std::vector output(16, 0); + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_C1_AESCBCDecrypt(key_handle, KEY_SIZE_128, input.data(), 16, + iv.data(), output.data())); + + std::string hello_world = "Hello world!_____"; + std::vector expected(hello_world.begin(), hello_world.end()); + for (int i = 0; i < 16; i++) { + ASSERT_EQ(expected[i], output[i]); + } +} + +TEST_F(CryptoTest, AESCBCEncryptDecryptLoop) { + std::vector key; + for (int i = 0; i < 32; i++) { + key.push_back(i); + } + + WTPI_K1_SymmetricKey_Handle key_handle; + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_K1_CreateKeyHandle(key.data(), KEY_SIZE_128, CONTENT_KEY, + &key_handle)); + ASSERT_NE(nullptr, key_handle); + + std::string message = "EncryptDecryptLoop"; + std::vector input(message.begin(), message.end()); + std::vector iv = {99, 0, 23, 18, 75, 4, 92, 115, + 24, 70, 56, 57, 12, 43, 15, 29}; + + std::vector decrypted(16, 0); + for (int i = 0; i < 10; i++) { + std::vector encrypted(16, 0); + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_C1_AESCBCEncrypt(key_handle, input.data(), 16, iv.data(), + encrypted.data())); + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_C1_AESCBCDecrypt(key_handle, KEY_SIZE_128, encrypted.data(), + 16, iv.data(), decrypted.data())); + } + + for (int i = 0; i < 16; i++) { + ASSERT_EQ(input[i], decrypted[i]); + } +} + +TEST_F(CryptoTest, AESCBCDecryptFailsForBadInput) { + std::vector key; + for (int i = 0; i < 32; i++) { + key.push_back(i); + } + + SymmetricKeyType key_type = CONTENT_KEY; + WTPI_K1_SymmetricKey_Handle key_handle; + + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_K1_CreateKeyHandle(key.data(), KEY_SIZE_128, + key_type, &key_handle)); + + std::vector input = {72, 148, 193, 81, 175, 242, 38, 26, + 247, 167, 88, 96, 223, 94, 41, 95}; + std::vector iv = {99, 0, 23, 18, 75, 4, 92, 115, + 24, 70, 56, 57, 12, 43, 15, 29}; + std::vector output(16, 0); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_AESCBCDecrypt(NULL, KEY_SIZE_128, input.data(), 16, + iv.data(), output.data())); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_AESCBCDecrypt(key_handle, KEY_SIZE_128, NULL, 16, iv.data(), + output.data())); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_AESCBCDecrypt(key_handle, KEY_SIZE_128, input.data(), 0, + iv.data(), output.data())); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_AESCBCDecrypt(key_handle, KEY_SIZE_128, input.data(), 15, + iv.data(), output.data())); + + // TODO(b/205751866): serializer allocates iv array on TEE side regardless if + // REE iv ptr is NULL, so the NULL never propagates to the TEE + // + // ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + // WTPI_C1_AESCBCDecrypt(key_handle, KEY_SIZE_128, input.data(), 16, NULL, + // output.data())); + + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_AESCBCDecrypt(key_handle, KEY_SIZE_128, input.data(), 16, + iv.data(), NULL)); +} + +// TODO: larger inputs for encrypt/decrypt? + +TEST_F(CryptoTest, SHA256Basic) { + std::vector input; + for (int i = 0; i < 32; i++) { + input.push_back(i); + } + std::vector output(32, 0); + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_C1_SHA256(input.data(), input.size(), output.data())); + + std::vector expected = {99, 13, 205, 41, 102, 196, 51, 102, + 145, 18, 84, 72, 187, 178, 91, 79, + 244, 18, 164, 156, 115, 45, 178, 200, + 171, 193, 184, 88, 27, 215, 16, 221}; + for (int i = 0; i < 32; i++) { + ASSERT_EQ(expected[i], output[i]); + } +} + +TEST_F(CryptoTest, SHA256Small) { + std::vector input; + for (int i = 0; i < 16; i++) { + input.push_back(i); + } + std::vector output(32, 0); + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_C1_SHA256(input.data(), input.size(), output.data())); + + std::vector expected = {190, 69, 203, 38, 5, 191, 54, 190, + 189, 230, 132, 132, 26, 40, 240, 253, + 67, 198, 152, 80, 163, 220, 229, 254, + 219, 166, 153, 40, 238, 58, 137, 145}; + + for (int i = 0; i < 32; i++) { + ASSERT_EQ(expected[i], output[i]); + } +} + +TEST_F(CryptoTest, SHA256FailsWithBadInput) { + std::vector input; + for (int i = 0; i < 32; i++) { + input.push_back(i); + } + std::vector output(32, 0); + + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_SHA256(NULL, input.size(), output.data())); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_SHA256(input.data(), 0, output.data())); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_SHA256(input.data(), input.size(), NULL)); +} + +TEST_F(CryptoTest, SHA256Medium) { + std::vector input; + for (int i = 0; i < 1024; i++) { + input.push_back(i); + } + std::vector output(32, 0); + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_C1_SHA256(input.data(), input.size(), output.data())); + std::vector expected = {120, 91, 7, 81, 252, 44, 83, 220, + 20, 164, 206, 61, 128, 14, 105, 239, + 156, 225, 0, 158, 179, 39, 204, 244, + 88, 175, 224, 156, 36, 44, 38, 201}; + + for (int i = 0; i < 32; i++) { + ASSERT_EQ(expected[i], output[i]); + } +} + +TEST_F(CryptoTest, CRC32FailsWhenInitialIsNull) { + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, WTPI_Crc32Init(NULL)); +} + +TEST_F(CryptoTest, CRC32Basic) { + uint32_t initial_hash; + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_Crc32Init(&initial_hash)); + + std::vector input; + for (int i = 0; i < 128; i++) { + input.push_back(i); + } + + uint32_t old_crc = initial_hash; + uint32_t new_crc; + uint32_t expected = 0x2f18b043; + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_Crc32Cont(input.data(), input.size(), old_crc, &new_crc)); + + ASSERT_EQ(expected, new_crc); +} + +TEST_F(CryptoTest, CRC32ContFailsForBadInputs) { + uint32_t initial_hash; + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_Crc32Init(&initial_hash)); + + std::vector input; + for (int i = 0; i < 128; i++) { + input.push_back(i); + } + + uint32_t old_crc = initial_hash; + uint32_t new_crc; + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_Crc32Cont(NULL, input.size(), old_crc, &new_crc)); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_Crc32Cont(input.data(), 0, old_crc, &new_crc)); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_Crc32Cont(input.data(), input.size(), old_crc, NULL)); +} + +TEST_F(CryptoTest, Crc32Cont_OutputBufferBasic) { + std::vector buf; + for (int i = 0; i < 32; i++) { + buf.push_back(i); + } + + OPK_OutputBuffer in; + in.type = OPK_CLEAR_INSECURE_OUTPUT_BUFFER; + in.buffer.clear_insecure = buf.data(); + in.size = buf.size(); + + uint32_t new_crc; + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_Crc32Cont_OutputBuffer(&in, 0, 32, 0, &new_crc)); + ASSERT_EQ((uint32_t)0xc5d43637, new_crc); + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_Crc32Cont_OutputBuffer(&in, 2, 30, 0, &new_crc)); + ASSERT_EQ((uint32_t)0xe45a3a4f, new_crc); + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_Crc32Cont_OutputBuffer(&in, 0, 30, 0, &new_crc)); + ASSERT_EQ((uint32_t)0x8cb03e9b, new_crc); + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_Crc32Cont_OutputBuffer(&in, 0, 32, 0x4242ff90, &new_crc)); + ASSERT_EQ((uint32_t)0x088147d2, new_crc); +} + +TEST_F(CryptoTest, Crc32Cont_OutputBufferFailsWithBadInput) { + std::vector buf; + for (int i = 0; i < 32; i++) { + buf.push_back(i); + } + + OPK_OutputBuffer in; + in.type = OPK_CLEAR_INSECURE_OUTPUT_BUFFER; + in.buffer.clear_insecure = buf.data(); + in.size = buf.size(); + + uint32_t new_crc; + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_Crc32Cont_OutputBuffer(NULL, 0, 32, 0, &new_crc)); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_Crc32Cont_OutputBuffer(&in, 30, 32, 0, &new_crc)); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_Crc32Cont_OutputBuffer(&in, 0, 32, 0, NULL)); +} + +TEST_F(CryptoTest, HMAC_SHA256FailsWithBadInput) { + std::vector key; + for (int i = 0; i < 32; i++) { + key.push_back(i); + } + + WTPI_K1_SymmetricKey_Handle key_handle; + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_K1_CreateKeyHandle(key.data(), KEY_SIZE_256, MAC_KEY_CLIENT, + &key_handle)); + + std::vector input; + for (int i = 0; i < 32; i++) { + input.push_back(i); + } + std::vector output(32, 0); + + ASSERT_EQ( + OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_HMAC_SHA256(NULL, input.data(), input.size(), output.data())); + + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_HMAC_SHA256(key_handle, NULL, input.size(), output.data())); + + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_HMAC_SHA256(key_handle, input.data(), 0, output.data())); + + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_HMAC_SHA256(key_handle, input.data(), input.size(), NULL)); +} + +TEST_F(CryptoTest, HMAC_SHA256Basic) { + std::vector key; + for (int i = 0; i < 32; i++) { + key.push_back(i); + } + + WTPI_K1_SymmetricKey_Handle key_handle; + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_K1_CreateKeyHandle(key.data(), KEY_SIZE_256, MAC_KEY_CLIENT, + &key_handle)); + + std::vector input; + for (int i = 0; i < 32; i++) { + input.push_back(i); + } + std::vector output(32, 0); + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_C1_HMAC_SHA256(key_handle, input.data(), input.size(), + output.data())); + + std::vector expected = {232, 73, 155, 228, 241, 152, 13, 104, + 241, 50, 34, 164, 24, 223, 92, 189, + 151, 213, 63, 221, 245, 144, 194, 16, + 142, 34, 212, 0, 5, 183, 7, 19}; + for (int i = 0; i < 32; i++) { + ASSERT_EQ(expected[i], output[i]); + } +} + +TEST_F(CryptoTest, HMAC_SHA256_VerifyBasic) { + std::vector key; + for (int i = 0; i < 32; i++) { + key.push_back(i); + } + + WTPI_K1_SymmetricKey_Handle key_handle; + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_K1_CreateKeyHandle(key.data(), KEY_SIZE_256, MAC_KEY_CLIENT, + &key_handle)); + + std::vector input; + for (int i = 0; i < 32; i++) { + input.push_back(i); + } + std::vector signature = {232, 73, 155, 228, 241, 152, 13, 104, + 241, 50, 34, 164, 24, 223, 92, 189, + 151, 213, 63, 221, 245, 144, 194, 16, + 142, 34, 212, 0, 5, 183, 7, 19}; + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_C1_HMAC_SHA256_Verify(key_handle, input.data(), 32, + signature.data())); + + signature[0] = 0xFF; + ASSERT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE, + WTPI_C1_HMAC_SHA256_Verify(key_handle, input.data(), input.size(), + signature.data())); +} + +TEST_F(CryptoTest, HMAC_SHA256_VerifyFailsWithBadInput) { + std::vector key; + for (int i = 0; i < 32; i++) { + key.push_back(i); + } + + WTPI_K1_SymmetricKey_Handle key_handle; + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_K1_CreateKeyHandle(key.data(), KEY_SIZE_256, MAC_KEY_CLIENT, + &key_handle)); + + std::vector input; + for (int i = 0; i < 32; i++) { + input.push_back(i); + } + std::vector signature = {70, 189, 50, 6, 5, 197, 166, 182, + 22, 58, 183, 11, 198, 52, 91, 146, + 165, 249, 8, 231, 159, 229, 137, 121, + 194, 62, 187, 71, 209, 165, 227, 7}; + + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_HMAC_SHA256_Verify(NULL, input.data(), input.size(), + signature.data())); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_HMAC_SHA256_Verify(key_handle, NULL, input.size(), + signature.data())); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_HMAC_SHA256_Verify(key_handle, input.data(), 0, + signature.data())); + + ASSERT_EQ( + OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_HMAC_SHA256_Verify(key_handle, input.data(), input.size(), NULL)); +} + +TEST_F(CryptoTest, HMAC_SHA1Basic) { + std::vector key; + for (int i = 0; i < 32; i++) { + key.push_back(i); + } + + WTPI_K1_SymmetricKey_Handle key_handle; + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_K1_CreateKeyHandle(key.data(), KEY_SIZE_256, MAC_KEY_CLIENT, + &key_handle)); + + std::vector input; + for (int i = 0; i < 32; i++) { + input.push_back(i); + } + std::vector output(20, 0); + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_C1_HMAC_SHA1(key_handle, input.data(), + input.size(), output.data())); + + std::vector expected = {106, 229, 208, 106, 97, 2, 39, + 252, 78, 167, 85, 132, 70, 103, + 130, 238, 157, 20, 201, 141}; + for (int i = 0; i < 20; i++) { + ASSERT_EQ(expected[i], output[i]); + } +} + +TEST_F(CryptoTest, HMAC_SHA1FailsWithBadInput) { + std::vector key; + for (int i = 0; i < 32; i++) { + key.push_back(i); + } + + WTPI_K1_SymmetricKey_Handle key_handle; + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_K1_CreateKeyHandle(key.data(), KEY_SIZE_256, MAC_KEY_CLIENT, + &key_handle)); + + std::vector input; + for (int i = 0; i < 32; i++) { + input.push_back(i); + } + std::vector output(20, 0); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_HMAC_SHA1(NULL, input.data(), input.size(), output.data())); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_HMAC_SHA1(key_handle, NULL, input.size(), output.data())); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_HMAC_SHA1(key_handle, input.data(), 0, output.data())); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_HMAC_SHA1(key_handle, input.data(), input.size(), NULL)); +} + +TEST_F(CryptoTest, CopyToOutputBufferBasicInsecure) { + std::vector input(32, 0); + for (int i = 0; i < 32; i++) { + input[i] = i; + } + std::vector output(16, 0); + + OPK_OutputBuffer output_buffer; + output_buffer.type = OPK_CLEAR_INSECURE_OUTPUT_BUFFER; + output_buffer.buffer.clear_insecure = output.data(); + output_buffer.size = 16; + + size_t offset = 0; + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_C1_CopyToOutputBuffer( + input.data(), 10, &output_buffer, offset)); + + for (int i = 0; i < 10; i++) { + ASSERT_EQ(input[i], output[i]); + } +} + +TEST_F(CryptoTest, CopyToOutputBufferBasicInsecureWithOffset) { + std::vector input(32, 0); + for (int i = 0; i < 32; i++) { + input[i] = i; + } + std::vector output(16, 0); + + OPK_OutputBuffer output_buffer; + output_buffer.type = OPK_CLEAR_INSECURE_OUTPUT_BUFFER; + output_buffer.buffer.clear_insecure = output.data(); + output_buffer.size = 16; + + size_t offset = 5; + + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_C1_CopyToOutputBuffer( + input.data(), 10, &output_buffer, offset)); + + for (int i = 0; i < 5; i++) { + ASSERT_EQ(0, output[i]); + } + for (int i = 0; i < 10; i++) { + ASSERT_EQ(input[i], output[i + offset]); + } +} + +TEST_F(CryptoTest, CopyToOutputBufferBasicInsecureOverlap) { + std::vector input(32, 0); + for (int i = 0; i < 32; i++) { + input[i] = i; + } + + OPK_OutputBuffer output_buffer; + output_buffer.type = OPK_CLEAR_INSECURE_OUTPUT_BUFFER; + output_buffer.buffer.clear_insecure = input.data(); + output_buffer.size = 16; + + size_t offset = 5; + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_C1_CopyToOutputBuffer( + input.data(), 10, &output_buffer, offset)); + + for (int i = 0; i < 5; i++) { + ASSERT_EQ(i, input[i]); + } + for (int i = 0; i < 10; i++) { + ASSERT_EQ(i, input[i + offset]); + } +} + +TEST_F(CryptoTest, CopyToOutputBufferFailsWithBadInput) { + std::vector input(32, 0); + for (int i = 0; i < 32; i++) { + input[i] = i; + } + + OPK_OutputBuffer output_buffer; + output_buffer.type = OPK_CLEAR_INSECURE_OUTPUT_BUFFER; + output_buffer.buffer.clear_insecure = input.data(); + output_buffer.size = 16; + + size_t offset = 5; + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_CopyToOutputBuffer(NULL, 10, &output_buffer, offset)); + ASSERT_EQ( + OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_CopyToOutputBuffer(input.data(), 0, &output_buffer, offset)); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_CopyToOutputBuffer(input.data(), 10, NULL, offset)); + ASSERT_EQ( + OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_CopyToOutputBuffer(input.data(), 32, &output_buffer, offset)); +} + +TEST_F(CryptoTest, CreateAsymmetricKeyHandleRSA) { + WTPI_AsymmetricKey_Handle handle; + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_CreateAsymmetricKeyHandle( + test_rsa_key_der, TEST_RSA_KEY_DER_LEN, + DRM_RSA_PRIVATE_KEY, &handle)); + ASSERT_NE(nullptr, handle); +} + +TEST_F(CryptoTest, CreateAsymmetricKeyHandleECC) { + EcKeyPtr ec_key; + ASSERT_TRUE(NewEccPrivateKey(&ec_key)); + std::vector serialized_key; + ASSERT_TRUE(SerializeEccPrivateKey(ec_key.get(), &serialized_key)); + + WTPI_AsymmetricKey_Handle key_handle; + OEMCryptoResult result = WTPI_CreateAsymmetricKeyHandle( + serialized_key.data(), serialized_key.size(), DRM_ECC_PRIVATE_KEY, + &key_handle); + ASSERT_EQ(result, OEMCrypto_SUCCESS) << "Failed to create ECC key"; + ASSERT_NE(nullptr, key_handle); +} + +TEST_F(CryptoTest, CreateAsymmetricKeyHandleFailsForBadInput) { + WTPI_AsymmetricKey_Handle handle; + + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_CreateAsymmetricKeyHandle(NULL, TEST_RSA_KEY_DER_LEN, + DRM_RSA_PRIVATE_KEY, &handle)); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_CreateAsymmetricKeyHandle(test_rsa_key_der, 0, + DRM_RSA_PRIVATE_KEY, &handle)); + ASSERT_EQ( + OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_CreateAsymmetricKeyHandle(test_rsa_key_der, TEST_RSA_KEY_DER_LEN, + DRM_RSA_PRIVATE_KEY, NULL)); + + std::vector bad_format; + for (int i = 0; i < 32; i++) { + bad_format.push_back(i); + } + ASSERT_EQ(OEMCrypto_ERROR_INVALID_KEY, + WTPI_CreateAsymmetricKeyHandle(bad_format.data(), 32, + DRM_RSA_PRIVATE_KEY, &handle)); +} + +TEST_F(CryptoTest, RSASign) { + WTPI_AsymmetricKey_Handle handle; + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_CreateAsymmetricKeyHandle( + test_rsa_key_der, TEST_RSA_KEY_DER_LEN, + DRM_RSA_PRIVATE_KEY, &handle)); + + std::string message = "Hello world"; + std::vector input(message.begin(), message.end()); + + std::vector output(256, 0); + size_t output_len = 256; + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_RSASign(handle, input.data(), input.size(), output.data(), + &output_len, kSign_RSASSA_PSS)); + + // Verify with openssl, since we can't use WTPI functions to verify + RSA* rsa = NULL; + ASSERT_TRUE( + DeserializePKCS8PrivateKey(test_rsa_key_der, TEST_RSA_KEY_DER_LEN, &rsa)); + + boringssl_ptr pkey(EVP_PKEY_new()); + ASSERT_EQ(1, EVP_PKEY_set1_RSA(pkey.get(), rsa)); + + EXPECT_TRUE(VerifyPSSSignature(pkey.get(), input.data(), input.size(), + output.data(), output.size())) + << "PSS signature check failed."; +} + +TEST_F(CryptoTest, RSASignFailsWithBadInput) { + WTPI_AsymmetricKey_Handle handle; + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_CreateAsymmetricKeyHandle( + test_rsa_key_der, TEST_RSA_KEY_DER_LEN, + DRM_RSA_PRIVATE_KEY, &handle)); + + std::string message = "Hello world"; + std::vector input(message.begin(), message.end()); + + std::vector output(256, 0); + size_t output_len = 256; + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_RSASign(NULL, input.data(), input.size(), output.data(), + &output_len, kSign_RSASSA_PSS)); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_RSASign(handle, NULL, input.size(), output.data(), &output_len, + kSign_RSASSA_PSS)); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_RSASign(handle, input.data(), 0, output.data(), &output_len, + kSign_RSASSA_PSS)); + + // wtpi_crypto_asymmetric_interface.h specifically mentions that it is okay + // for the |signature| field to be NULL + // + // ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + // WTPI_RSASign(handle, input.data(), input.size(), NULL, + // &output_len, kSign_RSASSA_PSS)); + + output_len = 10; + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + WTPI_RSASign(handle, input.data(), input.size(), output.data(), + &output_len, kSign_RSASSA_PSS)); +} + +TEST_F(CryptoTest, RSADecrypt) { + WTPI_AsymmetricKey_Handle handle; + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_CreateAsymmetricKeyHandle( + test_rsa_key_der, TEST_RSA_KEY_DER_LEN, + DRM_RSA_PRIVATE_KEY, &handle)); + + std::vector output(256, 0); + size_t output_len = 256; + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_RSADecrypt(handle, hello_world_encrypted, HELLO_WORLD_ENC_LEN, + output.data(), &output_len)); + + std::string message = "Hello world!"; + std::vector expected(message.begin(), message.end()); + for (size_t i = 0; i < expected.size(); i++) { + ASSERT_EQ(expected[i], output[i]); + } +} + +TEST_F(CryptoTest, RSADecryptFailsForBadInput) { + WTPI_AsymmetricKey_Handle handle; + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_CreateAsymmetricKeyHandle( + test_rsa_key_der, TEST_RSA_KEY_DER_LEN, + DRM_RSA_PRIVATE_KEY, &handle)); + + std::vector output(256, 0); + size_t output_len = 256; + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_RSADecrypt(NULL, hello_world_encrypted, HELLO_WORLD_ENC_LEN, + output.data(), &output_len)); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_RSADecrypt(handle, NULL, HELLO_WORLD_ENC_LEN, output.data(), + &output_len)); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_RSADecrypt(handle, hello_world_encrypted, 0, output.data(), + &output_len)); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_RSADecrypt(handle, hello_world_encrypted, HELLO_WORLD_ENC_LEN, + NULL, &output_len)); + output_len = 10; + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + WTPI_RSADecrypt(handle, hello_world_encrypted, HELLO_WORLD_ENC_LEN, + output.data(), &output_len)); + ASSERT_EQ((uint32_t)256, output_len); +} + +TEST_F(CryptoTest, GetSignatureSize) { + WTPI_AsymmetricKey_Handle handle; + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_CreateAsymmetricKeyHandle( + test_rsa_key_der, TEST_RSA_KEY_DER_LEN, + DRM_RSA_PRIVATE_KEY, &handle)); + + size_t signature_size = 0; + size_t expected_size = 256; + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_GetSignatureSize(handle, &signature_size)); + ASSERT_EQ(expected_size, signature_size); +} + +TEST_F(CryptoTest, GetSignatureSizeFailsForBadInputs) { + WTPI_AsymmetricKey_Handle handle; + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_CreateAsymmetricKeyHandle( + test_rsa_key_der, TEST_RSA_KEY_DER_LEN, + DRM_RSA_PRIVATE_KEY, &handle)); + + size_t signature_size = 0; + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_GetSignatureSize(NULL, &signature_size)); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_GetSignatureSize(handle, NULL)); +} + +TEST_F(CryptoTest, RandomBytesFailsForBadInputs) { + std::vector out(32, 0); + size_t size = 32; + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, WTPI_C1_RandomBytes(NULL, size)); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_C1_RandomBytes(out.data(), 0)); +} + +TEST_F(CryptoTest, DeriveKeyFromKeyHandleFailsForBadInputs) { + std::vector key; + for (int i = 0; i < 32; i++) { + key.push_back(i); + } + + WTPI_K1_SymmetricKey_Handle key_handle; + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_K1_CreateKeyHandle(key.data(), KEY_SIZE_128, DERIVING_KEY, + &key_handle)); + + uint8_t counter = 0; + const uint8_t context[] = {50, 51, 52, 53}; + size_t context_length = 4; + SymmetricKeyType out_key_type = CONTENT_KEY; + KeySize out_key_size = KEY_SIZE_128; + WTPI_K1_SymmetricKey_Handle out; + + ASSERT_EQ( + OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_K1_DeriveKeyFromKeyHandle(NULL, counter, context, context_length, + out_key_type, out_key_size, &out)); + ASSERT_EQ( + OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_K1_DeriveKeyFromKeyHandle(key_handle, counter, NULL, context_length, + out_key_type, out_key_size, &out)); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_K1_DeriveKeyFromKeyHandle(key_handle, counter, context, 0, + out_key_type, out_key_size, &out)); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_K1_DeriveKeyFromKeyHandle(key_handle, counter, context, + context_length, out_key_type, + out_key_size, NULL)); +} + +TEST_F(CryptoTest, DeriveKeyFromKeyHandleWorks) { + std::vector key; + for (int i = 0; i < 32; i++) { + key.push_back(i); + } + + WTPI_K1_SymmetricKey_Handle key_handle; + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_K1_CreateKeyHandle(key.data(), KEY_SIZE_128, DERIVING_KEY, + &key_handle)); + + const uint8_t context[4] = {'T', 'E', 'S', 'T'}; + + WTPI_K1_SymmetricKey_Handle out_key_handle; + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_K1_DeriveKeyFromKeyHandle(key_handle, 1, context, + sizeof(context), MAC_KEY_CLIENT, + KEY_SIZE_256, &out_key_handle)); + + const uint8_t expected_derived_key[] = {31, 230, 128, 12, 6, 223, 177, 250, + 199, 161, 58, 52, 105, 184, 151, 162, + 131, 204, 51, 13, 29, 230, 183, 214, + 157, 152, 245, 50, 81, 137, 110, 56}; + + WTPI_K1_SymmetricKey_Handle expected_derived_key_handle; + ASSERT_EQ( + OEMCrypto_SUCCESS, + WTPI_K1_CreateKeyHandle(expected_derived_key, KEY_SIZE_256, + MAC_KEY_CLIENT, &expected_derived_key_handle)); + + // perform an operation with out_key_handle and expected_derived_key_handle to + // prove they are using the same underlying key data + std::vector input; + for (int i = 0; i < 32; i++) { + input.push_back(i); + } + std::vector output1(32, 1); + std::vector output2(32, 2); + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_C1_HMAC_SHA256(out_key_handle, input.data(), input.size(), + output1.data())); + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_C1_HMAC_SHA256(expected_derived_key_handle, input.data(), + input.size(), output2.data())); + + for (int i = 0; i < 32; i++) { + ASSERT_EQ(output1[i], output2[i]); + } +} + +TEST_F(CryptoTest, WrapKeyFailsForBadInputs) { + std::vector key; + for (int i = 0; i < 32; i++) { + key.push_back(i); + } + WTPI_K1_SymmetricKey_Handle key_handle; + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_K1_CreateKeyHandle(key.data(), KEY_SIZE_128, DERIVING_KEY, + &key_handle)); + + uint32_t context = 0x1234; + SymmetricKeyType key_type = DERIVING_KEY; + uint8_t wrapped_key[256]; + uint8_t wrapped_key_length = KEY_SIZE_128; + + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_K1_WrapKey(context, NULL, key_type, wrapped_key, + wrapped_key_length)); + ASSERT_EQ( + OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_K1_WrapKey(context, key_handle, key_type, NULL, wrapped_key_length)); +} + +TEST_F(CryptoTest, UnwrapKeyFailsForBadInputs) { + WTPI_K1_SymmetricKey_Handle out_key_handle; + + uint32_t context = 0x1234; + SymmetricKeyType key_type = DERIVING_KEY; + uint8_t wrapped_key[256]; + uint8_t wrapped_key_length = KEY_SIZE_128; + + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_K1_UnwrapIntoKeyHandle(context, NULL, wrapped_key_length, + key_type, &out_key_handle)); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_K1_UnwrapIntoKeyHandle(context, wrapped_key, 7, key_type, + &out_key_handle)); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_K1_UnwrapIntoKeyHandle(context, wrapped_key, + wrapped_key_length, key_type, NULL)); +} + +TEST_F(CryptoTest, WrapAndUnwrapKeyWorks) { + std::vector key; + for (int i = 0; i < 32; i++) { + key.push_back(i); + } + + WTPI_K1_SymmetricKey_Handle key_handle; + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_K1_CreateKeyHandle(key.data(), KEY_SIZE_256, MAC_KEY_CLIENT, + &key_handle)); + + uint32_t context = 0x1234; + SymmetricKeyType key_type = MAC_KEY_CLIENT; + uint8_t wrapped_key[256]; + uint8_t wrapped_key_length = KEY_SIZE_256; + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_K1_WrapKey(context, key_handle, key_type, wrapped_key, + wrapped_key_length)); + + WTPI_K1_SymmetricKey_Handle out_key_handle; + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_K1_UnwrapIntoKeyHandle( + context, wrapped_key, wrapped_key_length, + key_type, &out_key_handle)); + + // Perform the same crypto operation with both key handles to prove the + // unwrapped handle is the same as the wrapped one + std::vector output1(32, 1); + std::vector output2(32, 2); + std::vector input; + for (int i = 0; i < 32; i++) { + input.push_back(i); + } + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_C1_HMAC_SHA256(key_handle, input.data(), input.size(), + output1.data())); + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_C1_HMAC_SHA256(out_key_handle, input.data(), input.size(), + output2.data())); + + for (int i = 0; i < 32; i++) { + ASSERT_EQ(output1[i], output2[i]); + } +} + +TEST_F(CryptoTest, WrapAsymmetricKeyFailsForBadInputs) { + uint8_t output[4000]; + size_t output_length = 4000; + AsymmetricKeyType key_type = DRM_RSA_PRIVATE_KEY; + uint8_t clear_key[256]; + size_t clear_key_length = 256; + + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_WrapAsymmetricKey(NULL, output_length, key_type, clear_key, + clear_key_length)); + ASSERT_EQ( + OEMCrypto_ERROR_SHORT_BUFFER, + WTPI_WrapAsymmetricKey(output, 0, key_type, clear_key, clear_key_length)); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_WrapAsymmetricKey(output, output_length, key_type, NULL, + clear_key_length)); + ASSERT_EQ( + OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_WrapAsymmetricKey(output, output_length, key_type, clear_key, 0)); +} + +TEST_F(CryptoTest, UnwrapAsymmetricKeyFailsForBadInputs) { + uint8_t input[256]; + size_t input_length = 256; + AsymmetricKeyType key_type = DRM_RSA_PRIVATE_KEY; + WTPI_AsymmetricKey_Handle key_handle; + uint32_t allowed_schemes = 0; + + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_UnwrapIntoAsymmetricKeyHandle(NULL, input_length, key_type, + &key_handle, &allowed_schemes)); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_UnwrapIntoAsymmetricKeyHandle(input, 0, key_type, &key_handle, + &allowed_schemes)); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_UnwrapIntoAsymmetricKeyHandle(input, input_length, key_type, + NULL, &allowed_schemes)); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_UnwrapIntoAsymmetricKeyHandle(input, input_length, key_type, + &key_handle, NULL)); +} + +TEST_F(CryptoTest, WrapAndUnwrapAsymmetricKeyWorks) { + WTPI_AsymmetricKey_Handle handle; + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_CreateAsymmetricKeyHandle( + test_rsa_key_der, TEST_RSA_KEY_DER_LEN, + DRM_RSA_PRIVATE_KEY, &handle)); + + size_t buffer_size = 0; + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_GetWrappedAsymmetricKeySize( + TEST_RSA_KEY_DER_LEN, DRM_RSA_PRIVATE_KEY, &buffer_size)); + + std::vector wrapped(buffer_size, 0); + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_WrapAsymmetricKey(wrapped.data(), wrapped.size(), + DRM_RSA_PRIVATE_KEY, test_rsa_key_der, + TEST_RSA_KEY_DER_LEN)); + + WTPI_AsymmetricKey_Handle out_handle; + uint32_t allowed_schemes; + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_UnwrapIntoAsymmetricKeyHandle(wrapped.data(), wrapped.size(), + DRM_RSA_PRIVATE_KEY, &out_handle, + &allowed_schemes)); + + // perform an operation with the two handles to prove they are the same + std::vector decrypted1(256, 0); + std::vector decrypted2(256, 0); + size_t output_len = 256; + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_RSADecrypt(handle, hello_world_encrypted, HELLO_WORLD_ENC_LEN, + decrypted1.data(), &output_len)); + + output_len = 256; + ASSERT_EQ( + OEMCrypto_SUCCESS, + WTPI_RSADecrypt(out_handle, hello_world_encrypted, HELLO_WORLD_ENC_LEN, + decrypted2.data(), &output_len)); + + std::string message = "Hello world!"; + std::vector expected(message.begin(), message.end()); + for (size_t i = 0; i < expected.size(); i++) { + ASSERT_EQ(expected[i], decrypted1[i]); + ASSERT_EQ(expected[i], decrypted2[i]); + } +} + +TEST_F(CryptoTest, GetKeySizeBasic) { + std::vector key; + for (int i = 0; i < 32; i++) { + key.push_back(i); + } + WTPI_K1_SymmetricKey_Handle key_handle; + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_K1_CreateKeyHandle(key.data(), KEY_SIZE_128, CONTENT_KEY, + &key_handle)); + + KeySize size = KEY_SIZE_256; + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_K1_GetKeySize(key_handle, &size)); + ASSERT_EQ(KEY_SIZE_128, size); +} + +TEST_F(CryptoTest, GetKeySizeFailsForBadInput) { + std::vector key; + for (int i = 0; i < 32; i++) { + key.push_back(i); + } + WTPI_K1_SymmetricKey_Handle key_handle; + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_K1_CreateKeyHandle(key.data(), KEY_SIZE_128, CONTENT_KEY, + &key_handle)); + + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_K1_GetKeySize(key_handle, NULL)); +} + +TEST_F(CryptoTest, AESDecryptAndCreateKeyHandleFailsForBadInput) { + std::vector key; + for (int i = 0; i < 32; i++) { + key.push_back(i); + } + + WTPI_K1_SymmetricKey_Handle decrypt_key_handle; + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_K1_CreateKeyHandle(key.data(), KEY_SIZE_128, CONTENT_KEY, + &decrypt_key_handle)); + + std::vector enc_key = {72, 148, 193, 81, 175, 242, 38, 26, + 247, 167, 88, 96, 223, 94, 41, 95}; + std::vector iv = {99, 0, 23, 18, 75, 4, 92, 115, + 24, 70, 56, 57, 12, 43, 15, 29}; + + WTPI_K1_SymmetricKey_Handle out_key_handle; + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_K1_AESDecryptAndCreateKeyHandle( + NULL, enc_key.data(), enc_key.size(), iv.data(), MAC_KEY_CLIENT, + &out_key_handle)); + + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_K1_AESDecryptAndCreateKeyHandle( + decrypt_key_handle, NULL, enc_key.size(), iv.data(), + MAC_KEY_CLIENT, &out_key_handle)); + + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_K1_AESDecryptAndCreateKeyHandle( + decrypt_key_handle, enc_key.data(), 7, iv.data(), + MAC_KEY_CLIENT, &out_key_handle)); + + // TODO(b/205751866): serializer allocates iv array on TEE side regardless if + // REE iv ptr is NULL, so the NULL never propagates to the TEE + // + // ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + // WTPI_K1_AESDecryptAndCreateKeyHandle( + // decrypt_key_handle, enc_key.data(), enc_key.size(), NULL, + // MAC_KEY_CLIENT, &out_key_handle)); + + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_K1_AESDecryptAndCreateKeyHandle( + decrypt_key_handle, enc_key.data(), enc_key.size(), iv.data(), + MAC_KEY_CLIENT, NULL)); +} + +TEST_F(CryptoTest, AESDecryptAndCreateKeyHandleBasic) { + std::vector key; + for (int i = 0; i < 32; i++) { + key.push_back(i); + } + + WTPI_K1_SymmetricKey_Handle expected_key_handle; + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_K1_CreateKeyHandle(key.data(), KEY_SIZE_256, MAC_KEY_CLIENT, + &expected_key_handle)); + + std::vector decryption_key; + for (int i = 0; i < 32; i++) { + decryption_key.push_back(10 + i); + } + + WTPI_K1_SymmetricKey_Handle decryption_key_handle; + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_K1_CreateKeyHandle(decryption_key.data(), KEY_SIZE_256, + CONTENT_KEY, &decryption_key_handle)); + + std::vector iv = {99, 0, 23, 18, 75, 4, 92, 115, + 24, 70, 56, 57, 12, 43, 15, 29}; + + // encrypt the `key` array using AES CBC + size_t key_size = KEY_SIZE_256; + uint8_t iv_buffer[KEY_IV_SIZE]; + std::vector encrypted_key(32, 0); + AES_KEY aes_key; + AES_set_encrypt_key(decryption_key.data(), (unsigned int)(key_size * 8), + &aes_key); + memcpy(iv_buffer, iv.data(), KEY_IV_SIZE); + AES_cbc_encrypt(key.data(), encrypted_key.data(), key.size(), &aes_key, + iv_buffer, AES_ENCRYPT); + + WTPI_K1_SymmetricKey_Handle out_key_handle; + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_K1_AESDecryptAndCreateKeyHandle( + decryption_key_handle, encrypted_key.data(), + encrypted_key.size(), iv.data(), + MAC_KEY_CLIENT, &out_key_handle)); + + // perform the same operation with both keys + std::vector output1(32, 1); + std::vector output2(32, 2); + std::vector input; + for (int i = 0; i < 32; i++) { + input.push_back(i); + } + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_C1_HMAC_SHA256(out_key_handle, input.data(), input.size(), + output1.data())); + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_C1_HMAC_SHA256(expected_key_handle, input.data(), input.size(), + output2.data())); + + for (int i = 0; i < 32; i++) { + ASSERT_EQ(output1[i], output2[i]); + } +} + +TEST_F(CryptoTest, AESDecryptAndCreateKeyHandleForMacKeysFailsForBadInput) { + std::vector enc_mac_keys; + for (int i = 0; i < 64; i++) { + enc_mac_keys.push_back(i); + } + + std::vector decryption_key; + for (int i = 0; i < 32; i++) { + decryption_key.push_back(10 + i); + } + + WTPI_K1_SymmetricKey_Handle decryption_key_handle; + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_K1_CreateKeyHandle(decryption_key.data(), KEY_SIZE_256, + CONTENT_KEY, &decryption_key_handle)); + + std::vector iv = {99, 0, 23, 18, 75, 4, 92, 115, + 24, 70, 56, 57, 12, 43, 15, 29}; + + WTPI_K1_SymmetricKey_Handle out_mac_key_client, out_mac_key_server; + + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_K1_AESDecryptAndCreateKeyHandleForMacKeys( + NULL, enc_mac_keys.data(), enc_mac_keys.size(), iv.data(), + &out_mac_key_server, &out_mac_key_client)); + + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_K1_AESDecryptAndCreateKeyHandleForMacKeys( + decryption_key_handle, NULL, enc_mac_keys.size(), iv.data(), + &out_mac_key_server, &out_mac_key_client)); + + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_K1_AESDecryptAndCreateKeyHandleForMacKeys( + decryption_key_handle, enc_mac_keys.data(), 63, iv.data(), + &out_mac_key_server, &out_mac_key_client)); + + // TODO(b/205751866): serializer allocates iv array on TEE side regardless + // if REE iv ptr is NULL, so the NULL never propagates to the TEE + // + // ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + // WTPI_K1_AESDecryptAndCreateKeyHandleForMacKeys( + // decryption_key_handle, enc_mac_keys.data(), enc_mac_keys.size(), NULL, + // &out_mac_key_server, &out_mac_key_client)); + + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_K1_AESDecryptAndCreateKeyHandleForMacKeys( + decryption_key_handle, enc_mac_keys.data(), enc_mac_keys.size(), + iv.data(), NULL, &out_mac_key_client)); + + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_K1_AESDecryptAndCreateKeyHandleForMacKeys( + decryption_key_handle, enc_mac_keys.data(), enc_mac_keys.size(), + iv.data(), &out_mac_key_server, NULL)); +} + +TEST_F(CryptoTest, AESDecryptAndCreateKeyHandleForMacKeysBasic) { + std::vector in_mac_key_client, in_mac_key_server; + for (int i = 0; i < 32; i++) { + in_mac_key_client.push_back(i); + in_mac_key_server.push_back(i + 50); + } + + WTPI_K1_SymmetricKey_Handle expected_mac_key_client_handle; + ASSERT_EQ( + OEMCrypto_SUCCESS, + WTPI_K1_CreateKeyHandle(in_mac_key_client.data(), KEY_SIZE_256, + MAC_KEY_CLIENT, &expected_mac_key_client_handle)); + + WTPI_K1_SymmetricKey_Handle expected_mac_key_server_handle; + ASSERT_EQ( + OEMCrypto_SUCCESS, + WTPI_K1_CreateKeyHandle(in_mac_key_server.data(), KEY_SIZE_256, + MAC_KEY_SERVER, &expected_mac_key_server_handle)); + + std::vector decryption_key; + for (int i = 0; i < 32; i++) { + decryption_key.push_back(10 + i); + } + + WTPI_K1_SymmetricKey_Handle decryption_key_handle; + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_K1_CreateKeyHandle(decryption_key.data(), KEY_SIZE_256, + CONTENT_KEY, &decryption_key_handle)); + + std::vector iv = {99, 0, 23, 18, 75, 4, 92, 115, + 24, 70, 56, 57, 12, 43, 15, 29}; + + // encrypt the mac keys using AES CBC + size_t key_size = KEY_SIZE_256; + uint8_t iv_buffer[KEY_IV_SIZE]; + std::vector enc_mac_keys(64, 0); + AES_KEY aes_key; + AES_set_encrypt_key(decryption_key.data(), (unsigned int)(key_size * 8), + &aes_key); + memcpy(iv_buffer, iv.data(), KEY_IV_SIZE); + AES_cbc_encrypt(in_mac_key_server.data(), enc_mac_keys.data(), + in_mac_key_server.size(), &aes_key, iv_buffer, AES_ENCRYPT); + AES_cbc_encrypt(in_mac_key_client.data(), enc_mac_keys.data() + 32, + in_mac_key_client.size(), &aes_key, iv_buffer, AES_ENCRYPT); + + WTPI_K1_SymmetricKey_Handle out_mac_key_client_handle, + out_mac_key_server_handle; + ASSERT_EQ( + OEMCrypto_SUCCESS, + WTPI_K1_AESDecryptAndCreateKeyHandleForMacKeys( + decryption_key_handle, enc_mac_keys.data(), enc_mac_keys.size(), + iv.data(), &out_mac_key_server_handle, &out_mac_key_client_handle)); + + // perform the same operation with both keys, check that the output is the + // same + std::vector output1(32, 1); + std::vector output2(32, 2); + std::vector output3(32, 3); + std::vector output4(32, 4); + std::vector input; + for (int i = 0; i < 32; i++) { + input.push_back(i); + } + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_C1_HMAC_SHA256(out_mac_key_client_handle, input.data(), + input.size(), output1.data())); + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_C1_HMAC_SHA256(expected_mac_key_client_handle, input.data(), + input.size(), output2.data())); + + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_C1_HMAC_SHA256(out_mac_key_server_handle, input.data(), + input.size(), output3.data())); + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_C1_HMAC_SHA256(expected_mac_key_server_handle, input.data(), + input.size(), output4.data())); + + for (int i = 0; i < 32; i++) { + ASSERT_EQ(output1[i], output2[i]); + ASSERT_EQ(output3[i], output4[i]); + } +} + +TEST_F(CryptoTest, DeriveDeviceKeyIntoHandleFailsForBadInput) { + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_K1_DeriveDeviceKeyIntoHandle(0x1234, CONTENT_KEY, NULL, + KEY_SIZE_128)); +} + +TEST_F(CryptoTest, ED25519SignSuccess) { + uint8_t public_key[ED25519_PUBLIC_KEY_LEN]; + uint8_t private_key[ED25519_PRIVATE_KEY_LEN]; + ED25519_keypair(public_key, private_key); + const uint8_t message[3] = {'m', 's', 'g'}; + + WTPI_AsymmetricKey_Handle handle; + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_CreateAsymmetricKeyHandle( + private_key, ED25519_PRIVATE_KEY_LEN, + PROV40_ED25519_PRIVATE_KEY, &handle)); + uint8_t signature[ED25519_SIGNATURE_LEN]; + size_t signature_length = sizeof(signature); + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_ED25519Sign(handle, message, sizeof(message), signature, + &signature_length)); + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_FreeAsymmetricKeyHandle(handle)); + + ASSERT_EQ(ED25519_verify(message, sizeof(message), signature, public_key), 1); + ASSERT_EQ(signature_length, size_t(ED25519_SIGNATURE_LEN)); +} + +TEST_F(CryptoTest, ECCSignFailsForBadInput) { + EcKeyPtr ec_key; + ASSERT_TRUE(NewEccPrivateKey(&ec_key)); + std::vector serialized_key; + ASSERT_TRUE(SerializeEccPrivateKey(ec_key.get(), &serialized_key)); + + WTPI_AsymmetricKey_Handle key_handle; + OEMCryptoResult result = WTPI_CreateAsymmetricKeyHandle( + serialized_key.data(), serialized_key.size(), DRM_ECC_PRIVATE_KEY, + &key_handle); + ASSERT_EQ(result, OEMCrypto_SUCCESS) << "Failed to load ECC key"; + + // Generate signature. + size_t signature_size = ECDSA_size(ec_key.get()); + const uint8_t kMessage[] = {'m', 'e', 's', 's', 'a', 'g', 'e'}; + std::vector signature(signature_size, 0); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_ECCSign(NULL, kMessage, sizeof(kMessage), signature.data(), + &signature_size)); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_ECCSign(key_handle, NULL, sizeof(kMessage), signature.data(), + &signature_size)); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, + WTPI_ECCSign(key_handle, kMessage, sizeof(kMessage), + signature.data(), NULL)); +} + +TEST_F(CryptoTest, ECCLoadKeyAndSign) { + EcKeyPtr ec_key; + ASSERT_TRUE(NewEccPrivateKey(&ec_key)); + std::vector serialized_key; + ASSERT_TRUE(SerializeEccPrivateKey(ec_key.get(), &serialized_key)); + + WTPI_AsymmetricKey_Handle key_handle; + OEMCryptoResult result = WTPI_CreateAsymmetricKeyHandle( + serialized_key.data(), serialized_key.size(), DRM_ECC_PRIVATE_KEY, + &key_handle); + ASSERT_EQ(result, OEMCrypto_SUCCESS) << "Failed to load ECC key"; + + // Generate signature. + size_t signature_size = 0; + result = WTPI_GetSignatureSize(key_handle, &signature_size); + ASSERT_EQ(result, OEMCrypto_SUCCESS) << "Failed to get ECC signature size"; + ASSERT_EQ(signature_size, ECDSA_size(ec_key.get())); + const uint8_t kMessage[] = {'m', 'e', 's', 's', 'a', 'g', 'e'}; + std::vector signature(signature_size, 0); + result = WTPI_ECCSign(key_handle, kMessage, sizeof(kMessage), + signature.data(), &signature_size); + ASSERT_EQ(result, OEMCrypto_SUCCESS) << "Failed to perform ECDSA signature"; + signature.resize(signature_size); + + // Verify signature. + EcKeyPtr ec_pub_key; + ASSERT_TRUE(MakeEccPublicKey(ec_key.get(), &ec_pub_key)); + // Widevine's ECDSA protocol for SECP256R1 uses SHA-256 for signatures. + uint8_t digest[SHA256_DIGEST_LENGTH]; + ASSERT_NE(SHA256(kMessage, sizeof(kMessage), digest), nullptr); + constexpr int kDefaultEcdsaType = 0; // Specific to OpenSSL. + constexpr int kSignatureValid = 1; + ASSERT_EQ(ECDSA_verify(kDefaultEcdsaType, digest, SHA256_DIGEST_LENGTH, + signature.data(), static_cast(signature.size()), + ec_pub_key.get()), + kSignatureValid) + << "Signature verification failed"; + + ASSERT_EQ(WTPI_FreeAsymmetricKeyHandle(key_handle), OEMCrypto_SUCCESS); +} + +TEST_F(CryptoTest, ECCKeyExchange) { + // Pre-define some types to help with key clean up. + using WtpiAsymmetircKeyType = + std::remove_pointer::type; + const auto wtpi_asymmetric_key_free = [](WtpiAsymmetircKeyType* key) { + WTPI_FreeAsymmetricKeyHandle(key); + }; + using WtpiAsymmetircKeyPtr = + std::unique_ptr>; + + // Create device private key. + EcKeyPtr device_priv_key; + ASSERT_TRUE(NewEccPrivateKey(&device_priv_key)); + std::vector serialized_key; + ASSERT_TRUE(SerializeEccPrivateKey(device_priv_key.get(), &serialized_key)); + WTPI_AsymmetricKey_Handle key_handle; + OEMCryptoResult result = WTPI_CreateAsymmetricKeyHandle( + serialized_key.data(), serialized_key.size(), DRM_ECC_PRIVATE_KEY, + &key_handle); + ASSERT_EQ(result, OEMCrypto_SUCCESS) << "Failed to load device ECC key"; + // Assign to smare pointer for auto cleanup on failure. + WtpiAsymmetircKeyPtr device_key_handle(key_handle, wtpi_asymmetric_key_free); + key_handle = nullptr; + + // Serialize device public key (view from server). + std::vector device_pub_key_data; + ASSERT_TRUE( + SerializeEccPublicKey(device_priv_key.get(), &device_pub_key_data)); + device_priv_key.reset(); + + // Create server private key. + EcKeyPtr server_priv_key; + ASSERT_TRUE(NewEccPrivateKey(&server_priv_key)); + ASSERT_TRUE(SerializeEccPrivateKey(server_priv_key.get(), &serialized_key)); + result = WTPI_CreateAsymmetricKeyHandle(serialized_key.data(), + serialized_key.size(), + DRM_ECC_PRIVATE_KEY, &key_handle); + ASSERT_EQ(result, OEMCrypto_SUCCESS) << "Failed to load server ECC key"; + WtpiAsymmetircKeyPtr server_key_handle(key_handle, wtpi_asymmetric_key_free); + key_handle = nullptr; + + // Serialize server public key (view from device). + std::vector server_pub_key_data; + ASSERT_TRUE( + SerializeEccPublicKey(server_priv_key.get(), &server_pub_key_data)); + server_priv_key.reset(); + + // Perform key exchange on device. + size_t session_key_size = 0; + result = WTPI_ECCDeriveSessionKey( + device_key_handle.get(), server_pub_key_data.data(), + server_pub_key_data.size(), + /* session_key = */ nullptr, &session_key_size); + ASSERT_EQ(result, OEMCrypto_ERROR_SHORT_BUFFER) + << "Expected session key size to be returned"; + ASSERT_EQ(session_key_size, KEY_SIZE_256); + std::vector device_session_key(session_key_size, 0); + result = WTPI_ECCDeriveSessionKey( + device_key_handle.get(), server_pub_key_data.data(), + server_pub_key_data.size(), device_session_key.data(), &session_key_size); + ASSERT_EQ(result, OEMCrypto_SUCCESS) + << "Failed to derive session key on device"; + + // Perform key exchange on server. + std::vector server_session_key(session_key_size); + result = WTPI_ECCDeriveSessionKey( + server_key_handle.get(), device_pub_key_data.data(), + device_pub_key_data.size(), server_session_key.data(), &session_key_size); + ASSERT_EQ(result, OEMCrypto_SUCCESS) + << "Failed to derive session key on server"; + + ASSERT_EQ(device_session_key, server_session_key) + << "Mismatch between server and device session key"; +} + +TEST_F(CryptoTest, GetBootCertificateChainSuccess) { + const size_t kExpectedBccSize = 180; + std::vector buffer; + buffer.resize(kExpectedBccSize); + size_t buffer_size = buffer.size(); + + OEMCryptoResult result = + WTPI_GetBootCertificateChain(buffer.data(), &buffer_size); + + if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED) { + GTEST_SKIP() << "BCC not supported"; + } + ASSERT_EQ(result, OEMCrypto_SUCCESS); + EXPECT_EQ(buffer_size, kExpectedBccSize); +} + +TEST_F(CryptoTest, GenerateRandomCertificateKeyPairSuccess) { + const size_t kBufferSize = 1000; + AsymmetricKeyType type; + uint8_t public_key[kBufferSize]; + size_t public_key_length = sizeof(public_key); + uint8_t wrapped_private_key[kBufferSize]; + size_t wrapped_private_key_length = sizeof(wrapped_private_key); + + ASSERT_EQ(WTPI_GenerateRandomCertificateKeyPair( + &type, wrapped_private_key, &wrapped_private_key_length, + public_key, &public_key_length), + OEMCrypto_SUCCESS); + EXPECT_EQ(type, DRM_ECC_PRIVATE_KEY); + EXPECT_GT(public_key_length, size_t(0)); + EXPECT_LT(public_key_length, kBufferSize); + EXPECT_GT(wrapped_private_key_length, size_t(0)); + EXPECT_LT(wrapped_private_key_length, kBufferSize); +} + +TEST_F(CryptoTest, WTPI_DeviceKeyCoseSign1Success) { + const uint8_t message[3] = {'m', 's', 'g'}; + uint8_t signature_buffer[1024]; + size_t signature_buffer_size = sizeof(signature_buffer); + + ASSERT_EQ(WTPI_DeviceKeyCoseSign1(message, sizeof(message), signature_buffer, + &signature_buffer_size), + OEMCrypto_SUCCESS); +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/generation_number_interface_test.cpp b/oemcrypto/opk/oemcrypto_ta/wtpi_test/generation_number_interface_test.cpp new file mode 100644 index 0000000..6d6979f --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/generation_number_interface_test.cpp @@ -0,0 +1,59 @@ +// 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 + +#include "OEMCryptoCENC.h" +#include "log.h" +#include "opk_init.h" +#include "wtpi_generation_number_interface.h" + +class GenerationNumberInterfaceTest : public ::testing::Test { + protected: + GenerationNumberInterfaceTest() {} + + void SetUp() override { + ::testing::Test::SetUp(); + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + LOGD("Running test %s.%s", test_info->test_case_name(), test_info->name()); + ASSERT_EQ(true, OPK_Initialize()); + } + + void TearDown() override { + OPK_Terminate(); + ::testing::Test::TearDown(); + } +}; + +TEST_F(GenerationNumberInterfaceTest, SaveLoadGenerationNumber) { + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_PrepareGenerationNumber()); + const uint64_t generation_number = 42; + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_SaveGenerationNumber(generation_number)); + uint64_t generation_number_loaded; + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_LoadGenerationNumber(&generation_number_loaded)); + ASSERT_EQ(generation_number, generation_number_loaded); +} + +TEST_F(GenerationNumberInterfaceTest, LoadGenerationNumberFailsForNullInput) { + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_PrepareGenerationNumber()); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, WTPI_LoadGenerationNumber(NULL)); +} + +class GenerationNumberRebootTest : public GenerationNumberInterfaceTest {}; + +TEST_F(GenerationNumberRebootTest, SaveBeforeReboot) { + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_PrepareGenerationNumber()); + const uint64_t generation_number = 0xaa; + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_SaveGenerationNumber(generation_number)); +} + +TEST_F(GenerationNumberRebootTest, LoadAfterReboot) { + ASSERT_EQ(OEMCrypto_SUCCESS, WTPI_PrepareGenerationNumber()); + uint64_t generation_number_loaded; + ASSERT_EQ(OEMCrypto_SUCCESS, + WTPI_LoadGenerationNumber(&generation_number_loaded)); + ASSERT_EQ((uint64_t)0xaa, generation_number_loaded); +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/GEN_oemcrypto_tee_test_api.c b/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/GEN_oemcrypto_tee_test_api.c new file mode 100644 index 0000000..f78e4f1 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/GEN_oemcrypto_tee_test_api.c @@ -0,0 +1,1308 @@ +/* + * Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine License + * Agreement. + */ + +/* + * This code is auto-generated, do not edit + */ + +#include +#include + +#include "GEN_common_serializer.h" +#include "GEN_ree_serializer.h" +#include "OEMCryptoCENC.h" +#include "api_support.h" +#include "bump_allocator.h" +#include "log_macros.h" +#include "message_debug.h" +#include "ree_special_cases.h" +#include "shared_buffer_allocator.h" +#include "tos_shared_memory_interface.h" +#include "tos_transport_interface.h" +#include "wtpi_crc32_interface.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" +#include "wtpi_crypto_asymmetric_interface.h" + +OEMCryptoResult WTPI_PrepareGenerationNumber(void) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_PrepareGenerationNumber_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_PrepareGenerationNumber_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_LoadGenerationNumber(uint64_t* value) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_LoadGenerationNumber_Request(value); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_LoadGenerationNumber_Response(&response, &result, &value); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_SaveGenerationNumber(uint64_t value) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_SaveGenerationNumber_Request(value); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_SaveGenerationNumber_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_K1_GetKeySize(WTPI_K1_SymmetricKey_Handle key_handle, + KeySize* size) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_K1_GetKeySize_Request(key_handle, size); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_K1_GetKeySize_Response(&response, &result, &size); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +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) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_C1_AESCBCDecrypt_Request(key_handle, key_length, in_buffer, + in_buffer_length, iv, out_buffer); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_C1_AESCBCDecrypt_Response(&response, &result, &in_buffer_length, + &out_buffer); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +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) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_C1_AESCBCEncrypt_Request(key_handle, in_buffer, + in_buffer_length, iv, out_buffer); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_C1_AESCBCEncrypt_Response(&response, &result, &in_buffer_length, + &out_buffer); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_C1_HMAC_SHA1(WTPI_K1_SymmetricKey_Handle key_handle, + const uint8_t* input, size_t input_length, + uint8_t* out_buffer) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_C1_HMAC_SHA1_Request(key_handle, input, input_length, + out_buffer); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_C1_HMAC_SHA1_Response(&response, &result, &out_buffer); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_C1_SHA256(const uint8_t* input, size_t input_length, + uint8_t* out_buffer) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_C1_SHA256_Request(input, input_length, out_buffer); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_C1_SHA256_Response(&response, &result, &out_buffer); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_C1_HMAC_SHA256(WTPI_K1_SymmetricKey_Handle key_handle, + const uint8_t* input, size_t input_length, + uint8_t* out_buffer) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_C1_HMAC_SHA256_Request(key_handle, input, input_length, + out_buffer); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_C1_HMAC_SHA256_Response(&response, &result, &out_buffer); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_C1_HMAC_SHA256_Verify( + WTPI_K1_SymmetricKey_Handle key_handle, const uint8_t* input, + size_t input_length, const uint8_t* signature) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_C1_HMAC_SHA256_Verify_Request(key_handle, input, + input_length, signature); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_C1_HMAC_SHA256_Verify_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_C1_CopyToOutputBuffer(const uint8_t* input, + size_t input_length, + const OPK_OutputBuffer* out, + size_t output_offset) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_C1_CopyToOutputBuffer_Request(input, input_length, out, + output_offset); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_C1_CopyToOutputBuffer_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_C1_RandomBytes(uint8_t* out, size_t out_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_C1_RandomBytes_Request(out, out_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_C1_RandomBytes_Response(&response, &result, &out, &out_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_K1_InitializeKeyManagement(void) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_K1_InitializeKeyManagement_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_K1_InitializeKeyManagement_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_K1_TerminateKeyManagement(void) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_K1_TerminateKeyManagement_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_K1_TerminateKeyManagement_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_K1_CreateKeyHandle( + const uint8_t* input, size_t input_length, SymmetricKeyType key_type, + WTPI_K1_SymmetricKey_Handle* out_key_handle) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_K1_CreateKeyHandle_Request(input, input_length, key_type, + out_key_handle); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_K1_CreateKeyHandle_Response(&response, &result, &out_key_handle); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_K1_DeriveDeviceKeyIntoHandle( + uint32_t context, SymmetricKeyType out_key_type, + WTPI_K1_SymmetricKey_Handle* out_key_handle, KeySize out_key_size) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_K1_DeriveDeviceKeyIntoHandle_Request( + context, out_key_type, out_key_handle, out_key_size); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_K1_DeriveDeviceKeyIntoHandle_Response(&response, &result, + &out_key_handle); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +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) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_K1_AESDecryptAndCreateKeyHandle_Request( + decrypt_key_handle, enc_key, enc_key_length, iv, key_type, + out_key_handle); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_K1_AESDecryptAndCreateKeyHandle_Response(&response, &result, + &out_key_handle); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +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) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_K1_AESDecryptAndCreateKeyHandleForMacKeys_Request( + decrypt_key_handle, enc_mac_keys, enc_mac_keys_length, iv, + out_mac_key_server, out_mac_key_client); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_K1_AESDecryptAndCreateKeyHandleForMacKeys_Response( + &response, &result, &out_mac_key_server, &out_mac_key_client); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + 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) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_K1_DeriveKeyFromKeyHandle_Request( + key_handle, counter, context, context_length, out_key_type, out_key_size, + out_key_handle); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_K1_DeriveKeyFromKeyHandle_Response(&response, &result, + &out_key_handle); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +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) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_K1_WrapKey_Request(context, key_handle, key_type, + wrapped_key, wrapped_key_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_K1_WrapKey_Response(&response, &result, &wrapped_key, + &wrapped_key_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +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) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_K1_UnwrapIntoKeyHandle_Request( + context, wrapped_key, wrapped_key_length, key_type, out_key_handle); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_K1_UnwrapIntoKeyHandle_Response(&response, &result, + &out_key_handle); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_K1_FreeKeyHandle(WTPI_K1_SymmetricKey_Handle key_handle) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_K1_FreeKeyHandle_Request(key_handle); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_K1_FreeKeyHandle_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_CreateAsymmetricKeyHandle( + const uint8_t* input, size_t input_length, AsymmetricKeyType key_type, + WTPI_AsymmetricKey_Handle* key_handle) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_CreateAsymmetricKeyHandle_Request(input, input_length, + key_type, key_handle); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_CreateAsymmetricKeyHandle_Response(&response, &result, + &key_handle); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_UnwrapIntoAsymmetricKeyHandle( + const uint8_t* input, size_t input_length, AsymmetricKeyType key_type, + WTPI_AsymmetricKey_Handle* key_handle, uint32_t* allowed_schemes) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_UnwrapIntoAsymmetricKeyHandle_Request( + input, input_length, key_type, key_handle, allowed_schemes); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_UnwrapIntoAsymmetricKeyHandle_Response( + &response, &result, &key_handle, &allowed_schemes); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_FreeAsymmetricKeyHandle( + WTPI_AsymmetricKey_Handle key_handle) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_FreeAsymmetricKeyHandle_Request(key_handle); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_FreeAsymmetricKeyHandle_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_GetWrappedAsymmetricKeySize(size_t enc_private_key_length, + AsymmetricKeyType key_type, + size_t* buffer_size) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GetWrappedAsymmetricKeySize_Request(enc_private_key_length, + key_type, buffer_size); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GetWrappedAsymmetricKeySize_Response(&response, &result, + &buffer_size); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_WrapAsymmetricKey(uint8_t* output, size_t output_length, + AsymmetricKeyType key_type, + const uint8_t* clear_key, + size_t clear_key_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_WrapAsymmetricKey_Request(output, output_length, key_type, + clear_key, clear_key_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_WrapAsymmetricKey_Response(&response, &result, &output, + &output_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +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) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_RSASign_Request(key, message, message_length, signature, + signature_length, padding_scheme); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_RSASign_Response(&response, &result, &signature, + &signature_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_RSADecrypt(WTPI_AsymmetricKey_Handle key, + const uint8_t* input, size_t input_length, + uint8_t* out, size_t* out_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = + OPK_Pack_RSADecrypt_Request(key, input, input_length, out, out_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_RSADecrypt_Response(&response, &result, &out, &out_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_ECCSign(WTPI_AsymmetricKey_Handle key, + const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_ECCSign_Request(key, message, message_length, signature, + signature_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_ECCSign_Response(&response, &result, &signature, + &signature_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +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) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_ECCDeriveSessionKey_Request( + key, key_source, key_source_length, session_key, session_key_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_ECCDeriveSessionKey_Response(&response, &result, &session_key, + &session_key_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_GetSignatureSize(WTPI_AsymmetricKey_Handle key, + size_t* signature_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GetSignatureSize_Request(key, signature_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GetSignatureSize_Response(&response, &result, &signature_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_ED25519Sign(WTPI_AsymmetricKey_Handle key, + const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_ED25519Sign_Request(key, message, message_length, + signature, signature_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_ED25519Sign_Response(&response, &result, &signature, + &signature_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_GetBootCertificateChain(uint8_t* out, size_t* out_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GetBootCertificateChain_Request(out, out_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GetBootCertificateChain_Response(&response, &result, &out, + &out_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + 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) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GenerateRandomCertificateKeyPair_Request( + key_type, wrapped_private_key, wrapped_private_key_length, public_key, + public_key_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GenerateRandomCertificateKeyPair_Response( + &response, &result, &key_type, &wrapped_private_key, + &wrapped_private_key_length, &public_key, &public_key_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_DeviceKeyCoseSign1(const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_DeviceKeyCoseSign1_Request(message, message_length, + signature, signature_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_DeviceKeyCoseSign1_Response(&response, &result, &signature, + &signature_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_Crc32Init(uint32_t* initial_hash) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_Crc32Init_Request(initial_hash); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_Crc32Init_Response(&response, &result, &initial_hash); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_Crc32Cont(const uint8_t* in, size_t in_length, + uint32_t prev_crc, uint32_t* new_crc) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_Crc32Cont_Request(in, in_length, prev_crc, new_crc); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_Crc32Cont_Response(&response, &result, &new_crc); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_Crc32Cont_OutputBuffer(const OPK_OutputBuffer* in, + size_t in_offset, size_t in_length, + uint32_t prev_crc, + uint32_t* new_crc) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_Crc32Cont_OutputBuffer_Request(in, in_offset, in_length, + prev_crc, new_crc); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_Crc32Cont_OutputBuffer_Response(&response, &result, &new_crc); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_GetTrustedTime(uint64_t* time_in_s) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GetTrustedTime_Request(time_in_s); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GetTrustedTime_Response(&response, &result, &time_in_s); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_InitializeClock(void) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_InitializeClock_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_InitializeClock_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCryptoResult WTPI_TerminateClock(void) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_TerminateClock_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_TerminateClock_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCrypto_Clock_Security_Level WTPI_GetClockType(void) { + pthread_mutex_lock(&api_lock); + OEMCrypto_Clock_Security_Level result = kInsecureClock; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GetClockType_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GetClockType_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + pthread_mutex_unlock(&api_lock); + return result; +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/GEN_ree_serializer.c b/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/GEN_ree_serializer.c new file mode 100644 index 0000000..0d2cb01 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/GEN_ree_serializer.c @@ -0,0 +1,1523 @@ +/* + * Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine License + * Agreement. + */ + +/* + * This code is auto-generated, do not edit + */ +#include "GEN_ree_serializer.h" + +#include +#include + +#include "GEN_common_serializer.h" +#include "bump_allocator.h" +#include "common_special_cases.h" +#include "log_macros.h" +#include "marshaller_base.h" +#include "odk_overflow.h" +#include "opk_serialization_base.h" +#include "ree_special_cases.h" +#include "shared_buffer_allocator.h" +#include "tos_transport_interface.h" +#include "wtpi_crc32_interface.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" +#include "wtpi_crypto_asymmetric_interface.h" + +ODK_Message OPK_Pack_PrepareGenerationNumber_Request(void) { + uint32_t api_value = 10000; /* from _tee10000 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_PrepareGenerationNumber_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10000) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_LoadGenerationNumber_Request(const uint64_t* value) { + uint32_t api_value = 10001; /* from _tee10001 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackIsNull(&msg, value); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadGenerationNumber_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint64_t** value) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10001) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_uint64_t(msg, value); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_SaveGenerationNumber_Request(uint64_t value) { + uint32_t api_value = 10002; /* from _tee10002 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_uint64_t(&msg, &value); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_SaveGenerationNumber_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10002) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_K1_GetKeySize_Request( + WTPI_K1_SymmetricKey_Handle key_handle, const KeySize* size) { + uint32_t api_value = 10003; /* from _tee10003 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_WTPI_K1_SymmetricKey_Handle(&msg, &key_handle); + OPK_PackIsNull(&msg, size); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_K1_GetKeySize_Response(ODK_Message* msg, + OEMCryptoResult* result, + KeySize** size) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10003) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_KeySize(msg, size); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_C1_AESCBCDecrypt_Request( + WTPI_K1_SymmetricKey_Handle key_handle, size_t key_length, + const uint8_t* in_buffer, size_t in_buffer_length, const uint8_t* iv, + const uint8_t* out_buffer) { + uint32_t api_value = 10004; /* from _tee10004 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &in_buffer_length); + OPK_Pack_WTPI_K1_SymmetricKey_Handle(&msg, &key_handle); + OPK_Pack_size_t(&msg, &key_length); + OPK_PackMemory(&msg, (const uint8_t*)in_buffer, + OPK_ToLengthType(in_buffer_length)); + OPK_PackArray(&msg, &iv[0], 16); + OPK_PackAlloc(&msg, out_buffer); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_C1_AESCBCDecrypt_Response(ODK_Message* msg, + OEMCryptoResult* result, + size_t* in_buffer_length, + uint8_t** out_buffer) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10004) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_size_t(msg, in_buffer_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtr(in_buffer_length)); + if (p && *out_buffer) { + memcpy(*out_buffer, p, OPK_SafeDerefSizeTPtr(in_buffer_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_C1_AESCBCEncrypt_Request( + WTPI_K1_SymmetricKey_Handle key_handle, const uint8_t* in_buffer, + size_t in_buffer_length, const uint8_t* iv, const uint8_t* out_buffer) { + uint32_t api_value = 10005; /* from _tee10005 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &in_buffer_length); + OPK_Pack_WTPI_K1_SymmetricKey_Handle(&msg, &key_handle); + OPK_PackMemory(&msg, (const uint8_t*)in_buffer, + OPK_ToLengthType(in_buffer_length)); + OPK_PackArray(&msg, &iv[0], 16); + OPK_PackAlloc(&msg, out_buffer); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_C1_AESCBCEncrypt_Response(ODK_Message* msg, + OEMCryptoResult* result, + size_t* in_buffer_length, + uint8_t** out_buffer) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10005) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_size_t(msg, in_buffer_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtr(in_buffer_length)); + if (p && *out_buffer) { + memcpy(*out_buffer, p, OPK_SafeDerefSizeTPtr(in_buffer_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +void OPK_Unpack_C1_HMAC_SHA256_Verify_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10009) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_C1_CopyToOutputBuffer_Request(const uint8_t* input, + size_t input_length, + const OPK_OutputBuffer* out, + size_t output_offset) { + uint32_t api_value = 10010; /* from _tee10010 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &input_length); + OPK_PackMemory(&msg, (const uint8_t*)input, OPK_ToLengthType(input_length)); + OPK_PackNullable_OPK_OutputBuffer(&msg, out); + OPK_Pack_size_t(&msg, &output_offset); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_C1_CopyToOutputBuffer_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10010) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_C1_RandomBytes_Request(const uint8_t* out, + size_t out_length) { + uint32_t api_value = 10011; /* from _tee10011 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &out_length); + OPK_PackAlloc(&msg, out); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_C1_RandomBytes_Response(ODK_Message* msg, + OEMCryptoResult* result, uint8_t** out, + size_t* out_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10011) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_size_t(msg, out_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtr(out_length)); + if (p && *out) { + memcpy(*out, p, OPK_SafeDerefSizeTPtr(out_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_K1_InitializeKeyManagement_Request(void) { + uint32_t api_value = 10012; /* from _tee10012 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_K1_InitializeKeyManagement_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10012) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_K1_TerminateKeyManagement_Request(void) { + uint32_t api_value = 10013; /* from _tee10013 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_K1_TerminateKeyManagement_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10013) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_K1_CreateKeyHandle_Request( + const uint8_t* input, size_t input_length, SymmetricKeyType key_type, + const WTPI_K1_SymmetricKey_Handle* out_key_handle) { + uint32_t api_value = 10014; /* from _tee10014 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &input_length); + OPK_PackMemory(&msg, (const uint8_t*)input, OPK_ToLengthType(input_length)); + OPK_Pack_SymmetricKeyType(&msg, &key_type); + OPK_PackIsNull(&msg, out_key_handle); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_K1_CreateKeyHandle_Response( + ODK_Message* msg, OEMCryptoResult* result, + WTPI_K1_SymmetricKey_Handle** out_key_handle) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10014) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_WTPI_K1_SymmetricKey_Handle(msg, out_key_handle); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_K1_DeriveDeviceKeyIntoHandle_Request( + uint32_t context, SymmetricKeyType out_key_type, + const WTPI_K1_SymmetricKey_Handle* out_key_handle, KeySize out_key_size) { + uint32_t api_value = 10015; /* from _tee10015 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_uint32_t(&msg, &context); + OPK_Pack_SymmetricKeyType(&msg, &out_key_type); + OPK_PackIsNull(&msg, out_key_handle); + OPK_Pack_KeySize(&msg, &out_key_size); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_K1_DeriveDeviceKeyIntoHandle_Response( + ODK_Message* msg, OEMCryptoResult* result, + WTPI_K1_SymmetricKey_Handle** out_key_handle) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10015) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_WTPI_K1_SymmetricKey_Handle(msg, out_key_handle); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_K1_AESDecryptAndCreateKeyHandle_Request( + WTPI_K1_SymmetricKey_Handle decrypt_key_handle, const uint8_t* enc_key, + size_t enc_key_length, const uint8_t* iv, SymmetricKeyType key_type, + const WTPI_K1_SymmetricKey_Handle* out_key_handle) { + uint32_t api_value = 10016; /* from _tee10016 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &enc_key_length); + OPK_Pack_WTPI_K1_SymmetricKey_Handle(&msg, &decrypt_key_handle); + OPK_PackMemory(&msg, (const uint8_t*)enc_key, + OPK_ToLengthType(enc_key_length)); + OPK_PackArray(&msg, &iv[0], 16); + OPK_Pack_SymmetricKeyType(&msg, &key_type); + OPK_PackIsNull(&msg, out_key_handle); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_K1_AESDecryptAndCreateKeyHandle_Response( + ODK_Message* msg, OEMCryptoResult* result, + WTPI_K1_SymmetricKey_Handle** out_key_handle) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10016) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_WTPI_K1_SymmetricKey_Handle(msg, out_key_handle); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_K1_AESDecryptAndCreateKeyHandleForMacKeys_Request( + WTPI_K1_SymmetricKey_Handle decrypt_key_handle, const uint8_t* enc_mac_keys, + size_t enc_mac_keys_length, const uint8_t* iv, + const WTPI_K1_SymmetricKey_Handle* out_mac_key_server, + const WTPI_K1_SymmetricKey_Handle* out_mac_key_client) { + uint32_t api_value = 10017; /* from _tee10017 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &enc_mac_keys_length); + OPK_Pack_WTPI_K1_SymmetricKey_Handle(&msg, &decrypt_key_handle); + OPK_PackMemory(&msg, (const uint8_t*)enc_mac_keys, + OPK_ToLengthType(enc_mac_keys_length)); + OPK_PackArray(&msg, &iv[0], 16); + OPK_PackIsNull(&msg, out_mac_key_server); + OPK_PackIsNull(&msg, out_mac_key_client); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_K1_AESDecryptAndCreateKeyHandleForMacKeys_Response( + ODK_Message* msg, OEMCryptoResult* result, + WTPI_K1_SymmetricKey_Handle** out_mac_key_server, + WTPI_K1_SymmetricKey_Handle** out_mac_key_client) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10017) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_WTPI_K1_SymmetricKey_Handle(msg, out_mac_key_server); + OPK_UnpackNullable_WTPI_K1_SymmetricKey_Handle(msg, out_mac_key_client); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_K1_DeriveKeyFromKeyHandle_Request( + 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, + const WTPI_K1_SymmetricKey_Handle* out_key_handle) { + uint32_t api_value = 10018; /* from _tee10018 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &context_length); + OPK_Pack_WTPI_K1_SymmetricKey_Handle(&msg, &key_handle); + OPK_Pack_uint8_t(&msg, &counter); + OPK_PackMemory(&msg, (const uint8_t*)context, + OPK_ToLengthType(context_length)); + OPK_Pack_SymmetricKeyType(&msg, &out_key_type); + OPK_Pack_KeySize(&msg, &out_key_size); + OPK_PackIsNull(&msg, out_key_handle); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_K1_DeriveKeyFromKeyHandle_Response( + ODK_Message* msg, OEMCryptoResult* result, + WTPI_K1_SymmetricKey_Handle** out_key_handle) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10018) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_WTPI_K1_SymmetricKey_Handle(msg, out_key_handle); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_K1_WrapKey_Request(uint32_t context, + WTPI_K1_SymmetricKey_Handle key_handle, + SymmetricKeyType key_type, + const uint8_t* wrapped_key, + size_t wrapped_key_length) { + uint32_t api_value = 10019; /* from _tee10019 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &wrapped_key_length); + OPK_Pack_uint32_t(&msg, &context); + OPK_Pack_WTPI_K1_SymmetricKey_Handle(&msg, &key_handle); + OPK_Pack_SymmetricKeyType(&msg, &key_type); + OPK_PackAlloc(&msg, wrapped_key); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_K1_WrapKey_Response(ODK_Message* msg, OEMCryptoResult* result, + uint8_t** wrapped_key, + size_t* wrapped_key_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10019) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_size_t(msg, wrapped_key_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtr(wrapped_key_length)); + if (p && *wrapped_key) { + memcpy(*wrapped_key, p, OPK_SafeDerefSizeTPtr(wrapped_key_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_K1_UnwrapIntoKeyHandle_Request( + uint32_t context, const uint8_t* wrapped_key, size_t wrapped_key_length, + SymmetricKeyType key_type, + const WTPI_K1_SymmetricKey_Handle* out_key_handle) { + uint32_t api_value = 10020; /* from _tee10020 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &wrapped_key_length); + OPK_Pack_uint32_t(&msg, &context); + OPK_PackMemory(&msg, (const uint8_t*)wrapped_key, + OPK_ToLengthType(wrapped_key_length)); + OPK_Pack_SymmetricKeyType(&msg, &key_type); + OPK_PackIsNull(&msg, out_key_handle); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_K1_UnwrapIntoKeyHandle_Response( + ODK_Message* msg, OEMCryptoResult* result, + WTPI_K1_SymmetricKey_Handle** out_key_handle) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10020) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_WTPI_K1_SymmetricKey_Handle(msg, out_key_handle); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_K1_FreeKeyHandle_Request( + WTPI_K1_SymmetricKey_Handle key_handle) { + uint32_t api_value = 10021; /* from _tee10021 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_WTPI_K1_SymmetricKey_Handle(&msg, &key_handle); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_K1_FreeKeyHandle_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10021) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_CreateAsymmetricKeyHandle_Request( + const uint8_t* input, size_t input_length, AsymmetricKeyType key_type, + const WTPI_AsymmetricKey_Handle* key_handle) { + uint32_t api_value = 10022; /* from _tee10022 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &input_length); + OPK_PackMemory(&msg, (const uint8_t*)input, OPK_ToLengthType(input_length)); + OPK_Pack_AsymmetricKeyType(&msg, &key_type); + OPK_PackIsNull(&msg, key_handle); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_CreateAsymmetricKeyHandle_Response( + ODK_Message* msg, OEMCryptoResult* result, + WTPI_AsymmetricKey_Handle** key_handle) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10022) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_WTPI_AsymmetricKey_Handle(msg, key_handle); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_UnwrapIntoAsymmetricKeyHandle_Request( + const uint8_t* input, size_t input_length, AsymmetricKeyType key_type, + const WTPI_AsymmetricKey_Handle* key_handle, + const uint32_t* allowed_schemes) { + uint32_t api_value = 10023; /* from _tee10023 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &input_length); + OPK_PackMemory(&msg, (const uint8_t*)input, OPK_ToLengthType(input_length)); + OPK_Pack_AsymmetricKeyType(&msg, &key_type); + OPK_PackIsNull(&msg, key_handle); + OPK_PackIsNull(&msg, allowed_schemes); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_UnwrapIntoAsymmetricKeyHandle_Response( + ODK_Message* msg, OEMCryptoResult* result, + WTPI_AsymmetricKey_Handle** key_handle, uint32_t** allowed_schemes) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10023) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_WTPI_AsymmetricKey_Handle(msg, key_handle); + OPK_UnpackNullable_uint32_t(msg, allowed_schemes); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_FreeAsymmetricKeyHandle_Request( + WTPI_AsymmetricKey_Handle key_handle) { + uint32_t api_value = 10024; /* from _tee10024 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_WTPI_AsymmetricKey_Handle(&msg, &key_handle); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_FreeAsymmetricKeyHandle_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10024) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_GetWrappedAsymmetricKeySize_Request( + size_t enc_private_key_length, AsymmetricKeyType key_type, + const size_t* buffer_size) { + uint32_t api_value = 10025; /* from _tee10025 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &enc_private_key_length); + OPK_Pack_AsymmetricKeyType(&msg, &key_type); + OPK_PackIsNull(&msg, buffer_size); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetWrappedAsymmetricKeySize_Response(ODK_Message* msg, + OEMCryptoResult* result, + size_t** buffer_size) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10025) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_size_t(msg, buffer_size); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_WrapAsymmetricKey_Request(const uint8_t* output, + size_t output_length, + AsymmetricKeyType key_type, + const uint8_t* clear_key, + size_t clear_key_length) { + uint32_t api_value = 10026; /* from _tee10026 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &output_length); + OPK_Pack_size_t(&msg, &clear_key_length); + OPK_PackAlloc(&msg, output); + OPK_Pack_AsymmetricKeyType(&msg, &key_type); + OPK_PackMemory(&msg, (const uint8_t*)clear_key, + OPK_ToLengthType(clear_key_length)); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_WrapAsymmetricKey_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** output, + size_t* output_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10026) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_size_t(msg, output_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtr(output_length)); + if (p && *output) { + memcpy(*output, p, OPK_SafeDerefSizeTPtr(output_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_RSASign_Request(WTPI_AsymmetricKey_Handle key, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + const size_t* signature_length, + RSA_Padding_Scheme padding_scheme) { + uint32_t api_value = 10027; /* from _tee10027 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &message_length); + OPK_PackNullable_size_t(&msg, signature_length); + OPK_Pack_WTPI_AsymmetricKey_Handle(&msg, &key); + OPK_PackMemory(&msg, (const uint8_t*)message, + OPK_ToLengthType(message_length)); + OPK_PackAlloc(&msg, signature); + OPK_Pack_RSA_Padding_Scheme(&msg, &padding_scheme); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_RSASign_Response(ODK_Message* msg, OEMCryptoResult* result, + uint8_t** signature, + size_t** signature_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10027) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, signature_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(signature_length)); + if (p && *signature) { + memcpy(*signature, p, OPK_SafeDerefSizeTPtrPtr(signature_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_RSADecrypt_Request(WTPI_AsymmetricKey_Handle key, + const uint8_t* input, + size_t input_length, const uint8_t* out, + const size_t* out_length) { + uint32_t api_value = 10028; /* from _tee10028 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &input_length); + OPK_PackNullable_size_t(&msg, out_length); + OPK_Pack_WTPI_AsymmetricKey_Handle(&msg, &key); + OPK_PackMemory(&msg, (const uint8_t*)input, OPK_ToLengthType(input_length)); + OPK_PackAlloc(&msg, out); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_RSADecrypt_Response(ODK_Message* msg, OEMCryptoResult* result, + uint8_t** out, size_t** out_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10028) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, out_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(out_length)); + if (p && *out) { + memcpy(*out, p, OPK_SafeDerefSizeTPtrPtr(out_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_ECCSign_Request(WTPI_AsymmetricKey_Handle key, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + const size_t* signature_length) { + uint32_t api_value = 10029; /* from _tee10029 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &message_length); + OPK_PackNullable_size_t(&msg, signature_length); + OPK_Pack_WTPI_AsymmetricKey_Handle(&msg, &key); + OPK_PackMemory(&msg, (const uint8_t*)message, + OPK_ToLengthType(message_length)); + OPK_PackAlloc(&msg, signature); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_ECCSign_Response(ODK_Message* msg, OEMCryptoResult* result, + uint8_t** signature, + size_t** signature_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10029) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, signature_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(signature_length)); + if (p && *signature) { + memcpy(*signature, p, OPK_SafeDerefSizeTPtrPtr(signature_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_ECCDeriveSessionKey_Request( + WTPI_AsymmetricKey_Handle key, const uint8_t* key_source, + size_t key_source_length, const uint8_t* session_key, + const size_t* session_key_length) { + uint32_t api_value = 10030; /* from _tee10030 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &key_source_length); + OPK_PackNullable_size_t(&msg, session_key_length); + OPK_Pack_WTPI_AsymmetricKey_Handle(&msg, &key); + OPK_PackMemory(&msg, (const uint8_t*)key_source, + OPK_ToLengthType(key_source_length)); + OPK_PackAlloc(&msg, session_key); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_ECCDeriveSessionKey_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** session_key, + size_t** session_key_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10030) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, session_key_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(session_key_length)); + if (p && *session_key) { + memcpy(*session_key, p, OPK_SafeDerefSizeTPtrPtr(session_key_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_GetSignatureSize_Request(WTPI_AsymmetricKey_Handle key, + const size_t* signature_length) { + uint32_t api_value = 10031; /* from _tee10031 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_WTPI_AsymmetricKey_Handle(&msg, &key); + OPK_PackIsNull(&msg, signature_length); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetSignatureSize_Response(ODK_Message* msg, + OEMCryptoResult* result, + size_t** signature_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10031) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_size_t(msg, signature_length); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_ED25519Sign_Request(WTPI_AsymmetricKey_Handle key, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + const size_t* signature_length) { + uint32_t api_value = 10032; /* from _tee10032 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &message_length); + OPK_PackNullable_size_t(&msg, signature_length); + OPK_Pack_WTPI_AsymmetricKey_Handle(&msg, &key); + OPK_PackMemory(&msg, (const uint8_t*)message, + OPK_ToLengthType(message_length)); + OPK_PackAlloc(&msg, signature); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_ED25519Sign_Response(ODK_Message* msg, OEMCryptoResult* result, + uint8_t** signature, + size_t** signature_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10032) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, signature_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(signature_length)); + if (p && *signature) { + memcpy(*signature, p, OPK_SafeDerefSizeTPtrPtr(signature_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_GetBootCertificateChain_Request(const uint8_t* out, + const size_t* out_length) { + uint32_t api_value = 10033; /* from _tee10033 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackNullable_size_t(&msg, out_length); + OPK_PackAlloc(&msg, out); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetBootCertificateChain_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** out, + size_t** out_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10033) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, out_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(out_length)); + if (p && *out) { + memcpy(*out, p, OPK_SafeDerefSizeTPtrPtr(out_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_GenerateRandomCertificateKeyPair_Request( + const AsymmetricKeyType* key_type, const uint8_t* wrapped_private_key, + const size_t* wrapped_private_key_length, const uint8_t* public_key, + const size_t* public_key_length) { + uint32_t api_value = 10034; /* from _tee10034 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackNullable_size_t(&msg, wrapped_private_key_length); + OPK_PackNullable_size_t(&msg, public_key_length); + OPK_PackIsNull(&msg, key_type); + OPK_PackAlloc(&msg, wrapped_private_key); + OPK_PackAlloc(&msg, public_key); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GenerateRandomCertificateKeyPair_Response( + ODK_Message* msg, OEMCryptoResult* result, AsymmetricKeyType** key_type, + uint8_t** wrapped_private_key, size_t** wrapped_private_key_length, + uint8_t** public_key, size_t** public_key_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10034) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, wrapped_private_key_length); + OPK_UnpackNullable_size_t(msg, public_key_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_AsymmetricKeyType(msg, key_type); + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(wrapped_private_key_length)); + if (p && *wrapped_private_key) { + memcpy(*wrapped_private_key, p, + OPK_SafeDerefSizeTPtrPtr(wrapped_private_key_length)); + } + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(public_key_length)); + if (p && *public_key) { + memcpy(*public_key, p, OPK_SafeDerefSizeTPtrPtr(public_key_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_DeviceKeyCoseSign1_Request( + const uint8_t* message, size_t message_length, const uint8_t* signature, + const size_t* signature_length) { + uint32_t api_value = 10035; /* from _tee10035 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &message_length); + OPK_PackNullable_size_t(&msg, signature_length); + OPK_PackMemory(&msg, (const uint8_t*)message, + OPK_ToLengthType(message_length)); + OPK_PackAlloc(&msg, signature); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_DeviceKeyCoseSign1_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** signature, + size_t** signature_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10035) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, signature_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(signature_length)); + if (p && *signature) { + memcpy(*signature, p, OPK_SafeDerefSizeTPtrPtr(signature_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_Crc32Init_Request(const uint32_t* initial_hash) { + uint32_t api_value = 10036; /* from _tee10036 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackIsNull(&msg, initial_hash); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Crc32Init_Response(ODK_Message* msg, OEMCryptoResult* result, + uint32_t** initial_hash) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10036) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_uint32_t(msg, initial_hash); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_Crc32Cont_Request(const uint8_t* in, size_t in_length, + uint32_t prev_crc, + const uint32_t* new_crc) { + uint32_t api_value = 10037; /* from _tee10037 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &in_length); + OPK_PackMemory(&msg, (const uint8_t*)in, OPK_ToLengthType(in_length)); + OPK_Pack_uint32_t(&msg, &prev_crc); + OPK_PackIsNull(&msg, new_crc); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Crc32Cont_Response(ODK_Message* msg, OEMCryptoResult* result, + uint32_t** new_crc) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10037) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_uint32_t(msg, new_crc); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_Crc32Cont_OutputBuffer_Request(const OPK_OutputBuffer* in, + size_t in_offset, + size_t in_length, + uint32_t prev_crc, + const uint32_t* new_crc) { + uint32_t api_value = 10038; /* from _tee10038 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &in_length); + /* pack object array with packer function OPK_Pack_OPK_OutputBuffer */ + ODK_Message* const odk_message = &msg; + const void* const objs = (const void*)in; + const LengthType count = OPK_ToLengthType(in_length); + const size_t size = sizeof(OPK_OutputBuffer); + const bool is_null = objs == NULL || OPK_LengthIsNull(count); + if (!OPK_PackBoolValue(odk_message, is_null)) { + for (size_t i = 0; i < OPK_ToSizeT(count); i++) { + OPK_Pack_OPK_OutputBuffer(odk_message, + (const OPK_OutputBuffer*)(objs + i * size)); + } + } + OPK_Pack_size_t(&msg, &in_offset); + OPK_Pack_uint32_t(&msg, &prev_crc); + OPK_PackIsNull(&msg, new_crc); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Crc32Cont_OutputBuffer_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint32_t** new_crc) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10038) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_uint32_t(msg, new_crc); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_GetTrustedTime_Request(const uint64_t* time_in_s) { + uint32_t api_value = 10039; /* from _tee10039 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackIsNull(&msg, time_in_s); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetTrustedTime_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint64_t** time_in_s) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10039) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_uint64_t(msg, time_in_s); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_InitializeClock_Request(void) { + uint32_t api_value = 10040; /* from _tee10040 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_InitializeClock_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10040) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_TerminateClock_Request(void) { + uint32_t api_value = 10041; /* from _tee10041 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_TerminateClock_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10041) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_GetClockType_Request(void) { + uint32_t api_value = 10042; /* from _tee10042 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetClockType_Response(ODK_Message* msg, + OEMCrypto_Clock_Security_Level* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10042) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_OEMCrypto_Clock_Security_Level(msg, result); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/GEN_ree_serializer.h b/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/GEN_ree_serializer.h new file mode 100644 index 0000000..40a9fa5 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/GEN_ree_serializer.h @@ -0,0 +1,256 @@ +/* + * Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine License + * Agreement. + */ + +/* + * This code is auto-generated, do not edit + */ +#ifndef OPK_REE_SERIALIZER_H_ +#define OPK_REE_SERIALIZER_H_ + +#include "log_macros.h" +#include "opk_serialization_base.h" +#include "wtpi_crc32_interface.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" +#include "wtpi_crypto_asymmetric_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +ODK_Message OPK_Pack_PrepareGenerationNumber_Request(void); +void OPK_Unpack_PrepareGenerationNumber_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_LoadGenerationNumber_Request(const uint64_t* value); +void OPK_Unpack_LoadGenerationNumber_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint64_t** value); +ODK_Message OPK_Pack_SaveGenerationNumber_Request(uint64_t value); +void OPK_Unpack_SaveGenerationNumber_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_K1_GetKeySize_Request( + WTPI_K1_SymmetricKey_Handle key_handle, const KeySize* size); +void OPK_Unpack_K1_GetKeySize_Response(ODK_Message* msg, + OEMCryptoResult* result, KeySize** size); +ODK_Message OPK_Pack_C1_AESCBCDecrypt_Request( + WTPI_K1_SymmetricKey_Handle key_handle, size_t key_length, + const uint8_t* in_buffer, size_t in_buffer_length, const uint8_t* iv, + const uint8_t* out_buffer); +void OPK_Unpack_C1_AESCBCDecrypt_Response(ODK_Message* msg, + OEMCryptoResult* result, + size_t* in_buffer_length, + uint8_t** out_buffer); +ODK_Message OPK_Pack_C1_AESCBCEncrypt_Request( + WTPI_K1_SymmetricKey_Handle key_handle, const uint8_t* in_buffer, + size_t in_buffer_length, const uint8_t* iv, const uint8_t* out_buffer); +void OPK_Unpack_C1_AESCBCEncrypt_Response(ODK_Message* msg, + OEMCryptoResult* result, + size_t* in_buffer_length, + uint8_t** out_buffer); +void OPK_Unpack_C1_HMAC_SHA256_Verify_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_C1_CopyToOutputBuffer_Request(const uint8_t* input, + size_t input_length, + const OPK_OutputBuffer* out, + size_t output_offset); +void OPK_Unpack_C1_CopyToOutputBuffer_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_C1_RandomBytes_Request(const uint8_t* out, + size_t out_length); +void OPK_Unpack_C1_RandomBytes_Response(ODK_Message* msg, + OEMCryptoResult* result, uint8_t** out, + size_t* out_length); +ODK_Message OPK_Pack_K1_InitializeKeyManagement_Request(void); +void OPK_Unpack_K1_InitializeKeyManagement_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_K1_TerminateKeyManagement_Request(void); +void OPK_Unpack_K1_TerminateKeyManagement_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_K1_CreateKeyHandle_Request( + const uint8_t* input, size_t input_length, SymmetricKeyType key_type, + const WTPI_K1_SymmetricKey_Handle* out_key_handle); +void OPK_Unpack_K1_CreateKeyHandle_Response( + ODK_Message* msg, OEMCryptoResult* result, + WTPI_K1_SymmetricKey_Handle** out_key_handle); +ODK_Message OPK_Pack_K1_DeriveDeviceKeyIntoHandle_Request( + uint32_t context, SymmetricKeyType out_key_type, + const WTPI_K1_SymmetricKey_Handle* out_key_handle, KeySize out_key_size); +void OPK_Unpack_K1_DeriveDeviceKeyIntoHandle_Response( + ODK_Message* msg, OEMCryptoResult* result, + WTPI_K1_SymmetricKey_Handle** out_key_handle); +ODK_Message OPK_Pack_K1_AESDecryptAndCreateKeyHandle_Request( + WTPI_K1_SymmetricKey_Handle decrypt_key_handle, const uint8_t* enc_key, + size_t enc_key_length, const uint8_t* iv, SymmetricKeyType key_type, + const WTPI_K1_SymmetricKey_Handle* out_key_handle); +void OPK_Unpack_K1_AESDecryptAndCreateKeyHandle_Response( + ODK_Message* msg, OEMCryptoResult* result, + WTPI_K1_SymmetricKey_Handle** out_key_handle); +ODK_Message OPK_Pack_K1_AESDecryptAndCreateKeyHandleForMacKeys_Request( + WTPI_K1_SymmetricKey_Handle decrypt_key_handle, const uint8_t* enc_mac_keys, + size_t enc_mac_keys_length, const uint8_t* iv, + const WTPI_K1_SymmetricKey_Handle* out_mac_key_server, + const WTPI_K1_SymmetricKey_Handle* out_mac_key_client); +void OPK_Unpack_K1_AESDecryptAndCreateKeyHandleForMacKeys_Response( + ODK_Message* msg, OEMCryptoResult* result, + WTPI_K1_SymmetricKey_Handle** out_mac_key_server, + WTPI_K1_SymmetricKey_Handle** out_mac_key_client); +ODK_Message OPK_Pack_K1_DeriveKeyFromKeyHandle_Request( + 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, + const WTPI_K1_SymmetricKey_Handle* out_key_handle); +void OPK_Unpack_K1_DeriveKeyFromKeyHandle_Response( + ODK_Message* msg, OEMCryptoResult* result, + WTPI_K1_SymmetricKey_Handle** out_key_handle); +ODK_Message OPK_Pack_K1_WrapKey_Request(uint32_t context, + WTPI_K1_SymmetricKey_Handle key_handle, + SymmetricKeyType key_type, + const uint8_t* wrapped_key, + size_t wrapped_key_length); +void OPK_Unpack_K1_WrapKey_Response(ODK_Message* msg, OEMCryptoResult* result, + uint8_t** wrapped_key, + size_t* wrapped_key_length); +ODK_Message OPK_Pack_K1_UnwrapIntoKeyHandle_Request( + uint32_t context, const uint8_t* wrapped_key, size_t wrapped_key_length, + SymmetricKeyType key_type, + const WTPI_K1_SymmetricKey_Handle* out_key_handle); +void OPK_Unpack_K1_UnwrapIntoKeyHandle_Response( + ODK_Message* msg, OEMCryptoResult* result, + WTPI_K1_SymmetricKey_Handle** out_key_handle); +ODK_Message OPK_Pack_K1_FreeKeyHandle_Request( + WTPI_K1_SymmetricKey_Handle key_handle); +void OPK_Unpack_K1_FreeKeyHandle_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_CreateAsymmetricKeyHandle_Request( + const uint8_t* input, size_t input_length, AsymmetricKeyType key_type, + const WTPI_AsymmetricKey_Handle* key_handle); +void OPK_Unpack_CreateAsymmetricKeyHandle_Response( + ODK_Message* msg, OEMCryptoResult* result, + WTPI_AsymmetricKey_Handle** key_handle); +ODK_Message OPK_Pack_UnwrapIntoAsymmetricKeyHandle_Request( + const uint8_t* input, size_t input_length, AsymmetricKeyType key_type, + const WTPI_AsymmetricKey_Handle* key_handle, + const uint32_t* allowed_schemes); +void OPK_Unpack_UnwrapIntoAsymmetricKeyHandle_Response( + ODK_Message* msg, OEMCryptoResult* result, + WTPI_AsymmetricKey_Handle** key_handle, uint32_t** allowed_schemes); +ODK_Message OPK_Pack_FreeAsymmetricKeyHandle_Request( + WTPI_AsymmetricKey_Handle key_handle); +void OPK_Unpack_FreeAsymmetricKeyHandle_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_GetWrappedAsymmetricKeySize_Request( + size_t enc_private_key_length, AsymmetricKeyType key_type, + const size_t* buffer_size); +void OPK_Unpack_GetWrappedAsymmetricKeySize_Response(ODK_Message* msg, + OEMCryptoResult* result, + size_t** buffer_size); +ODK_Message OPK_Pack_WrapAsymmetricKey_Request(const uint8_t* output, + size_t output_length, + AsymmetricKeyType key_type, + const uint8_t* clear_key, + size_t clear_key_length); +void OPK_Unpack_WrapAsymmetricKey_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** output, + size_t* output_length); +ODK_Message OPK_Pack_RSASign_Request(WTPI_AsymmetricKey_Handle key, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + const size_t* signature_length, + RSA_Padding_Scheme padding_scheme); +void OPK_Unpack_RSASign_Response(ODK_Message* msg, OEMCryptoResult* result, + uint8_t** signature, + size_t** signature_length); +ODK_Message OPK_Pack_RSADecrypt_Request(WTPI_AsymmetricKey_Handle key, + const uint8_t* input, + size_t input_length, const uint8_t* out, + const size_t* out_length); +void OPK_Unpack_RSADecrypt_Response(ODK_Message* msg, OEMCryptoResult* result, + uint8_t** out, size_t** out_length); +ODK_Message OPK_Pack_ECCSign_Request(WTPI_AsymmetricKey_Handle key, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + const size_t* signature_length); +void OPK_Unpack_ECCSign_Response(ODK_Message* msg, OEMCryptoResult* result, + uint8_t** signature, + size_t** signature_length); +ODK_Message OPK_Pack_ECCDeriveSessionKey_Request( + WTPI_AsymmetricKey_Handle key, const uint8_t* key_source, + size_t key_source_length, const uint8_t* session_key, + const size_t* session_key_length); +void OPK_Unpack_ECCDeriveSessionKey_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** session_key, + size_t** session_key_length); +ODK_Message OPK_Pack_GetSignatureSize_Request(WTPI_AsymmetricKey_Handle key, + const size_t* signature_length); +void OPK_Unpack_GetSignatureSize_Response(ODK_Message* msg, + OEMCryptoResult* result, + size_t** signature_length); +ODK_Message OPK_Pack_ED25519Sign_Request(WTPI_AsymmetricKey_Handle key, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + const size_t* signature_length); +void OPK_Unpack_ED25519Sign_Response(ODK_Message* msg, OEMCryptoResult* result, + uint8_t** signature, + size_t** signature_length); +ODK_Message OPK_Pack_GetBootCertificateChain_Request(const uint8_t* out, + const size_t* out_length); +void OPK_Unpack_GetBootCertificateChain_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** out, + size_t** out_length); +ODK_Message OPK_Pack_GenerateRandomCertificateKeyPair_Request( + const AsymmetricKeyType* key_type, const uint8_t* wrapped_private_key, + const size_t* wrapped_private_key_length, const uint8_t* public_key, + const size_t* public_key_length); +void OPK_Unpack_GenerateRandomCertificateKeyPair_Response( + ODK_Message* msg, OEMCryptoResult* result, AsymmetricKeyType** key_type, + uint8_t** wrapped_private_key, size_t** wrapped_private_key_length, + uint8_t** public_key, size_t** public_key_length); +ODK_Message OPK_Pack_DeviceKeyCoseSign1_Request(const uint8_t* message, + size_t message_length, + const uint8_t* signature, + const size_t* signature_length); +void OPK_Unpack_DeviceKeyCoseSign1_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** signature, + size_t** signature_length); +ODK_Message OPK_Pack_Crc32Init_Request(const uint32_t* initial_hash); +void OPK_Unpack_Crc32Init_Response(ODK_Message* msg, OEMCryptoResult* result, + uint32_t** initial_hash); +ODK_Message OPK_Pack_Crc32Cont_Request(const uint8_t* in, size_t in_length, + uint32_t prev_crc, + const uint32_t* new_crc); +void OPK_Unpack_Crc32Cont_Response(ODK_Message* msg, OEMCryptoResult* result, + uint32_t** new_crc); +ODK_Message OPK_Pack_Crc32Cont_OutputBuffer_Request(const OPK_OutputBuffer* in, + size_t in_offset, + size_t in_length, + uint32_t prev_crc, + const uint32_t* new_crc); +void OPK_Unpack_Crc32Cont_OutputBuffer_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint32_t** new_crc); +ODK_Message OPK_Pack_GetTrustedTime_Request(const uint64_t* time_in_s); +void OPK_Unpack_GetTrustedTime_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint64_t** time_in_s); +ODK_Message OPK_Pack_InitializeClock_Request(void); +void OPK_Unpack_InitializeClock_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_TerminateClock_Request(void); +void OPK_Unpack_TerminateClock_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_GetClockType_Request(void); +void OPK_Unpack_GetClockType_Response(ODK_Message* msg, + OEMCrypto_Clock_Security_Level* result); +#ifdef __cplusplus +} // extern "C" +#endif +#endif /* OPK_REE_SERIALIZER_H_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/api_support.h b/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/api_support.h new file mode 100644 index 0000000..ec67207 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/api_support.h @@ -0,0 +1,39 @@ +/* + * 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 OPK_API_SUPPORT_H_ +#define OPK_API_SUPPORT_H_ + +/* + * Support functions for the TEE API functions, related to message handling. + */ + +#include + +#include "OEMCryptoCENC.h" +#include "bump_allocator.h" +#include "marshaller_base.h" +#include "shared_buffer_allocator.h" +#include "tos_shared_memory_interface.h" +#include "tos_transport_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern pthread_mutex_t api_lock; +extern OEMCryptoResult api_result; + +void API_Initialize(void); +ODK_Message API_Transact(ODK_Message* request); +OEMCryptoResult API_CheckResult(OEMCryptoResult unpacked_result); +void API_Terminate(void); + +#ifdef __cplusplus +} +#endif + +#endif /* OPK_API_SUPPORT_H_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/ree.gyp b/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/ree.gyp new file mode 100644 index 0000000..5df1c6d --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/ree.gyp @@ -0,0 +1,36 @@ +# +# Builds the static library opk_ree.a, which includes Linux-based +# implementation of transport and shared memory. +# +# Dependencies: +# OPK serialization library for REE [ 'ree_api.gyp:opk_ree_api' ] +# +{ + 'includes' : [ + '../settings.gypi', + ], + 'variables': { + 'serialization_adapter_dir' : '<(oemcrypto_dir)/opk/ports/linux/ipc-test/serialization_adapter', + }, + 'targets' : [ + { + 'target_name': 'opk_ree', + 'type': 'static_library', + 'sources': [ + '<(serialization_adapter_dir)/tos_logging.cpp', + '<(serialization_adapter_dir)/tos_secure_buffers.c', + '<(serialization_adapter_dir)/tos_shared_memory.cpp', + '<(serialization_adapter_dir)/tos_transport.cpp', + ], + 'link_settings': { + 'libraries': [ + '-lpthread', + '-lrt' + ], + }, + 'dependencies': [ + '<(ree_dir)/ree_api.gyp:opk_ree_api', + ], + }, + ] +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/ree_api.gyp b/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/ree_api.gyp new file mode 100644 index 0000000..1377849 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/ree_api.gyp @@ -0,0 +1,48 @@ +# +# Builds the static library opk_ree_api.a from generated sources based on +# oemcrypto_wtpi_macros.h. +# +{ + 'includes': [ + '../settings.gypi' + ], + 'target_defaults': { + 'variables': { + # If |enable_code_generator| is true, the GEN_* files will be + # updated if they are out of date. + 'enable_code_generator%': 'false', + }, + }, + 'targets': [ + { + 'target_name': 'opk_ree_api', + 'type': 'static_library', + 'sources': [ + 'GEN_ree_serializer.c', + 'GEN_oemcrypto_tee_test_api.c', + 'ree_special_cases.c', + '<(common_dir)/GEN_common_serializer.c', + '<(common_dir)/common_special_cases.c', + '<(odk_dir)/src/odk_message.c', + '<(odk_dir)/src/odk_overflow.c', + '<(serialization_common_dir)/bump_allocator.c', + '<(serialization_common_dir)/log_macros.c', + '<(serialization_common_dir)/length_types.c', + '<(serialization_common_dir)/marshaller_base.c', + '<(serialization_common_dir)/opk_init.c', + '<(serialization_common_dir)/opk_serialization_base.c', + '<(serialization_common_dir)/shared_buffer_allocator.c', + '<(serialization_dir)/ree/api_support.c', + ], + 'conditions': [ + ['enable_code_generator=="true"', { + 'dependencies': [ + '<(wtpi_test_dir)/generator/make_source.gyp:make_api_src', + '<(wtpi_test_dir)/generator/make_source.gyp:make_common_serializer_src', + '<(wtpi_test_dir)/generator/make_source.gyp:make_ree_serializer_src', + ], + }], + ], + } + ] +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/ree_special_cases.c b/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/ree_special_cases.c new file mode 100644 index 0000000..c79633c --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/ree_special_cases.c @@ -0,0 +1,147 @@ +/* + * 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 "ree_special_cases.h" + +#include +#include "GEN_common_serializer.h" +#include "bump_allocator.h" +#include "common_special_cases.h" +#include "log_macros.h" +#include "marshaller_base.h" +#include "odk_overflow.h" +#include "opk_serialization_base.h" +#include "shared_buffer_allocator.h" +#include "tos_transport_interface.h" + +ODK_Message OPK_Pack_C1_HMAC_SHA256_Verify_Request( + WTPI_K1_SymmetricKey_Handle key, const uint8_t* in_buffer, + size_t in_buffer_length, const uint8_t* signature) { + uint32_t api_value = 10009; /* from _tee10009 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &in_buffer_length); + OPK_Pack_WTPI_K1_SymmetricKey_Handle(&msg, &key); + OPK_PackMemory(&msg, in_buffer, OPK_ToLengthType(in_buffer_length)); + OPK_PackMemory(&msg, signature, OPK_ToLengthType(32)); + OPK_PackEOM(&msg); + return msg; +} + +ODK_Message OPK_Pack_C1_SHA256_Request(const uint8_t* in_buffer, + size_t in_buffer_length, + const uint8_t* out_buffer) { + uint32_t api_value = 10007; /* from _tee10007 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &in_buffer_length); + OPK_PackMemory(&msg, in_buffer, OPK_ToLengthType(in_buffer_length)); + OPK_PackAlloc(&msg, out_buffer); + OPK_PackEOM(&msg); + return msg; +} + +void OPK_Unpack_C1_SHA256_Response(ODK_Message* msg, OEMCryptoResult* result, + uint8_t** out_buffer) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10007) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + size_t out_length = 32; + OPK_UnpackInPlace(msg, &p, OPK_ToLengthType(out_length)); + if (p && *out_buffer) { + memcpy(*out_buffer, p, out_length); + } + } + OPK_UnpackEOM(msg); +} + +ODK_Message OPK_Pack_C1_HMAC_SHA1_Request(WTPI_K1_SymmetricKey_Handle key, + const uint8_t* in_buffer, + size_t in_buffer_length, + const uint8_t* out_buffer) { + uint32_t api_value = 10006; /* from _tee10006 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &in_buffer_length); + OPK_Pack_WTPI_K1_SymmetricKey_Handle(&msg, &key); + OPK_PackMemory(&msg, in_buffer, OPK_ToLengthType(in_buffer_length)); + OPK_PackAlloc(&msg, out_buffer); + OPK_PackEOM(&msg); + return msg; +} + +void OPK_Unpack_C1_HMAC_SHA1_Response(ODK_Message* msg, OEMCryptoResult* result, + uint8_t** out_buffer) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10006) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + size_t out_length = 20; + OPK_UnpackInPlace(msg, &p, OPK_ToLengthType(out_length)); + if (p && *out_buffer) { + memcpy(*out_buffer, p, out_length); + } + } + OPK_UnpackEOM(msg); +} + +ODK_Message OPK_Pack_C1_HMAC_SHA256_Request(WTPI_K1_SymmetricKey_Handle key, + const uint8_t* in_buffer, + size_t in_buffer_length, + const uint8_t* out_buffer) { + uint32_t api_value = 10008; /* from _tee10008 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &in_buffer_length); + OPK_Pack_WTPI_K1_SymmetricKey_Handle(&msg, &key); + OPK_PackMemory(&msg, in_buffer, OPK_ToLengthType(in_buffer_length)); + OPK_PackAlloc(&msg, out_buffer); + OPK_PackEOM(&msg); + return msg; +} + +void OPK_Unpack_C1_HMAC_SHA256_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** out_buffer) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10008) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + size_t out_length = 32; + OPK_UnpackInPlace(msg, &p, OPK_ToLengthType(out_length)); + if (p && *out_buffer) { + memcpy(*out_buffer, p, out_length); + } + } + OPK_UnpackEOM(msg); +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/ree_special_cases.h b/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/ree_special_cases.h new file mode 100644 index 0000000..0196a38 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/ree_special_cases.h @@ -0,0 +1,52 @@ +/* + * 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 WTPI_TEST_REE_SPECIAL_CASES_H_ +#define WTPI_TEST_REE_SPECIAL_CASES_H_ + +#include "odk_message.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" +#include "wtpi_crypto_asymmetric_interface.h" + +void OPK_Pack_SymmetricKeyType(ODK_Message* msg, + const SymmetricKeyType* key_type); +void OPK_Unpack_SymmetricKeyType(ODK_Message* msg, SymmetricKeyType* key_type); + +void OPK_Pack_AsymmetricKeyType(ODK_Message* msg, + const AsymmetricKeyType* key_type); +void OPK_Unpack_AsymmetricKeyType(ODK_Message* msg, + AsymmetricKeyType* key_type); + +void OPK_Pack_RSA_Padding_Scheme(ODK_Message* msg, + const RSA_Padding_Scheme* padding_scheme); +void OPK_Unpack_RSA_Padding_Scheme(ODK_Message* msg, + RSA_Padding_Scheme* padding_scheme); + +void OPK_Pack_KeySize(ODK_Message* msg, const KeySize* key_size); +void OPK_Unpack_KeySize(ODK_Message* msg, KeySize* key_size); + +ODK_Message OPK_Pack_C1_HMAC_SHA256_Verify_Request( + WTPI_K1_SymmetricKey_Handle key, const uint8_t* in_buffer, + size_t in_buffer_length, const uint8_t* signature); +ODK_Message OPK_Pack_C1_SHA256_Request(const uint8_t* in_buffer, + size_t in_buffer_length, + const uint8_t* out_buffer); +void OPK_Unpack_C1_SHA256_Response(ODK_Message* msg, OEMCryptoResult* result, + uint8_t** out_buffer); + +void OPK_Unpack_C1_HMAC_SHA1_Response(ODK_Message* msg, OEMCryptoResult* result, + uint8_t** out_buffer); +ODK_Message OPK_Pack_C1_HMAC_SHA1_Request(WTPI_K1_SymmetricKey_Handle key, + const uint8_t* in_buffer, + size_t in_buffer_length, + const uint8_t* out_buffer); +ODK_Message OPK_Pack_C1_HMAC_SHA256_Request(WTPI_K1_SymmetricKey_Handle key, + const uint8_t* in_buffer, + size_t in_buffer_length, + const uint8_t* out_buffer); +void OPK_Unpack_C1_HMAC_SHA256_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** out_buffer); +#endif diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/settings.gypi b/oemcrypto/opk/oemcrypto_ta/wtpi_test/settings.gypi new file mode 100644 index 0000000..70606e6 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/settings.gypi @@ -0,0 +1,43 @@ +{ + 'variables': { + 'oemcrypto_dir' : '<(DEPTH)/oemcrypto', + 'odk_dir' : '<(oemcrypto_dir)/odk', + 'oemcrypto_ta_dir' : '<(oemcrypto_dir)/opk/oemcrypto_ta', + 'serialization_dir' : '<(oemcrypto_dir)/opk/serialization', + 'serialization_common_dir' : '<(serialization_dir)/common', + 'serialization_generator_dir' : '<(serialization_dir)/generator', + 'os_interfaces_dir' : '<(serialization_dir)/os_interfaces', + 'wtpi_dir' : '<(oemcrypto_ta_dir)/wtpi', + 'wtpi_test_dir' : '<(oemcrypto_ta_dir)/wtpi_test', + 'common_dir' : '<(wtpi_test_dir)/common', + 'ree_dir' : '<(wtpi_test_dir)/ree', + 'tee_dir' : '<(wtpi_test_dir)/tee', + 'third_party_dir' : '<(DEPTH)/third_party', + 'json_dir' : '<(third_party_dir)/nlohmann-json', + 'util_dir' : '<(DEPTH)/util', + }, + 'target_defaults' : { + 'cflags' : [ + '-g', + '-Werror=all', + ], + 'include_dirs' : [ + '.', + '<(common_dir)', + '<(json_dir)/single_include', + '<(odk_dir)/include', + '<(odk_dir)/src', + '<(oemcrypto_dir)/include', + '<(oemcrypto_ta_dir)', + '<(oemcrypto_ta_dir)/wtpi', + '<(os_interfaces_dir)', + '<(serialization_common_dir)/include', + '<(serialization_generator_dir)', + ], + 'defines' : [ + 'ENABLE_LOGGING=1', + 'MIN_LOG_LEVEL=LOG_LEVEL_DEBUG', + 'ENABLE_ANSI_COLORS=1', + ], + } +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/ssl_util.cpp b/oemcrypto/opk/oemcrypto_ta/wtpi_test/ssl_util.cpp new file mode 100644 index 0000000..9ffb463 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/ssl_util.cpp @@ -0,0 +1,240 @@ +// 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 "ssl_util.h" + +#include + +#include "log.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 DeserializePKCS8PrivateKey(const uint8_t* serialized_bytes, size_t size, + RSA** rsa) { + 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"); + goto cleanup; + } + evp = EVP_PKCS82PKEY(pkcs8_pki); + if (evp == NULL) { + LOGE("EVP_PKCS82PKEY returned NULL"); + goto cleanup; + } + *rsa = EVP_PKEY_get1_RSA(evp); + if (*rsa == NULL) { + LOGE("PrivateKeyInfo did not contain an RSA key"); + goto cleanup; + } + success = true; + +cleanup: + dump_ssl_error(); + if (evp != NULL) { + EVP_PKEY_free(evp); + } + if (pkcs8_pki != NULL) { + PKCS8_PRIV_KEY_INFO_free(pkcs8_pki); + } + BIO_free(bio); + return success; +} + +bool VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message, + size_t message_length, const uint8_t* signature, + size_t signature_length) { + boringssl_ptr md_ctx(EVP_MD_CTX_new()); + EVP_PKEY_CTX* pkey_ctx = nullptr; + + if (EVP_DigestVerifyInit(md_ctx.get(), &pkey_ctx, EVP_sha1(), + nullptr /* no ENGINE */, pkey) != 1) { + LOGE("EVP_DigestVerifyInit failed in VerifyPSSSignature"); + goto err; + } + + if (EVP_PKEY_CTX_set_signature_md(pkey_ctx, + const_cast(EVP_sha1())) != 1) { + LOGE("EVP_PKEY_CTX_set_signature_md failed in VerifyPSSSignature"); + goto err; + } + + if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) { + LOGE("EVP_PKEY_CTX_set_rsa_padding failed in VerifyPSSSignature"); + goto err; + } + + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, SHA_DIGEST_LENGTH) != 1) { + LOGE("EVP_PKEY_CTX_set_rsa_pss_saltlen failed in VerifyPSSSignature"); + goto err; + } + + if (EVP_DigestVerifyUpdate(md_ctx.get(), message, message_length) != 1) { + LOGE("EVP_DigestVerifyUpdate failed in VerifyPSSSignature"); + goto err; + } + + if (EVP_DigestVerifyFinal(md_ctx.get(), const_cast(signature), + signature_length) != 1) { + LOGE( + "EVP_DigestVerifyFinal failed in VerifyPSSSignature. (Probably a bad " + "signature.)"); + goto err; + } + + return true; + +err: + dump_ssl_error(); + return false; +} + +namespace wtpi_test { +bool NewEccPrivateKey(EcKeyPtr* ec_key) { + if (ec_key == nullptr) { + LOGE("Output |ec_key| is null"); + return false; + } + // Create an SECP256R1 key. + ec_key->reset(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + if (!*ec_key) { + LOGE("Failed to allocate ECC private key"); + dump_ssl_error(); + return false; + } + if (!EC_KEY_generate_key(ec_key->get())) { + LOGE("Failed to generate ECC private key"); + dump_ssl_error(); + return false; + } + return true; +} + +bool MakeEccPublicKey(EC_KEY* priv_key, EcKeyPtr* pub_key) { + if (priv_key == nullptr) { + LOGE("Input |priv_key| is null"); + return false; + } + if (pub_key == nullptr) { + LOGE("Output |pub_key| is null"); + return false; + } + // Create an SECP256R1 key. + pub_key->reset(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + if (!*pub_key) { + LOGE("Failed to allocate ECC public key"); + dump_ssl_error(); + return false; + } + if (!EC_KEY_set_public_key(pub_key->get(), + EC_KEY_get0_public_key(priv_key))) { + // Possible errors are that the |priv_key| is from a different curve + // family. This may happen if the private key was deserialized from + // a real DRM certificate. + LOGE("Failed to set ECC public key component"); + dump_ssl_error(); + pub_key->reset(); + return false; + } + return true; +} + +bool SerializeEccPrivateKey(EC_KEY* priv_key, std::vector* key_data) { + if (priv_key == nullptr) { + LOGE("Input |priv_key| is null"); + return false; + } + if (key_data == nullptr) { + LOGE("Output |key_data| is null"); + return false; + } + EC_KEY_set_asn1_flag(priv_key, OPENSSL_EC_NAMED_CURVE); + EC_KEY_set_conv_form(priv_key, POINT_CONVERSION_UNCOMPRESSED); + using EvpPKeyPtr = boringssl_ptr; + EvpPKeyPtr evp_key(EVP_PKEY_new()); + if (!evp_key) { + LOGE("Failed to allocate EVP_PKEY for private key"); + dump_ssl_error(); + return false; + } + if (!EVP_PKEY_set1_EC_KEY(evp_key.get(), priv_key)) { + LOGE("Failed to assign ECC private key to EVP_PKEY"); + dump_ssl_error(); + return false; + } + + using PrivKeyInfoPtr = + boringssl_ptr; + PrivKeyInfoPtr priv_info(EVP_PKEY2PKCS8(evp_key.get())); + if (!priv_info) { + LOGE("Failed to convert to PKCS8 private key info"); + dump_ssl_error(); + return false; + } + + using BioPtr = boringssl_ptr; + BioPtr bio(BIO_new(BIO_s_mem())); + if (!bio) { + LOGE("BIO allocation failed"); + dump_ssl_error(); + return false; + } + if (!i2d_PKCS8_PRIV_KEY_INFO_bio(bio.get(), priv_info.get())) { + LOGE("Failed to serialize ECC private key"); + dump_ssl_error(); + return false; + } + + char* key_ptr = nullptr; + const long key_size = BIO_get_mem_data(bio.get(), &key_ptr); + if (key_size <= 0l || key_ptr == nullptr) { + LOGE( + "Failed to get serialized ECC private key data: " + "key_size = %ld, key_ptr = %s", + key_size, key_ptr ? "not-null" : "null"); + dump_ssl_error(); + return false; + } + key_data->assign(key_ptr, key_ptr + key_size); + return true; +} + +bool SerializeEccPublicKey(EC_KEY* ec_key, std::vector* key_data) { + if (ec_key == nullptr) { + LOGE("Input |ec_key| is null"); + return false; + } + if (key_data == nullptr) { + LOGE("Output |key_data| is null"); + return false; + } + EC_KEY_set_asn1_flag(ec_key, OPENSSL_EC_NAMED_CURVE); + EC_KEY_set_conv_form(ec_key, POINT_CONVERSION_UNCOMPRESSED); + uint8_t* key_ptr = nullptr; + const int key_size = i2d_EC_PUBKEY(ec_key, &key_ptr); + if (key_size <= 0 || key_ptr == nullptr) { + LOGE("Failed to serialize ECC public key: key_size = %d, key_ptr = %s", + key_size, key_ptr ? "not-null" : "null"); + dump_ssl_error(); + return false; + } + key_data->assign(key_ptr, key_ptr + key_size); + OPENSSL_free(key_ptr); + return true; +} +} // namespace wtpi_test diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/ssl_util.h b/oemcrypto/opk/oemcrypto_ta/wtpi_test/ssl_util.h new file mode 100644 index 0000000..d48c647 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/ssl_util.h @@ -0,0 +1,82 @@ +// 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 WTPI_TEST_SSL_UTIL_H_ +#define WTPI_TEST_SSL_UTIL_H_ +#include + +#include "openssl/aes.h" +#include "openssl/bio.h" +#include "openssl/ec.h" +#include "openssl/err.h" +#include "openssl/rsa.h" +#include "openssl/sha.h" +#include "openssl/x509.h" + +// A smart pointer for BoringSSL objects. It uses the specified free function +// to release resources and free memory when the pointer is deleted. +template +class boringssl_ptr { + public: + boringssl_ptr() {} + explicit boringssl_ptr(T* p) : ptr_(p) {} + boringssl_ptr(const boringssl_ptr& ptr) = delete; + boringssl_ptr(boringssl_ptr&& other) : ptr_(other.release()) {} + + ~boringssl_ptr() { reset(); } + + boringssl_ptr& operator=(const boringssl_ptr& other) = delete; + boringssl_ptr& operator=(boringssl_ptr&& other) { + reset(other.release()); + return *this; + } + + explicit operator bool() const { return ptr_ != nullptr; } + + T& operator*() const { return *ptr_; } + T* operator->() const { return ptr_; } + T* get() const { return ptr_; } + bool NotNull() const { return ptr_ != nullptr; } + + T* release() { + T* temp = ptr_; + ptr_ = nullptr; + return temp; + } + + void reset(T* ptr = nullptr) { + if (ptr_) { + dtor(ptr_); + } + ptr_ = ptr; + } + + private: + T* ptr_ = nullptr; +}; + +bool DeserializePKCS8PrivateKey(const uint8_t* serialized_bytes, size_t size, + RSA** rsa); +bool VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message, + size_t message_length, const uint8_t* signature, + size_t signature_length); + +namespace wtpi_test { +// ECC Utils +using EcKeyPtr = boringssl_ptr; + +// Creates a new, randomly generated SECP256R1 ECC private key. +bool NewEccPrivateKey(EcKeyPtr* ec_key); + +// Creates a public key equivalent of the provided EC private key. +bool MakeEccPublicKey(EC_KEY* priv_key, EcKeyPtr* pub_key); + +// Serializes the provided ECC private key into its DER encoded PKCS8 +// PrivateKeyInfo, using named curves and uncompressed points. +bool SerializeEccPrivateKey(EC_KEY* priv_key, std::vector* key_data); + +// Serializes the provided ECC public key into its DER encoded PKCS8 +// SubjectPublicKeyInfo, using named curves and uncompressed points. +bool SerializeEccPublicKey(EC_KEY* ec_key, std::vector* key_data); +} // namespace wtpi_test +#endif // WTPI_TEST_SSL_UTIL_H_ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/tee/GEN_dispatcher.c b/oemcrypto/opk/oemcrypto_ta/wtpi_test/tee/GEN_dispatcher.c new file mode 100644 index 0000000..09dcd38 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/tee/GEN_dispatcher.c @@ -0,0 +1,964 @@ +/* + * Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine License + * Agreement. + */ + +/* + * This code is auto-generated, do not edit + */ + +#include + +#include "GEN_common_serializer.h" +#include "GEN_tee_serializer.h" +#include "OEMCryptoCENC.h" +#include "bump_allocator.h" +#include "log_macros.h" +#include "marshaller_base.h" +#include "message_debug.h" +#include "oemcrypto_wall_clock.h" +#include "opk_dispatcher.h" +#include "shared_buffer_allocator.h" +#include "tee_special_cases.h" +#include "tos_shared_memory_interface.h" +#include "tos_transport_interface.h" +#include "wtpi_clock_interface_layer1.h" +#include "wtpi_generation_number_interface.h" + +static ODK_Message CreateEmptyMessage(void) { + static uint8_t buffer[1]; + ODK_Message msg = ODK_Message_Create(buffer, sizeof(buffer)); + OPK_PackEOM(&msg); + return msg; +} + +static ODK_Message NullMessage(void) { return ODK_Message_Create(NULL, 0); } + +void OPK_Init_OEMCrypto_Substring(OEMCrypto_Substring* obj) { + OPK_Init_size_t((size_t*)&obj->offset); + OPK_Init_size_t((size_t*)&obj->length); +} + +void OPK_Init_OEMCrypto_DTCP2_CMI_Descriptor_0( + OEMCrypto_DTCP2_CMI_Descriptor_0* obj) { + OPK_Init_uint8_t((uint8_t*)&obj->id); + OPK_Init_uint8_t((uint8_t*)&obj->extension); + OPK_Init_uint16_t((uint16_t*)&obj->length); + OPK_Init_uint8_t((uint8_t*)&obj->data); +} + +void OPK_Init_OEMCrypto_DTCP2_CMI_Descriptor_1( + OEMCrypto_DTCP2_CMI_Descriptor_1* obj) { + OPK_Init_uint8_t((uint8_t*)&obj->id); + OPK_Init_uint8_t((uint8_t*)&obj->extension); + OPK_Init_uint16_t((uint16_t*)&obj->length); + OPK_InitMemory(&obj->data[0], 3); +} + +void OPK_Init_OEMCrypto_DTCP2_CMI_Descriptor_2( + OEMCrypto_DTCP2_CMI_Descriptor_2* obj) { + OPK_Init_uint8_t((uint8_t*)&obj->id); + OPK_Init_uint8_t((uint8_t*)&obj->extension); + OPK_Init_uint16_t((uint16_t*)&obj->length); + OPK_InitMemory(&obj->data[0], 3); +} + +void OPK_Init_OEMCrypto_DTCP2_CMI_Packet(OEMCrypto_DTCP2_CMI_Packet* obj) { + OPK_Init_uint8_t((uint8_t*)&obj->dtcp2_required); + OPK_Init_OEMCrypto_DTCP2_CMI_Descriptor_0( + (OEMCrypto_DTCP2_CMI_Descriptor_0*)&obj->cmi_descriptor_0); + OPK_Init_OEMCrypto_DTCP2_CMI_Descriptor_1( + (OEMCrypto_DTCP2_CMI_Descriptor_1*)&obj->cmi_descriptor_1); + OPK_Init_OEMCrypto_DTCP2_CMI_Descriptor_2( + (OEMCrypto_DTCP2_CMI_Descriptor_2*)&obj->cmi_descriptor_2); +} + +void OPK_Init_OEMCrypto_KeyObject(OEMCrypto_KeyObject* obj) { + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&obj->key_id); + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&obj->key_data_iv); + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&obj->key_data); + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&obj->key_control_iv); + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&obj->key_control); +} + +/* See opk_dispatcher.h for definition of OPK_DispatchMessage() */ +ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, + ODK_Message* response) { + if (request == NULL || response == NULL) + return MESSAGE_STATUS_NULL_POINTER_ERROR; + *response = NullMessage(); + uint32_t api_value; + OPK_Unpack_uint32_t(request, &api_value); + uint64_t timestamp; + OPK_Unpack_uint64_t(request, ×tamp); + OPK_SetWallClockTime(timestamp); + OPK_BumpAllocator_Reset(); + OPK_SharedBuffer_Reset(); + ODK_Message_Reset(request); + switch (api_value) { + case 10000: /* WTPI_PrepareGenerationNumber */ + { + OPK_Unpack_PrepareGenerationNumber_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("PrepareGenerationNumber"); + result = WTPI_PrepareGenerationNumber(); + *response = OPK_Pack_PrepareGenerationNumber_Response(result); + break; + } + case 10001: /* WTPI_LoadGenerationNumber */ + { + uint64_t* value; + OPK_InitPointer((uint8_t**)&value); + OPK_Unpack_LoadGenerationNumber_Request(request, &value); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("LoadGenerationNumber"); + result = WTPI_LoadGenerationNumber(value); + *response = OPK_Pack_LoadGenerationNumber_Response(result, value); + break; + } + case 10002: /* WTPI_SaveGenerationNumber */ + { + uint64_t value; + OPK_Init_uint64_t((uint64_t*)&value); + OPK_Unpack_SaveGenerationNumber_Request(request, &value); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("SaveGenerationNumber"); + result = WTPI_SaveGenerationNumber(value); + *response = OPK_Pack_SaveGenerationNumber_Response(result); + break; + } + case 10003: /* WTPI_K1_GetKeySize */ + { + WTPI_K1_SymmetricKey_Handle key_handle; + OPK_Init_WTPI_K1_SymmetricKey_Handle( + (WTPI_K1_SymmetricKey_Handle*)&key_handle); + KeySize* size; + OPK_InitPointer((uint8_t**)&size); + OPK_Unpack_K1_GetKeySize_Request(request, &key_handle, &size); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("K1_GetKeySize"); + result = WTPI_K1_GetKeySize(key_handle, size); + *response = OPK_Pack_K1_GetKeySize_Response(result, size); + break; + } + case 10004: /* WTPI_C1_AESCBCDecrypt */ + { + size_t in_buffer_length; + OPK_Init_size_t((size_t*)&in_buffer_length); + WTPI_K1_SymmetricKey_Handle key_handle; + OPK_Init_WTPI_K1_SymmetricKey_Handle( + (WTPI_K1_SymmetricKey_Handle*)&key_handle); + size_t key_length; + OPK_Init_size_t((size_t*)&key_length); + uint8_t* in_buffer; + OPK_InitPointer((uint8_t**)&in_buffer); + uint8_t iv[16]; + OPK_InitMemory(&iv[0], 16); + uint8_t* out_buffer; + OPK_InitPointer((uint8_t**)&out_buffer); + OPK_Unpack_C1_AESCBCDecrypt_Request(request, &key_handle, &key_length, + &in_buffer, &in_buffer_length, &iv[0], + &out_buffer); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("C1_AESCBCDecrypt"); + result = WTPI_C1_AESCBCDecrypt(key_handle, key_length, in_buffer, + in_buffer_length, iv, out_buffer); + *response = OPK_Pack_C1_AESCBCDecrypt_Response(result, in_buffer_length, + out_buffer); + break; + } + case 10005: /* WTPI_C1_AESCBCEncrypt */ + { + size_t in_buffer_length; + OPK_Init_size_t((size_t*)&in_buffer_length); + WTPI_K1_SymmetricKey_Handle key_handle; + OPK_Init_WTPI_K1_SymmetricKey_Handle( + (WTPI_K1_SymmetricKey_Handle*)&key_handle); + uint8_t* in_buffer; + OPK_InitPointer((uint8_t**)&in_buffer); + uint8_t iv[16]; + OPK_InitMemory(&iv[0], 16); + uint8_t* out_buffer; + OPK_InitPointer((uint8_t**)&out_buffer); + OPK_Unpack_C1_AESCBCEncrypt_Request(request, &key_handle, &in_buffer, + &in_buffer_length, &iv[0], + &out_buffer); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("C1_AESCBCEncrypt"); + result = WTPI_C1_AESCBCEncrypt(key_handle, in_buffer, in_buffer_length, + iv, out_buffer); + *response = OPK_Pack_C1_AESCBCEncrypt_Response(result, in_buffer_length, + out_buffer); + break; + } + case 10006: /* WTPI_C1_HMAC_SHA1 */ + { + size_t input_length; + OPK_Init_size_t((size_t*)&input_length); + WTPI_K1_SymmetricKey_Handle key_handle; + OPK_Init_WTPI_K1_SymmetricKey_Handle( + (WTPI_K1_SymmetricKey_Handle*)&key_handle); + uint8_t* input; + OPK_InitPointer((uint8_t**)&input); + uint8_t* out_buffer; + OPK_InitPointer((uint8_t**)&out_buffer); + OPK_Unpack_C1_HMAC_SHA1_Request(request, &key_handle, &input, + &input_length, &out_buffer); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("C1_HMAC_SHA1"); + result = WTPI_C1_HMAC_SHA1(key_handle, input, input_length, out_buffer); + *response = OPK_Pack_C1_HMAC_SHA1_Response(result, out_buffer); + break; + } + case 10007: /* WTPI_C1_SHA256 */ + { + size_t input_length; + OPK_Init_size_t((size_t*)&input_length); + uint8_t* input; + OPK_InitPointer((uint8_t**)&input); + uint8_t* out_buffer; + OPK_InitPointer((uint8_t**)&out_buffer); + OPK_Unpack_C1_SHA256_Request(request, &input, &input_length, &out_buffer); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("C1_SHA256"); + result = WTPI_C1_SHA256(input, input_length, out_buffer); + *response = OPK_Pack_C1_SHA256_Response(result, out_buffer); + break; + } + case 10008: /* WTPI_C1_HMAC_SHA256 */ + { + size_t input_length; + OPK_Init_size_t((size_t*)&input_length); + WTPI_K1_SymmetricKey_Handle key_handle; + OPK_Init_WTPI_K1_SymmetricKey_Handle( + (WTPI_K1_SymmetricKey_Handle*)&key_handle); + uint8_t* input; + OPK_InitPointer((uint8_t**)&input); + uint8_t* out_buffer; + OPK_InitPointer((uint8_t**)&out_buffer); + OPK_Unpack_C1_HMAC_SHA256_Request(request, &key_handle, &input, + &input_length, &out_buffer); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("C1_HMAC_SHA256"); + result = WTPI_C1_HMAC_SHA256(key_handle, input, input_length, out_buffer); + *response = OPK_Pack_C1_HMAC_SHA256_Response(result, out_buffer); + break; + } + case 10009: /* WTPI_C1_HMAC_SHA256_Verify */ + { + size_t input_length; + OPK_Init_size_t((size_t*)&input_length); + WTPI_K1_SymmetricKey_Handle key_handle; + OPK_Init_WTPI_K1_SymmetricKey_Handle( + (WTPI_K1_SymmetricKey_Handle*)&key_handle); + uint8_t* input; + OPK_InitPointer((uint8_t**)&input); + uint8_t* signature = (uint8_t*)OPK_VarAlloc(sizeof(uint8_t)); + OPK_Init_uint8_t((uint8_t*)signature); + OPK_Unpack_C1_HMAC_SHA256_Verify_Request(request, &key_handle, &input, + &input_length, &signature); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("C1_HMAC_SHA256_Verify"); + result = WTPI_C1_HMAC_SHA256_Verify(key_handle, input, input_length, + signature); + *response = OPK_Pack_C1_HMAC_SHA256_Verify_Response(result); + break; + } + case 10010: /* WTPI_C1_CopyToOutputBuffer */ + { + size_t input_length; + OPK_Init_size_t((size_t*)&input_length); + uint8_t* input; + OPK_InitPointer((uint8_t**)&input); + OPK_OutputBuffer* out = + (OPK_OutputBuffer*)OPK_VarAlloc(sizeof(OPK_OutputBuffer)); + OPK_Init_OPK_OutputBuffer((OPK_OutputBuffer*)out); + size_t output_offset; + OPK_Init_size_t((size_t*)&output_offset); + OPK_Unpack_C1_CopyToOutputBuffer_Request(request, &input, &input_length, + &out, &output_offset); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("C1_CopyToOutputBuffer"); + result = + WTPI_C1_CopyToOutputBuffer(input, input_length, out, output_offset); + *response = OPK_Pack_C1_CopyToOutputBuffer_Response(result); + break; + } + case 10011: /* WTPI_C1_RandomBytes */ + { + size_t out_length; + OPK_Init_size_t((size_t*)&out_length); + uint8_t* out; + OPK_InitPointer((uint8_t**)&out); + OPK_Unpack_C1_RandomBytes_Request(request, &out, &out_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("C1_RandomBytes"); + result = WTPI_C1_RandomBytes(out, out_length); + *response = OPK_Pack_C1_RandomBytes_Response(result, out, out_length); + break; + } + case 10012: /* WTPI_K1_InitializeKeyManagement */ + { + OPK_Unpack_K1_InitializeKeyManagement_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("K1_InitializeKeyManagement"); + result = WTPI_K1_InitializeKeyManagement(); + *response = OPK_Pack_K1_InitializeKeyManagement_Response(result); + break; + } + case 10013: /* WTPI_K1_TerminateKeyManagement */ + { + OPK_Unpack_K1_TerminateKeyManagement_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("K1_TerminateKeyManagement"); + result = WTPI_K1_TerminateKeyManagement(); + *response = OPK_Pack_K1_TerminateKeyManagement_Response(result); + break; + } + case 10014: /* WTPI_K1_CreateKeyHandle */ + { + size_t input_length; + OPK_Init_size_t((size_t*)&input_length); + uint8_t* input; + OPK_InitPointer((uint8_t**)&input); + SymmetricKeyType key_type; + OPK_Init_SymmetricKeyType((SymmetricKeyType*)&key_type); + WTPI_K1_SymmetricKey_Handle* out_key_handle; + OPK_InitPointer((uint8_t**)&out_key_handle); + OPK_Unpack_K1_CreateKeyHandle_Request(request, &input, &input_length, + &key_type, &out_key_handle); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("K1_CreateKeyHandle"); + result = WTPI_K1_CreateKeyHandle(input, input_length, key_type, + out_key_handle); + *response = OPK_Pack_K1_CreateKeyHandle_Response(result, out_key_handle); + break; + } + case 10015: /* WTPI_K1_DeriveDeviceKeyIntoHandle */ + { + uint32_t context; + OPK_Init_uint32_t((uint32_t*)&context); + SymmetricKeyType out_key_type; + OPK_Init_SymmetricKeyType((SymmetricKeyType*)&out_key_type); + WTPI_K1_SymmetricKey_Handle* out_key_handle; + OPK_InitPointer((uint8_t**)&out_key_handle); + KeySize out_key_size; + OPK_Init_KeySize((KeySize*)&out_key_size); + OPK_Unpack_K1_DeriveDeviceKeyIntoHandle_Request( + request, &context, &out_key_type, &out_key_handle, &out_key_size); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("K1_DeriveDeviceKeyIntoHandle"); + result = WTPI_K1_DeriveDeviceKeyIntoHandle(context, out_key_type, + out_key_handle, out_key_size); + *response = OPK_Pack_K1_DeriveDeviceKeyIntoHandle_Response( + result, out_key_handle); + break; + } + case 10016: /* WTPI_K1_AESDecryptAndCreateKeyHandle */ + { + size_t enc_key_length; + OPK_Init_size_t((size_t*)&enc_key_length); + WTPI_K1_SymmetricKey_Handle decrypt_key_handle; + OPK_Init_WTPI_K1_SymmetricKey_Handle( + (WTPI_K1_SymmetricKey_Handle*)&decrypt_key_handle); + uint8_t* enc_key; + OPK_InitPointer((uint8_t**)&enc_key); + uint8_t iv[16]; + OPK_InitMemory(&iv[0], 16); + SymmetricKeyType key_type; + OPK_Init_SymmetricKeyType((SymmetricKeyType*)&key_type); + WTPI_K1_SymmetricKey_Handle* out_key_handle; + OPK_InitPointer((uint8_t**)&out_key_handle); + OPK_Unpack_K1_AESDecryptAndCreateKeyHandle_Request( + request, &decrypt_key_handle, &enc_key, &enc_key_length, &iv[0], + &key_type, &out_key_handle); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("K1_AESDecryptAndCreateKeyHandle"); + result = WTPI_K1_AESDecryptAndCreateKeyHandle(decrypt_key_handle, enc_key, + enc_key_length, iv, + key_type, out_key_handle); + *response = OPK_Pack_K1_AESDecryptAndCreateKeyHandle_Response( + result, out_key_handle); + break; + } + case 10017: /* WTPI_K1_AESDecryptAndCreateKeyHandleForMacKeys */ + { + size_t enc_mac_keys_length; + OPK_Init_size_t((size_t*)&enc_mac_keys_length); + WTPI_K1_SymmetricKey_Handle decrypt_key_handle; + OPK_Init_WTPI_K1_SymmetricKey_Handle( + (WTPI_K1_SymmetricKey_Handle*)&decrypt_key_handle); + uint8_t* enc_mac_keys; + OPK_InitPointer((uint8_t**)&enc_mac_keys); + uint8_t iv[16]; + OPK_InitMemory(&iv[0], 16); + WTPI_K1_SymmetricKey_Handle* out_mac_key_server; + OPK_InitPointer((uint8_t**)&out_mac_key_server); + WTPI_K1_SymmetricKey_Handle* out_mac_key_client; + OPK_InitPointer((uint8_t**)&out_mac_key_client); + OPK_Unpack_K1_AESDecryptAndCreateKeyHandleForMacKeys_Request( + request, &decrypt_key_handle, &enc_mac_keys, &enc_mac_keys_length, + &iv[0], &out_mac_key_server, &out_mac_key_client); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("K1_AESDecryptAndCreateKeyHandleForMacKeys"); + result = WTPI_K1_AESDecryptAndCreateKeyHandleForMacKeys( + decrypt_key_handle, enc_mac_keys, enc_mac_keys_length, iv, + out_mac_key_server, out_mac_key_client); + *response = OPK_Pack_K1_AESDecryptAndCreateKeyHandleForMacKeys_Response( + result, out_mac_key_server, out_mac_key_client); + break; + } + case 10018: /* WTPI_K1_DeriveKeyFromKeyHandle */ + { + size_t context_length; + OPK_Init_size_t((size_t*)&context_length); + WTPI_K1_SymmetricKey_Handle key_handle; + OPK_Init_WTPI_K1_SymmetricKey_Handle( + (WTPI_K1_SymmetricKey_Handle*)&key_handle); + uint8_t counter; + OPK_Init_uint8_t((uint8_t*)&counter); + uint8_t* context; + OPK_InitPointer((uint8_t**)&context); + SymmetricKeyType out_key_type; + OPK_Init_SymmetricKeyType((SymmetricKeyType*)&out_key_type); + KeySize out_key_size; + OPK_Init_KeySize((KeySize*)&out_key_size); + WTPI_K1_SymmetricKey_Handle* out_key_handle; + OPK_InitPointer((uint8_t**)&out_key_handle); + OPK_Unpack_K1_DeriveKeyFromKeyHandle_Request( + request, &key_handle, &counter, &context, &context_length, + &out_key_type, &out_key_size, &out_key_handle); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("K1_DeriveKeyFromKeyHandle"); + result = WTPI_K1_DeriveKeyFromKeyHandle(key_handle, counter, context, + context_length, out_key_type, + out_key_size, out_key_handle); + *response = + OPK_Pack_K1_DeriveKeyFromKeyHandle_Response(result, out_key_handle); + break; + } + case 10019: /* WTPI_K1_WrapKey */ + { + size_t wrapped_key_length; + OPK_Init_size_t((size_t*)&wrapped_key_length); + uint32_t context; + OPK_Init_uint32_t((uint32_t*)&context); + WTPI_K1_SymmetricKey_Handle key_handle; + OPK_Init_WTPI_K1_SymmetricKey_Handle( + (WTPI_K1_SymmetricKey_Handle*)&key_handle); + SymmetricKeyType key_type; + OPK_Init_SymmetricKeyType((SymmetricKeyType*)&key_type); + uint8_t* wrapped_key; + OPK_InitPointer((uint8_t**)&wrapped_key); + OPK_Unpack_K1_WrapKey_Request(request, &context, &key_handle, &key_type, + &wrapped_key, &wrapped_key_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("K1_WrapKey"); + result = WTPI_K1_WrapKey(context, key_handle, key_type, wrapped_key, + wrapped_key_length); + *response = + OPK_Pack_K1_WrapKey_Response(result, wrapped_key, wrapped_key_length); + break; + } + case 10020: /* WTPI_K1_UnwrapIntoKeyHandle */ + { + size_t wrapped_key_length; + OPK_Init_size_t((size_t*)&wrapped_key_length); + uint32_t context; + OPK_Init_uint32_t((uint32_t*)&context); + uint8_t* wrapped_key; + OPK_InitPointer((uint8_t**)&wrapped_key); + SymmetricKeyType key_type; + OPK_Init_SymmetricKeyType((SymmetricKeyType*)&key_type); + WTPI_K1_SymmetricKey_Handle* out_key_handle; + OPK_InitPointer((uint8_t**)&out_key_handle); + OPK_Unpack_K1_UnwrapIntoKeyHandle_Request(request, &context, &wrapped_key, + &wrapped_key_length, &key_type, + &out_key_handle); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("K1_UnwrapIntoKeyHandle"); + result = WTPI_K1_UnwrapIntoKeyHandle( + context, wrapped_key, wrapped_key_length, key_type, out_key_handle); + *response = + OPK_Pack_K1_UnwrapIntoKeyHandle_Response(result, out_key_handle); + break; + } + case 10021: /* WTPI_K1_FreeKeyHandle */ + { + WTPI_K1_SymmetricKey_Handle key_handle; + OPK_Init_WTPI_K1_SymmetricKey_Handle( + (WTPI_K1_SymmetricKey_Handle*)&key_handle); + OPK_Unpack_K1_FreeKeyHandle_Request(request, &key_handle); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("K1_FreeKeyHandle"); + result = WTPI_K1_FreeKeyHandle(key_handle); + *response = OPK_Pack_K1_FreeKeyHandle_Response(result); + break; + } + case 10022: /* WTPI_CreateAsymmetricKeyHandle */ + { + size_t input_length; + OPK_Init_size_t((size_t*)&input_length); + uint8_t* input; + OPK_InitPointer((uint8_t**)&input); + AsymmetricKeyType key_type; + OPK_Init_AsymmetricKeyType((AsymmetricKeyType*)&key_type); + WTPI_AsymmetricKey_Handle* key_handle; + OPK_InitPointer((uint8_t**)&key_handle); + OPK_Unpack_CreateAsymmetricKeyHandle_Request( + request, &input, &input_length, &key_type, &key_handle); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("CreateAsymmetricKeyHandle"); + result = WTPI_CreateAsymmetricKeyHandle(input, input_length, key_type, + key_handle); + *response = + OPK_Pack_CreateAsymmetricKeyHandle_Response(result, key_handle); + break; + } + case 10023: /* WTPI_UnwrapIntoAsymmetricKeyHandle */ + { + size_t input_length; + OPK_Init_size_t((size_t*)&input_length); + uint8_t* input; + OPK_InitPointer((uint8_t**)&input); + AsymmetricKeyType key_type; + OPK_Init_AsymmetricKeyType((AsymmetricKeyType*)&key_type); + WTPI_AsymmetricKey_Handle* key_handle; + OPK_InitPointer((uint8_t**)&key_handle); + uint32_t* allowed_schemes; + OPK_InitPointer((uint8_t**)&allowed_schemes); + OPK_Unpack_UnwrapIntoAsymmetricKeyHandle_Request( + request, &input, &input_length, &key_type, &key_handle, + &allowed_schemes); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("UnwrapIntoAsymmetricKeyHandle"); + result = WTPI_UnwrapIntoAsymmetricKeyHandle(input, input_length, key_type, + key_handle, allowed_schemes); + *response = OPK_Pack_UnwrapIntoAsymmetricKeyHandle_Response( + result, key_handle, allowed_schemes); + break; + } + case 10024: /* WTPI_FreeAsymmetricKeyHandle */ + { + WTPI_AsymmetricKey_Handle key_handle; + OPK_Init_WTPI_AsymmetricKey_Handle( + (WTPI_AsymmetricKey_Handle*)&key_handle); + OPK_Unpack_FreeAsymmetricKeyHandle_Request(request, &key_handle); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("FreeAsymmetricKeyHandle"); + result = WTPI_FreeAsymmetricKeyHandle(key_handle); + *response = OPK_Pack_FreeAsymmetricKeyHandle_Response(result); + break; + } + case 10025: /* WTPI_GetWrappedAsymmetricKeySize */ + { + size_t enc_private_key_length; + OPK_Init_size_t((size_t*)&enc_private_key_length); + AsymmetricKeyType key_type; + OPK_Init_AsymmetricKeyType((AsymmetricKeyType*)&key_type); + size_t* buffer_size; + OPK_InitPointer((uint8_t**)&buffer_size); + OPK_Unpack_GetWrappedAsymmetricKeySize_Request( + request, &enc_private_key_length, &key_type, &buffer_size); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GetWrappedAsymmetricKeySize"); + result = WTPI_GetWrappedAsymmetricKeySize(enc_private_key_length, + key_type, buffer_size); + *response = + OPK_Pack_GetWrappedAsymmetricKeySize_Response(result, buffer_size); + break; + } + case 10026: /* WTPI_WrapAsymmetricKey */ + { + size_t output_length; + OPK_Init_size_t((size_t*)&output_length); + size_t clear_key_length; + OPK_Init_size_t((size_t*)&clear_key_length); + uint8_t* output; + OPK_InitPointer((uint8_t**)&output); + AsymmetricKeyType key_type; + OPK_Init_AsymmetricKeyType((AsymmetricKeyType*)&key_type); + uint8_t* clear_key; + OPK_InitPointer((uint8_t**)&clear_key); + OPK_Unpack_WrapAsymmetricKey_Request(request, &output, &output_length, + &key_type, &clear_key, + &clear_key_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("WrapAsymmetricKey"); + result = WTPI_WrapAsymmetricKey(output, output_length, key_type, + clear_key, clear_key_length); + *response = + OPK_Pack_WrapAsymmetricKey_Response(result, output, output_length); + break; + } + case 10027: /* WTPI_RSASign */ + { + size_t message_length; + OPK_Init_size_t((size_t*)&message_length); + size_t* signature_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(signature_length); + WTPI_AsymmetricKey_Handle key; + OPK_Init_WTPI_AsymmetricKey_Handle((WTPI_AsymmetricKey_Handle*)&key); + uint8_t* message; + OPK_InitPointer((uint8_t**)&message); + uint8_t* signature; + OPK_InitPointer((uint8_t**)&signature); + RSA_Padding_Scheme padding_scheme; + OPK_Init_RSA_Padding_Scheme((RSA_Padding_Scheme*)&padding_scheme); + OPK_Unpack_RSASign_Request(request, &key, &message, &message_length, + &signature, &signature_length, + &padding_scheme); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("RSASign"); + result = WTPI_RSASign(key, message, message_length, signature, + signature_length, padding_scheme); + *response = + OPK_Pack_RSASign_Response(result, signature, signature_length); + break; + } + case 10028: /* WTPI_RSADecrypt */ + { + size_t input_length; + OPK_Init_size_t((size_t*)&input_length); + size_t* out_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(out_length); + WTPI_AsymmetricKey_Handle key; + OPK_Init_WTPI_AsymmetricKey_Handle((WTPI_AsymmetricKey_Handle*)&key); + uint8_t* input; + OPK_InitPointer((uint8_t**)&input); + uint8_t* out; + OPK_InitPointer((uint8_t**)&out); + OPK_Unpack_RSADecrypt_Request(request, &key, &input, &input_length, &out, + &out_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("RSADecrypt"); + result = WTPI_RSADecrypt(key, input, input_length, out, out_length); + *response = OPK_Pack_RSADecrypt_Response(result, out, out_length); + break; + } + case 10029: /* WTPI_ECCSign */ + { + size_t message_length; + OPK_Init_size_t((size_t*)&message_length); + size_t* signature_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(signature_length); + WTPI_AsymmetricKey_Handle key; + OPK_Init_WTPI_AsymmetricKey_Handle((WTPI_AsymmetricKey_Handle*)&key); + uint8_t* message; + OPK_InitPointer((uint8_t**)&message); + uint8_t* signature; + OPK_InitPointer((uint8_t**)&signature); + OPK_Unpack_ECCSign_Request(request, &key, &message, &message_length, + &signature, &signature_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("ECCSign"); + result = WTPI_ECCSign(key, message, message_length, signature, + signature_length); + *response = + OPK_Pack_ECCSign_Response(result, signature, signature_length); + break; + } + case 10030: /* WTPI_ECCDeriveSessionKey */ + { + size_t key_source_length; + OPK_Init_size_t((size_t*)&key_source_length); + size_t* session_key_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(session_key_length); + WTPI_AsymmetricKey_Handle key; + OPK_Init_WTPI_AsymmetricKey_Handle((WTPI_AsymmetricKey_Handle*)&key); + uint8_t* key_source; + OPK_InitPointer((uint8_t**)&key_source); + uint8_t* session_key; + OPK_InitPointer((uint8_t**)&session_key); + OPK_Unpack_ECCDeriveSessionKey_Request(request, &key, &key_source, + &key_source_length, &session_key, + &session_key_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("ECCDeriveSessionKey"); + result = WTPI_ECCDeriveSessionKey(key, key_source, key_source_length, + session_key, session_key_length); + *response = OPK_Pack_ECCDeriveSessionKey_Response(result, session_key, + session_key_length); + break; + } + case 10031: /* WTPI_GetSignatureSize */ + { + WTPI_AsymmetricKey_Handle key; + OPK_Init_WTPI_AsymmetricKey_Handle((WTPI_AsymmetricKey_Handle*)&key); + size_t* signature_length; + OPK_InitPointer((uint8_t**)&signature_length); + OPK_Unpack_GetSignatureSize_Request(request, &key, &signature_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GetSignatureSize"); + result = WTPI_GetSignatureSize(key, signature_length); + *response = OPK_Pack_GetSignatureSize_Response(result, signature_length); + break; + } + case 10032: /* WTPI_ED25519Sign */ + { + size_t message_length; + OPK_Init_size_t((size_t*)&message_length); + size_t* signature_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(signature_length); + WTPI_AsymmetricKey_Handle key; + OPK_Init_WTPI_AsymmetricKey_Handle((WTPI_AsymmetricKey_Handle*)&key); + uint8_t* message; + OPK_InitPointer((uint8_t**)&message); + uint8_t* signature; + OPK_InitPointer((uint8_t**)&signature); + OPK_Unpack_ED25519Sign_Request(request, &key, &message, &message_length, + &signature, &signature_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("ED25519Sign"); + result = WTPI_ED25519Sign(key, message, message_length, signature, + signature_length); + *response = + OPK_Pack_ED25519Sign_Response(result, signature, signature_length); + break; + } + case 10033: /* WTPI_GetBootCertificateChain */ + { + size_t* out_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(out_length); + uint8_t* out; + OPK_InitPointer((uint8_t**)&out); + OPK_Unpack_GetBootCertificateChain_Request(request, &out, &out_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GetBootCertificateChain"); + result = WTPI_GetBootCertificateChain(out, out_length); + *response = + OPK_Pack_GetBootCertificateChain_Response(result, out, out_length); + break; + } + case 10034: /* WTPI_GenerateRandomCertificateKeyPair */ + { + size_t* wrapped_private_key_length = + (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(wrapped_private_key_length); + size_t* public_key_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(public_key_length); + AsymmetricKeyType* key_type; + OPK_InitPointer((uint8_t**)&key_type); + uint8_t* wrapped_private_key; + OPK_InitPointer((uint8_t**)&wrapped_private_key); + uint8_t* public_key; + OPK_InitPointer((uint8_t**)&public_key); + OPK_Unpack_GenerateRandomCertificateKeyPair_Request( + request, &key_type, &wrapped_private_key, &wrapped_private_key_length, + &public_key, &public_key_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GenerateRandomCertificateKeyPair"); + result = WTPI_GenerateRandomCertificateKeyPair( + key_type, wrapped_private_key, wrapped_private_key_length, public_key, + public_key_length); + *response = OPK_Pack_GenerateRandomCertificateKeyPair_Response( + result, key_type, wrapped_private_key, wrapped_private_key_length, + public_key, public_key_length); + break; + } + case 10035: /* WTPI_DeviceKeyCoseSign1 */ + { + size_t message_length; + OPK_Init_size_t((size_t*)&message_length); + size_t* signature_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(signature_length); + uint8_t* message; + OPK_InitPointer((uint8_t**)&message); + uint8_t* signature; + OPK_InitPointer((uint8_t**)&signature); + OPK_Unpack_DeviceKeyCoseSign1_Request(request, &message, &message_length, + &signature, &signature_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("DeviceKeyCoseSign1"); + result = WTPI_DeviceKeyCoseSign1(message, message_length, signature, + signature_length); + *response = OPK_Pack_DeviceKeyCoseSign1_Response(result, signature, + signature_length); + break; + } + case 10036: /* WTPI_Crc32Init */ + { + uint32_t* initial_hash; + OPK_InitPointer((uint8_t**)&initial_hash); + OPK_Unpack_Crc32Init_Request(request, &initial_hash); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("Crc32Init"); + result = WTPI_Crc32Init(initial_hash); + *response = OPK_Pack_Crc32Init_Response(result, initial_hash); + break; + } + case 10037: /* WTPI_Crc32Cont */ + { + size_t in_length; + OPK_Init_size_t((size_t*)&in_length); + uint8_t* in; + OPK_InitPointer((uint8_t**)&in); + uint32_t prev_crc; + OPK_Init_uint32_t((uint32_t*)&prev_crc); + uint32_t* new_crc; + OPK_InitPointer((uint8_t**)&new_crc); + OPK_Unpack_Crc32Cont_Request(request, &in, &in_length, &prev_crc, + &new_crc); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("Crc32Cont"); + result = WTPI_Crc32Cont(in, in_length, prev_crc, new_crc); + *response = OPK_Pack_Crc32Cont_Response(result, new_crc); + break; + } + case 10038: /* WTPI_Crc32Cont_OutputBuffer */ + { + size_t in_length; + OPK_Init_size_t((size_t*)&in_length); + OPK_OutputBuffer* in; + OPK_InitPointer((uint8_t**)&in); + size_t in_offset; + OPK_Init_size_t((size_t*)&in_offset); + uint32_t prev_crc; + OPK_Init_uint32_t((uint32_t*)&prev_crc); + uint32_t* new_crc; + OPK_InitPointer((uint8_t**)&new_crc); + OPK_Unpack_Crc32Cont_OutputBuffer_Request( + request, &in, &in_offset, &in_length, &prev_crc, &new_crc); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("Crc32Cont_OutputBuffer"); + result = WTPI_Crc32Cont_OutputBuffer(in, in_offset, in_length, prev_crc, + new_crc); + *response = OPK_Pack_Crc32Cont_OutputBuffer_Response(result, new_crc); + break; + } + case 10039: /* WTPI_GetTrustedTime */ + { + uint64_t* time_in_s; + OPK_InitPointer((uint8_t**)&time_in_s); + OPK_Unpack_GetTrustedTime_Request(request, &time_in_s); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GetTrustedTime"); + result = WTPI_GetTrustedTime(time_in_s); + *response = OPK_Pack_GetTrustedTime_Response(result, time_in_s); + break; + } + case 10040: /* WTPI_InitializeClock */ + { + OPK_Unpack_InitializeClock_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("InitializeClock"); + result = WTPI_InitializeClock(); + *response = OPK_Pack_InitializeClock_Response(result); + break; + } + case 10041: /* WTPI_TerminateClock */ + { + OPK_Unpack_TerminateClock_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("TerminateClock"); + result = WTPI_TerminateClock(); + *response = OPK_Pack_TerminateClock_Response(result); + break; + } + case 10042: /* WTPI_GetClockType */ + { + OPK_Unpack_GetClockType_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCrypto_Clock_Security_Level result; + OPK_Init_OEMCrypto_Clock_Security_Level( + (OEMCrypto_Clock_Security_Level*)&result); + LOGD("GetClockType"); + result = WTPI_GetClockType(); + *response = OPK_Pack_GetClockType_Response(result); + break; + } + default: + return MESSAGE_STATUS_API_VALUE_ERROR; + } + return ODK_Message_GetStatus(response); + +handle_invalid_request: + LOGE("invalid request"); + *response = CreateEmptyMessage(); + return MESSAGE_STATUS_OK; +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/tee/GEN_tee_serializer.c b/oemcrypto/opk/oemcrypto_ta/wtpi_test/tee/GEN_tee_serializer.c new file mode 100644 index 0000000..e2b60ed --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/tee/GEN_tee_serializer.c @@ -0,0 +1,1254 @@ +/* + * Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine License + * Agreement. + */ + +/* + * This code is auto-generated, do not edit + */ +#include "GEN_tee_serializer.h" + +#include +#include + +#include "GEN_common_serializer.h" +#include "bump_allocator.h" +#include "common_special_cases.h" +#include "log_macros.h" +#include "marshaller_base.h" +#include "odk_overflow.h" +#include "opk_serialization_base.h" +#include "shared_buffer_allocator.h" +#include "tee_special_cases.h" +#include "tos_transport_interface.h" +#include "wtpi_crc32_interface.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" +#include "wtpi_crypto_asymmetric_interface.h" + +void OPK_Unpack_PrepareGenerationNumber_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10000) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_PrepareGenerationNumber_Response(OEMCryptoResult result) { + uint32_t api_value = 10000; /* from _tee10000 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadGenerationNumber_Request(ODK_Message* msg, + uint64_t** value) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10001) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + *value = (uint64_t*)OPK_UnpackAlloc(msg, sizeof(uint64_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_LoadGenerationNumber_Response(OEMCryptoResult result, + const uint64_t* value) { + uint32_t api_value = 10001; /* from _tee10001 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_uint64_t(&msg, value); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_SaveGenerationNumber_Request(ODK_Message* msg, + uint64_t* value) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10002) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_uint64_t(msg, value); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_SaveGenerationNumber_Response(OEMCryptoResult result) { + uint32_t api_value = 10002; /* from _tee10002 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_K1_GetKeySize_Request(ODK_Message* msg, + WTPI_K1_SymmetricKey_Handle* key_handle, + KeySize** size) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10003) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_WTPI_K1_SymmetricKey_Handle(msg, key_handle); + *size = (KeySize*)OPK_UnpackAlloc(msg, sizeof(KeySize)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_K1_GetKeySize_Response(OEMCryptoResult result, + const KeySize* size) { + uint32_t api_value = 10003; /* from _tee10003 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_KeySize(&msg, size); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_C1_AESCBCDecrypt_Request( + ODK_Message* msg, WTPI_K1_SymmetricKey_Handle* key_handle, + size_t* key_length, uint8_t** in_buffer, size_t* in_buffer_length, + uint8_t* iv, uint8_t** out_buffer) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10004) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, in_buffer_length); + OPK_Unpack_WTPI_K1_SymmetricKey_Handle(msg, key_handle); + OPK_Unpack_size_t(msg, key_length); + OPK_UnpackInPlace(msg, (uint8_t**)in_buffer, + OPK_FromSizeTPtr(in_buffer_length)); + OPK_UnpackArray(msg, &iv[0], 16); + *out_buffer = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtr(in_buffer_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_C1_AESCBCDecrypt_Response(OEMCryptoResult result, + size_t in_buffer_length, + const uint8_t* out_buffer) { + uint32_t api_value = 10004; /* from _tee10004 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_size_t(&msg, &in_buffer_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)out_buffer, + OPK_ToLengthType(in_buffer_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_C1_AESCBCEncrypt_Request( + ODK_Message* msg, WTPI_K1_SymmetricKey_Handle* key_handle, + uint8_t** in_buffer, size_t* in_buffer_length, uint8_t* iv, + uint8_t** out_buffer) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10005) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, in_buffer_length); + OPK_Unpack_WTPI_K1_SymmetricKey_Handle(msg, key_handle); + OPK_UnpackInPlace(msg, (uint8_t**)in_buffer, + OPK_FromSizeTPtr(in_buffer_length)); + OPK_UnpackArray(msg, &iv[0], 16); + *out_buffer = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtr(in_buffer_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_C1_AESCBCEncrypt_Response(OEMCryptoResult result, + size_t in_buffer_length, + const uint8_t* out_buffer) { + uint32_t api_value = 10005; /* from _tee10005 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_size_t(&msg, &in_buffer_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)out_buffer, + OPK_ToLengthType(in_buffer_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +ODK_Message OPK_Pack_C1_HMAC_SHA256_Verify_Response(OEMCryptoResult result) { + uint32_t api_value = 10009; /* from _tee10009 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_C1_CopyToOutputBuffer_Request(ODK_Message* msg, uint8_t** input, + size_t* input_length, + OPK_OutputBuffer** out, + size_t* output_offset) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10010) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, input_length); + OPK_UnpackInPlace(msg, (uint8_t**)input, OPK_FromSizeTPtr(input_length)); + OPK_UnpackNullable_OPK_OutputBuffer(msg, out); + OPK_Unpack_size_t(msg, output_offset); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_C1_CopyToOutputBuffer_Response(OEMCryptoResult result) { + uint32_t api_value = 10010; /* from _tee10010 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_C1_RandomBytes_Request(ODK_Message* msg, uint8_t** out, + size_t* out_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10011) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, out_length); + *out = (uint8_t*)OPK_UnpackAllocBuffer(msg, OPK_FromSizeTPtr(out_length), + sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_C1_RandomBytes_Response(OEMCryptoResult result, + const uint8_t* out, + size_t out_length) { + uint32_t api_value = 10011; /* from _tee10011 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_size_t(&msg, &out_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)out, OPK_ToLengthType(out_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_K1_InitializeKeyManagement_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10012) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_K1_InitializeKeyManagement_Response( + OEMCryptoResult result) { + uint32_t api_value = 10012; /* from _tee10012 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_K1_TerminateKeyManagement_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10013) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_K1_TerminateKeyManagement_Response( + OEMCryptoResult result) { + uint32_t api_value = 10013; /* from _tee10013 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_K1_CreateKeyHandle_Request( + ODK_Message* msg, uint8_t** input, size_t* input_length, + SymmetricKeyType* key_type, WTPI_K1_SymmetricKey_Handle** out_key_handle) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10014) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, input_length); + OPK_UnpackInPlace(msg, (uint8_t**)input, OPK_FromSizeTPtr(input_length)); + OPK_Unpack_SymmetricKeyType(msg, key_type); + *out_key_handle = (WTPI_K1_SymmetricKey_Handle*)OPK_UnpackAlloc( + msg, sizeof(WTPI_K1_SymmetricKey_Handle)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_K1_CreateKeyHandle_Response( + OEMCryptoResult result, const WTPI_K1_SymmetricKey_Handle* out_key_handle) { + uint32_t api_value = 10014; /* from _tee10014 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_WTPI_K1_SymmetricKey_Handle(&msg, out_key_handle); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_K1_DeriveDeviceKeyIntoHandle_Request( + ODK_Message* msg, uint32_t* context, SymmetricKeyType* out_key_type, + WTPI_K1_SymmetricKey_Handle** out_key_handle, KeySize* out_key_size) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10015) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_uint32_t(msg, context); + OPK_Unpack_SymmetricKeyType(msg, out_key_type); + *out_key_handle = (WTPI_K1_SymmetricKey_Handle*)OPK_UnpackAlloc( + msg, sizeof(WTPI_K1_SymmetricKey_Handle)); + OPK_Unpack_KeySize(msg, out_key_size); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_K1_DeriveDeviceKeyIntoHandle_Response( + OEMCryptoResult result, const WTPI_K1_SymmetricKey_Handle* out_key_handle) { + uint32_t api_value = 10015; /* from _tee10015 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_WTPI_K1_SymmetricKey_Handle(&msg, out_key_handle); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_K1_AESDecryptAndCreateKeyHandle_Request( + ODK_Message* msg, WTPI_K1_SymmetricKey_Handle* decrypt_key_handle, + uint8_t** enc_key, size_t* enc_key_length, uint8_t* iv, + SymmetricKeyType* key_type, WTPI_K1_SymmetricKey_Handle** out_key_handle) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10016) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, enc_key_length); + OPK_Unpack_WTPI_K1_SymmetricKey_Handle(msg, decrypt_key_handle); + OPK_UnpackInPlace(msg, (uint8_t**)enc_key, OPK_FromSizeTPtr(enc_key_length)); + OPK_UnpackArray(msg, &iv[0], 16); + OPK_Unpack_SymmetricKeyType(msg, key_type); + *out_key_handle = (WTPI_K1_SymmetricKey_Handle*)OPK_UnpackAlloc( + msg, sizeof(WTPI_K1_SymmetricKey_Handle)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_K1_AESDecryptAndCreateKeyHandle_Response( + OEMCryptoResult result, const WTPI_K1_SymmetricKey_Handle* out_key_handle) { + uint32_t api_value = 10016; /* from _tee10016 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_WTPI_K1_SymmetricKey_Handle(&msg, out_key_handle); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_K1_AESDecryptAndCreateKeyHandleForMacKeys_Request( + ODK_Message* msg, WTPI_K1_SymmetricKey_Handle* decrypt_key_handle, + uint8_t** enc_mac_keys, size_t* enc_mac_keys_length, uint8_t* iv, + WTPI_K1_SymmetricKey_Handle** out_mac_key_server, + WTPI_K1_SymmetricKey_Handle** out_mac_key_client) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10017) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, enc_mac_keys_length); + OPK_Unpack_WTPI_K1_SymmetricKey_Handle(msg, decrypt_key_handle); + OPK_UnpackInPlace(msg, (uint8_t**)enc_mac_keys, + OPK_FromSizeTPtr(enc_mac_keys_length)); + OPK_UnpackArray(msg, &iv[0], 16); + *out_mac_key_server = (WTPI_K1_SymmetricKey_Handle*)OPK_UnpackAlloc( + msg, sizeof(WTPI_K1_SymmetricKey_Handle)); + *out_mac_key_client = (WTPI_K1_SymmetricKey_Handle*)OPK_UnpackAlloc( + msg, sizeof(WTPI_K1_SymmetricKey_Handle)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_K1_AESDecryptAndCreateKeyHandleForMacKeys_Response( + OEMCryptoResult result, + const WTPI_K1_SymmetricKey_Handle* out_mac_key_server, + const WTPI_K1_SymmetricKey_Handle* out_mac_key_client) { + uint32_t api_value = 10017; /* from _tee10017 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_WTPI_K1_SymmetricKey_Handle(&msg, out_mac_key_server); + OPK_PackNullable_WTPI_K1_SymmetricKey_Handle(&msg, out_mac_key_client); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_K1_DeriveKeyFromKeyHandle_Request( + ODK_Message* msg, WTPI_K1_SymmetricKey_Handle* key_handle, uint8_t* counter, + uint8_t** context, size_t* context_length, SymmetricKeyType* out_key_type, + KeySize* out_key_size, WTPI_K1_SymmetricKey_Handle** out_key_handle) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10018) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, context_length); + OPK_Unpack_WTPI_K1_SymmetricKey_Handle(msg, key_handle); + OPK_Unpack_uint8_t(msg, counter); + OPK_UnpackInPlace(msg, (uint8_t**)context, OPK_FromSizeTPtr(context_length)); + OPK_Unpack_SymmetricKeyType(msg, out_key_type); + OPK_Unpack_KeySize(msg, out_key_size); + *out_key_handle = (WTPI_K1_SymmetricKey_Handle*)OPK_UnpackAlloc( + msg, sizeof(WTPI_K1_SymmetricKey_Handle)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_K1_DeriveKeyFromKeyHandle_Response( + OEMCryptoResult result, const WTPI_K1_SymmetricKey_Handle* out_key_handle) { + uint32_t api_value = 10018; /* from _tee10018 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_WTPI_K1_SymmetricKey_Handle(&msg, out_key_handle); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_K1_WrapKey_Request(ODK_Message* msg, uint32_t* context, + WTPI_K1_SymmetricKey_Handle* key_handle, + SymmetricKeyType* key_type, + uint8_t** wrapped_key, + size_t* wrapped_key_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10019) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, wrapped_key_length); + OPK_Unpack_uint32_t(msg, context); + OPK_Unpack_WTPI_K1_SymmetricKey_Handle(msg, key_handle); + OPK_Unpack_SymmetricKeyType(msg, key_type); + *wrapped_key = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtr(wrapped_key_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_K1_WrapKey_Response(OEMCryptoResult result, + const uint8_t* wrapped_key, + size_t wrapped_key_length) { + uint32_t api_value = 10019; /* from _tee10019 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_size_t(&msg, &wrapped_key_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)wrapped_key, + OPK_ToLengthType(wrapped_key_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_K1_UnwrapIntoKeyHandle_Request( + ODK_Message* msg, uint32_t* context, uint8_t** wrapped_key, + size_t* wrapped_key_length, SymmetricKeyType* key_type, + WTPI_K1_SymmetricKey_Handle** out_key_handle) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10020) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, wrapped_key_length); + OPK_Unpack_uint32_t(msg, context); + OPK_UnpackInPlace(msg, (uint8_t**)wrapped_key, + OPK_FromSizeTPtr(wrapped_key_length)); + OPK_Unpack_SymmetricKeyType(msg, key_type); + *out_key_handle = (WTPI_K1_SymmetricKey_Handle*)OPK_UnpackAlloc( + msg, sizeof(WTPI_K1_SymmetricKey_Handle)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_K1_UnwrapIntoKeyHandle_Response( + OEMCryptoResult result, const WTPI_K1_SymmetricKey_Handle* out_key_handle) { + uint32_t api_value = 10020; /* from _tee10020 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_WTPI_K1_SymmetricKey_Handle(&msg, out_key_handle); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_K1_FreeKeyHandle_Request( + ODK_Message* msg, WTPI_K1_SymmetricKey_Handle* key_handle) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10021) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_WTPI_K1_SymmetricKey_Handle(msg, key_handle); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_K1_FreeKeyHandle_Response(OEMCryptoResult result) { + uint32_t api_value = 10021; /* from _tee10021 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_CreateAsymmetricKeyHandle_Request( + ODK_Message* msg, uint8_t** input, size_t* input_length, + AsymmetricKeyType* key_type, WTPI_AsymmetricKey_Handle** key_handle) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10022) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, input_length); + OPK_UnpackInPlace(msg, (uint8_t**)input, OPK_FromSizeTPtr(input_length)); + OPK_Unpack_AsymmetricKeyType(msg, key_type); + *key_handle = (WTPI_AsymmetricKey_Handle*)OPK_UnpackAlloc( + msg, sizeof(WTPI_AsymmetricKey_Handle)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_CreateAsymmetricKeyHandle_Response( + OEMCryptoResult result, const WTPI_AsymmetricKey_Handle* key_handle) { + uint32_t api_value = 10022; /* from _tee10022 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_WTPI_AsymmetricKey_Handle(&msg, key_handle); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_UnwrapIntoAsymmetricKeyHandle_Request( + ODK_Message* msg, uint8_t** input, size_t* input_length, + AsymmetricKeyType* key_type, WTPI_AsymmetricKey_Handle** key_handle, + uint32_t** allowed_schemes) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10023) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, input_length); + OPK_UnpackInPlace(msg, (uint8_t**)input, OPK_FromSizeTPtr(input_length)); + OPK_Unpack_AsymmetricKeyType(msg, key_type); + *key_handle = (WTPI_AsymmetricKey_Handle*)OPK_UnpackAlloc( + msg, sizeof(WTPI_AsymmetricKey_Handle)); + *allowed_schemes = (uint32_t*)OPK_UnpackAlloc(msg, sizeof(uint32_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_UnwrapIntoAsymmetricKeyHandle_Response( + OEMCryptoResult result, const WTPI_AsymmetricKey_Handle* key_handle, + const uint32_t* allowed_schemes) { + uint32_t api_value = 10023; /* from _tee10023 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_WTPI_AsymmetricKey_Handle(&msg, key_handle); + OPK_PackNullable_uint32_t(&msg, allowed_schemes); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_FreeAsymmetricKeyHandle_Request( + ODK_Message* msg, WTPI_AsymmetricKey_Handle* key_handle) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10024) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_WTPI_AsymmetricKey_Handle(msg, key_handle); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_FreeAsymmetricKeyHandle_Response(OEMCryptoResult result) { + uint32_t api_value = 10024; /* from _tee10024 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetWrappedAsymmetricKeySize_Request( + ODK_Message* msg, size_t* enc_private_key_length, + AsymmetricKeyType* key_type, size_t** buffer_size) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10025) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, enc_private_key_length); + OPK_Unpack_AsymmetricKeyType(msg, key_type); + *buffer_size = (size_t*)OPK_UnpackAlloc(msg, sizeof(size_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GetWrappedAsymmetricKeySize_Response( + OEMCryptoResult result, const size_t* buffer_size) { + uint32_t api_value = 10025; /* from _tee10025 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_size_t(&msg, buffer_size); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_WrapAsymmetricKey_Request(ODK_Message* msg, uint8_t** output, + size_t* output_length, + AsymmetricKeyType* key_type, + uint8_t** clear_key, + size_t* clear_key_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10026) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, output_length); + OPK_Unpack_size_t(msg, clear_key_length); + *output = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtr(output_length), sizeof(uint8_t)); + OPK_Unpack_AsymmetricKeyType(msg, key_type); + OPK_UnpackInPlace(msg, (uint8_t**)clear_key, + OPK_FromSizeTPtr(clear_key_length)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_WrapAsymmetricKey_Response(OEMCryptoResult result, + const uint8_t* output, + size_t output_length) { + uint32_t api_value = 10026; /* from _tee10026 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_size_t(&msg, &output_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)output, + OPK_ToLengthType(output_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_RSASign_Request(ODK_Message* msg, + WTPI_AsymmetricKey_Handle* key, + uint8_t** message, size_t* message_length, + uint8_t** signature, size_t** signature_length, + RSA_Padding_Scheme* padding_scheme) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10027) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, message_length); + OPK_UnpackNullable_size_t(msg, signature_length); + OPK_Unpack_WTPI_AsymmetricKey_Handle(msg, key); + OPK_UnpackInPlace(msg, (uint8_t**)message, OPK_FromSizeTPtr(message_length)); + *signature = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(signature_length), sizeof(uint8_t)); + OPK_Unpack_RSA_Padding_Scheme(msg, padding_scheme); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_RSASign_Response(OEMCryptoResult result, + const uint8_t* signature, + const size_t* signature_length) { + uint32_t api_value = 10027; /* from _tee10027 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, signature_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)signature, + OPK_FromSizeTPtr(signature_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_RSADecrypt_Request(ODK_Message* msg, + WTPI_AsymmetricKey_Handle* key, + uint8_t** input, size_t* input_length, + uint8_t** out, size_t** out_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10028) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, input_length); + OPK_UnpackNullable_size_t(msg, out_length); + OPK_Unpack_WTPI_AsymmetricKey_Handle(msg, key); + OPK_UnpackInPlace(msg, (uint8_t**)input, OPK_FromSizeTPtr(input_length)); + *out = (uint8_t*)OPK_UnpackAllocBuffer(msg, OPK_FromSizeTPtrPtr(out_length), + sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_RSADecrypt_Response(OEMCryptoResult result, + const uint8_t* out, + const size_t* out_length) { + uint32_t api_value = 10028; /* from _tee10028 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, out_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)out, OPK_FromSizeTPtr(out_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_ECCSign_Request(ODK_Message* msg, + WTPI_AsymmetricKey_Handle* key, + uint8_t** message, size_t* message_length, + uint8_t** signature, + size_t** signature_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10029) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, message_length); + OPK_UnpackNullable_size_t(msg, signature_length); + OPK_Unpack_WTPI_AsymmetricKey_Handle(msg, key); + OPK_UnpackInPlace(msg, (uint8_t**)message, OPK_FromSizeTPtr(message_length)); + *signature = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(signature_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_ECCSign_Response(OEMCryptoResult result, + const uint8_t* signature, + const size_t* signature_length) { + uint32_t api_value = 10029; /* from _tee10029 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, signature_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)signature, + OPK_FromSizeTPtr(signature_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_ECCDeriveSessionKey_Request(ODK_Message* msg, + WTPI_AsymmetricKey_Handle* key, + uint8_t** key_source, + size_t* key_source_length, + uint8_t** session_key, + size_t** session_key_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10030) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, key_source_length); + OPK_UnpackNullable_size_t(msg, session_key_length); + OPK_Unpack_WTPI_AsymmetricKey_Handle(msg, key); + OPK_UnpackInPlace(msg, (uint8_t**)key_source, + OPK_FromSizeTPtr(key_source_length)); + *session_key = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(session_key_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_ECCDeriveSessionKey_Response( + OEMCryptoResult result, const uint8_t* session_key, + const size_t* session_key_length) { + uint32_t api_value = 10030; /* from _tee10030 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, session_key_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)session_key, + OPK_FromSizeTPtr(session_key_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetSignatureSize_Request(ODK_Message* msg, + WTPI_AsymmetricKey_Handle* key, + size_t** signature_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10031) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_WTPI_AsymmetricKey_Handle(msg, key); + *signature_length = (size_t*)OPK_UnpackAlloc(msg, sizeof(size_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GetSignatureSize_Response(OEMCryptoResult result, + const size_t* signature_length) { + uint32_t api_value = 10031; /* from _tee10031 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_size_t(&msg, signature_length); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_ED25519Sign_Request(ODK_Message* msg, + WTPI_AsymmetricKey_Handle* key, + uint8_t** message, size_t* message_length, + uint8_t** signature, + size_t** signature_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10032) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, message_length); + OPK_UnpackNullable_size_t(msg, signature_length); + OPK_Unpack_WTPI_AsymmetricKey_Handle(msg, key); + OPK_UnpackInPlace(msg, (uint8_t**)message, OPK_FromSizeTPtr(message_length)); + *signature = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(signature_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_ED25519Sign_Response(OEMCryptoResult result, + const uint8_t* signature, + const size_t* signature_length) { + uint32_t api_value = 10032; /* from _tee10032 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, signature_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)signature, + OPK_FromSizeTPtr(signature_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetBootCertificateChain_Request(ODK_Message* msg, uint8_t** out, + size_t** out_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10033) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackNullable_size_t(msg, out_length); + *out = (uint8_t*)OPK_UnpackAllocBuffer(msg, OPK_FromSizeTPtrPtr(out_length), + sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GetBootCertificateChain_Response( + OEMCryptoResult result, const uint8_t* out, const size_t* out_length) { + uint32_t api_value = 10033; /* from _tee10033 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, out_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)out, OPK_FromSizeTPtr(out_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GenerateRandomCertificateKeyPair_Request( + ODK_Message* msg, AsymmetricKeyType** key_type, + uint8_t** wrapped_private_key, size_t** wrapped_private_key_length, + uint8_t** public_key, size_t** public_key_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10034) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackNullable_size_t(msg, wrapped_private_key_length); + OPK_UnpackNullable_size_t(msg, public_key_length); + *key_type = + (AsymmetricKeyType*)OPK_UnpackAlloc(msg, sizeof(AsymmetricKeyType)); + *wrapped_private_key = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(wrapped_private_key_length), sizeof(uint8_t)); + *public_key = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(public_key_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GenerateRandomCertificateKeyPair_Response( + OEMCryptoResult result, const AsymmetricKeyType* key_type, + const uint8_t* wrapped_private_key, + const size_t* wrapped_private_key_length, const uint8_t* public_key, + const size_t* public_key_length) { + uint32_t api_value = 10034; /* from _tee10034 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, wrapped_private_key_length); + OPK_PackNullable_size_t(&msg, public_key_length); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_AsymmetricKeyType(&msg, key_type); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)wrapped_private_key, + OPK_FromSizeTPtr(wrapped_private_key_length)); + } + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)public_key, + OPK_FromSizeTPtr(public_key_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_DeviceKeyCoseSign1_Request(ODK_Message* msg, uint8_t** message, + size_t* message_length, + uint8_t** signature, + size_t** signature_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10035) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, message_length); + OPK_UnpackNullable_size_t(msg, signature_length); + OPK_UnpackInPlace(msg, (uint8_t**)message, OPK_FromSizeTPtr(message_length)); + *signature = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(signature_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_DeviceKeyCoseSign1_Response( + OEMCryptoResult result, const uint8_t* signature, + const size_t* signature_length) { + uint32_t api_value = 10035; /* from _tee10035 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, signature_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)signature, + OPK_FromSizeTPtr(signature_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Crc32Init_Request(ODK_Message* msg, uint32_t** initial_hash) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10036) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + *initial_hash = (uint32_t*)OPK_UnpackAlloc(msg, sizeof(uint32_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_Crc32Init_Response(OEMCryptoResult result, + const uint32_t* initial_hash) { + uint32_t api_value = 10036; /* from _tee10036 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_uint32_t(&msg, initial_hash); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Crc32Cont_Request(ODK_Message* msg, uint8_t** in, + size_t* in_length, uint32_t* prev_crc, + uint32_t** new_crc) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10037) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, in_length); + OPK_UnpackInPlace(msg, (uint8_t**)in, OPK_FromSizeTPtr(in_length)); + OPK_Unpack_uint32_t(msg, prev_crc); + *new_crc = (uint32_t*)OPK_UnpackAlloc(msg, sizeof(uint32_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_Crc32Cont_Response(OEMCryptoResult result, + const uint32_t* new_crc) { + uint32_t api_value = 10037; /* from _tee10037 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_uint32_t(&msg, new_crc); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Crc32Cont_OutputBuffer_Request( + ODK_Message* msg, OPK_OutputBuffer** in, size_t* in_offset, + size_t* in_length, uint32_t* prev_crc, uint32_t** new_crc) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10038) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, in_length); + /* unpack object array with unpacker function OPK_Unpack_OPK_OutputBuffer */ + ODK_Message* odk_message = msg; + void** address = (void**)in; + LengthType count = OPK_FromSizeTPtr(in_length); + size_t size = sizeof(OPK_OutputBuffer); + if (address) { + *address = NULL; + } + if (!OPK_UnpackIsNull(odk_message)) { + if (address && !OPK_LengthIsNull(count)) { + size_t bytes_to_unpack = 0; + if (odk_mul_overflow_ux(OPK_ToSizeT(count), size, &bytes_to_unpack)) { + ODK_MESSAGE_SETSTATUS(odk_message, MESSAGE_STATUS_PARSE_ERROR); + } else { + *address = OPK_BumpAllocate(bytes_to_unpack); + if (!*address) { + ODK_MESSAGE_SETSTATUS(odk_message, MESSAGE_STATUS_OUT_OF_MEMORY); + } else { + for (size_t i = 0; i < OPK_ToSizeT(count); i++) { + OPK_Unpack_OPK_OutputBuffer( + odk_message, (OPK_OutputBuffer*)((*address) + size * i)); + } + } + } + } + } + OPK_Unpack_size_t(msg, in_offset); + OPK_Unpack_uint32_t(msg, prev_crc); + *new_crc = (uint32_t*)OPK_UnpackAlloc(msg, sizeof(uint32_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_Crc32Cont_OutputBuffer_Response(OEMCryptoResult result, + const uint32_t* new_crc) { + uint32_t api_value = 10038; /* from _tee10038 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_uint32_t(&msg, new_crc); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetTrustedTime_Request(ODK_Message* msg, uint64_t** time_in_s) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10039) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + *time_in_s = (uint64_t*)OPK_UnpackAlloc(msg, sizeof(uint64_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GetTrustedTime_Response(OEMCryptoResult result, + const uint64_t* time_in_s) { + uint32_t api_value = 10039; /* from _tee10039 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_uint64_t(&msg, time_in_s); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_InitializeClock_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10040) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_InitializeClock_Response(OEMCryptoResult result) { + uint32_t api_value = 10040; /* from _tee10040 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_TerminateClock_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10041) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_TerminateClock_Response(OEMCryptoResult result) { + uint32_t api_value = 10041; /* from _tee10041 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetClockType_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10042) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GetClockType_Response( + OEMCrypto_Clock_Security_Level result) { + uint32_t api_value = 10042; /* from _tee10042 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_OEMCrypto_Clock_Security_Level(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/tee/GEN_tee_serializer.h b/oemcrypto/opk/oemcrypto_ta/wtpi_test/tee/GEN_tee_serializer.h new file mode 100644 index 0000000..b159a8d --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/tee/GEN_tee_serializer.h @@ -0,0 +1,233 @@ +/* + * Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine License + * Agreement. + */ + +/* + * This code is auto-generated, do not edit + */ +#ifndef OPK_TEE_SERIALIZER_H_ +#define OPK_TEE_SERIALIZER_H_ + +#include "log_macros.h" +#include "opk_serialization_base.h" +#include "wtpi_crc32_interface.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" +#include "wtpi_crypto_asymmetric_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void OPK_Unpack_PrepareGenerationNumber_Request(ODK_Message* msg); +ODK_Message OPK_Pack_PrepareGenerationNumber_Response(OEMCryptoResult result); +void OPK_Unpack_LoadGenerationNumber_Request(ODK_Message* msg, + uint64_t** value); +ODK_Message OPK_Pack_LoadGenerationNumber_Response(OEMCryptoResult result, + const uint64_t* value); +void OPK_Unpack_SaveGenerationNumber_Request(ODK_Message* msg, uint64_t* value); +ODK_Message OPK_Pack_SaveGenerationNumber_Response(OEMCryptoResult result); +void OPK_Unpack_K1_GetKeySize_Request(ODK_Message* msg, + WTPI_K1_SymmetricKey_Handle* key_handle, + KeySize** size); +ODK_Message OPK_Pack_K1_GetKeySize_Response(OEMCryptoResult result, + const KeySize* size); +void OPK_Unpack_C1_AESCBCDecrypt_Request( + ODK_Message* msg, WTPI_K1_SymmetricKey_Handle* key_handle, + size_t* key_length, uint8_t** in_buffer, size_t* in_buffer_length, + uint8_t* iv, uint8_t** out_buffer); +ODK_Message OPK_Pack_C1_AESCBCDecrypt_Response(OEMCryptoResult result, + size_t in_buffer_length, + const uint8_t* out_buffer); +void OPK_Unpack_C1_AESCBCEncrypt_Request( + ODK_Message* msg, WTPI_K1_SymmetricKey_Handle* key_handle, + uint8_t** in_buffer, size_t* in_buffer_length, uint8_t* iv, + uint8_t** out_buffer); +ODK_Message OPK_Pack_C1_AESCBCEncrypt_Response(OEMCryptoResult result, + size_t in_buffer_length, + const uint8_t* out_buffer); +ODK_Message OPK_Pack_C1_HMAC_SHA256_Verify_Response(OEMCryptoResult result); +void OPK_Unpack_C1_CopyToOutputBuffer_Request(ODK_Message* msg, uint8_t** input, + size_t* input_length, + OPK_OutputBuffer** out, + size_t* output_offset); +ODK_Message OPK_Pack_C1_CopyToOutputBuffer_Response(OEMCryptoResult result); +void OPK_Unpack_C1_RandomBytes_Request(ODK_Message* msg, uint8_t** out, + size_t* out_length); +ODK_Message OPK_Pack_C1_RandomBytes_Response(OEMCryptoResult result, + const uint8_t* out, + size_t out_length); +void OPK_Unpack_K1_InitializeKeyManagement_Request(ODK_Message* msg); +ODK_Message OPK_Pack_K1_InitializeKeyManagement_Response( + OEMCryptoResult result); +void OPK_Unpack_K1_TerminateKeyManagement_Request(ODK_Message* msg); +ODK_Message OPK_Pack_K1_TerminateKeyManagement_Response(OEMCryptoResult result); +void OPK_Unpack_K1_CreateKeyHandle_Request( + ODK_Message* msg, uint8_t** input, size_t* input_length, + SymmetricKeyType* key_type, WTPI_K1_SymmetricKey_Handle** out_key_handle); +ODK_Message OPK_Pack_K1_CreateKeyHandle_Response( + OEMCryptoResult result, const WTPI_K1_SymmetricKey_Handle* out_key_handle); +void OPK_Unpack_K1_DeriveDeviceKeyIntoHandle_Request( + ODK_Message* msg, uint32_t* context, SymmetricKeyType* out_key_type, + WTPI_K1_SymmetricKey_Handle** out_key_handle, KeySize* out_key_size); +ODK_Message OPK_Pack_K1_DeriveDeviceKeyIntoHandle_Response( + OEMCryptoResult result, const WTPI_K1_SymmetricKey_Handle* out_key_handle); +void OPK_Unpack_K1_AESDecryptAndCreateKeyHandle_Request( + ODK_Message* msg, WTPI_K1_SymmetricKey_Handle* decrypt_key_handle, + uint8_t** enc_key, size_t* enc_key_length, uint8_t* iv, + SymmetricKeyType* key_type, WTPI_K1_SymmetricKey_Handle** out_key_handle); +ODK_Message OPK_Pack_K1_AESDecryptAndCreateKeyHandle_Response( + OEMCryptoResult result, const WTPI_K1_SymmetricKey_Handle* out_key_handle); +void OPK_Unpack_K1_AESDecryptAndCreateKeyHandleForMacKeys_Request( + ODK_Message* msg, WTPI_K1_SymmetricKey_Handle* decrypt_key_handle, + uint8_t** enc_mac_keys, size_t* enc_mac_keys_length, uint8_t* iv, + WTPI_K1_SymmetricKey_Handle** out_mac_key_server, + WTPI_K1_SymmetricKey_Handle** out_mac_key_client); +ODK_Message OPK_Pack_K1_AESDecryptAndCreateKeyHandleForMacKeys_Response( + OEMCryptoResult result, + const WTPI_K1_SymmetricKey_Handle* out_mac_key_server, + const WTPI_K1_SymmetricKey_Handle* out_mac_key_client); +void OPK_Unpack_K1_DeriveKeyFromKeyHandle_Request( + ODK_Message* msg, WTPI_K1_SymmetricKey_Handle* key_handle, uint8_t* counter, + uint8_t** context, size_t* context_length, SymmetricKeyType* out_key_type, + KeySize* out_key_size, WTPI_K1_SymmetricKey_Handle** out_key_handle); +ODK_Message OPK_Pack_K1_DeriveKeyFromKeyHandle_Response( + OEMCryptoResult result, const WTPI_K1_SymmetricKey_Handle* out_key_handle); +void OPK_Unpack_K1_WrapKey_Request(ODK_Message* msg, uint32_t* context, + WTPI_K1_SymmetricKey_Handle* key_handle, + SymmetricKeyType* key_type, + uint8_t** wrapped_key, + size_t* wrapped_key_length); +ODK_Message OPK_Pack_K1_WrapKey_Response(OEMCryptoResult result, + const uint8_t* wrapped_key, + size_t wrapped_key_length); +void OPK_Unpack_K1_UnwrapIntoKeyHandle_Request( + ODK_Message* msg, uint32_t* context, uint8_t** wrapped_key, + size_t* wrapped_key_length, SymmetricKeyType* key_type, + WTPI_K1_SymmetricKey_Handle** out_key_handle); +ODK_Message OPK_Pack_K1_UnwrapIntoKeyHandle_Response( + OEMCryptoResult result, const WTPI_K1_SymmetricKey_Handle* out_key_handle); +void OPK_Unpack_K1_FreeKeyHandle_Request( + ODK_Message* msg, WTPI_K1_SymmetricKey_Handle* key_handle); +ODK_Message OPK_Pack_K1_FreeKeyHandle_Response(OEMCryptoResult result); +void OPK_Unpack_CreateAsymmetricKeyHandle_Request( + ODK_Message* msg, uint8_t** input, size_t* input_length, + AsymmetricKeyType* key_type, WTPI_AsymmetricKey_Handle** key_handle); +ODK_Message OPK_Pack_CreateAsymmetricKeyHandle_Response( + OEMCryptoResult result, const WTPI_AsymmetricKey_Handle* key_handle); +void OPK_Unpack_UnwrapIntoAsymmetricKeyHandle_Request( + ODK_Message* msg, uint8_t** input, size_t* input_length, + AsymmetricKeyType* key_type, WTPI_AsymmetricKey_Handle** key_handle, + uint32_t** allowed_schemes); +ODK_Message OPK_Pack_UnwrapIntoAsymmetricKeyHandle_Response( + OEMCryptoResult result, const WTPI_AsymmetricKey_Handle* key_handle, + const uint32_t* allowed_schemes); +void OPK_Unpack_FreeAsymmetricKeyHandle_Request( + ODK_Message* msg, WTPI_AsymmetricKey_Handle* key_handle); +ODK_Message OPK_Pack_FreeAsymmetricKeyHandle_Response(OEMCryptoResult result); +void OPK_Unpack_GetWrappedAsymmetricKeySize_Request( + ODK_Message* msg, size_t* enc_private_key_length, + AsymmetricKeyType* key_type, size_t** buffer_size); +ODK_Message OPK_Pack_GetWrappedAsymmetricKeySize_Response( + OEMCryptoResult result, const size_t* buffer_size); +void OPK_Unpack_WrapAsymmetricKey_Request(ODK_Message* msg, uint8_t** output, + size_t* output_length, + AsymmetricKeyType* key_type, + uint8_t** clear_key, + size_t* clear_key_length); +ODK_Message OPK_Pack_WrapAsymmetricKey_Response(OEMCryptoResult result, + const uint8_t* output, + size_t output_length); +void OPK_Unpack_RSASign_Request(ODK_Message* msg, + WTPI_AsymmetricKey_Handle* key, + uint8_t** message, size_t* message_length, + uint8_t** signature, size_t** signature_length, + RSA_Padding_Scheme* padding_scheme); +ODK_Message OPK_Pack_RSASign_Response(OEMCryptoResult result, + const uint8_t* signature, + const size_t* signature_length); +void OPK_Unpack_RSADecrypt_Request(ODK_Message* msg, + WTPI_AsymmetricKey_Handle* key, + uint8_t** input, size_t* input_length, + uint8_t** out, size_t** out_length); +ODK_Message OPK_Pack_RSADecrypt_Response(OEMCryptoResult result, + const uint8_t* out, + const size_t* out_length); +void OPK_Unpack_ECCSign_Request(ODK_Message* msg, + WTPI_AsymmetricKey_Handle* key, + uint8_t** message, size_t* message_length, + uint8_t** signature, size_t** signature_length); +ODK_Message OPK_Pack_ECCSign_Response(OEMCryptoResult result, + const uint8_t* signature, + const size_t* signature_length); +void OPK_Unpack_ECCDeriveSessionKey_Request(ODK_Message* msg, + WTPI_AsymmetricKey_Handle* key, + uint8_t** key_source, + size_t* key_source_length, + uint8_t** session_key, + size_t** session_key_length); +ODK_Message OPK_Pack_ECCDeriveSessionKey_Response( + OEMCryptoResult result, const uint8_t* session_key, + const size_t* session_key_length); +void OPK_Unpack_GetSignatureSize_Request(ODK_Message* msg, + WTPI_AsymmetricKey_Handle* key, + size_t** signature_length); +ODK_Message OPK_Pack_GetSignatureSize_Response(OEMCryptoResult result, + const size_t* signature_length); +void OPK_Unpack_ED25519Sign_Request(ODK_Message* msg, + WTPI_AsymmetricKey_Handle* key, + uint8_t** message, size_t* message_length, + uint8_t** signature, + size_t** signature_length); +ODK_Message OPK_Pack_ED25519Sign_Response(OEMCryptoResult result, + const uint8_t* signature, + const size_t* signature_length); +void OPK_Unpack_GetBootCertificateChain_Request(ODK_Message* msg, uint8_t** out, + size_t** out_length); +ODK_Message OPK_Pack_GetBootCertificateChain_Response(OEMCryptoResult result, + const uint8_t* out, + const size_t* out_length); +void OPK_Unpack_GenerateRandomCertificateKeyPair_Request( + ODK_Message* msg, AsymmetricKeyType** key_type, + uint8_t** wrapped_private_key, size_t** wrapped_private_key_length, + uint8_t** public_key, size_t** public_key_length); +ODK_Message OPK_Pack_GenerateRandomCertificateKeyPair_Response( + OEMCryptoResult result, const AsymmetricKeyType* key_type, + const uint8_t* wrapped_private_key, + const size_t* wrapped_private_key_length, const uint8_t* public_key, + const size_t* public_key_length); +void OPK_Unpack_DeviceKeyCoseSign1_Request(ODK_Message* msg, uint8_t** message, + size_t* message_length, + uint8_t** signature, + size_t** signature_length); +ODK_Message OPK_Pack_DeviceKeyCoseSign1_Response( + OEMCryptoResult result, const uint8_t* signature, + const size_t* signature_length); +void OPK_Unpack_Crc32Init_Request(ODK_Message* msg, uint32_t** initial_hash); +ODK_Message OPK_Pack_Crc32Init_Response(OEMCryptoResult result, + const uint32_t* initial_hash); +void OPK_Unpack_Crc32Cont_Request(ODK_Message* msg, uint8_t** in, + size_t* in_length, uint32_t* prev_crc, + uint32_t** new_crc); +ODK_Message OPK_Pack_Crc32Cont_Response(OEMCryptoResult result, + const uint32_t* new_crc); +void OPK_Unpack_Crc32Cont_OutputBuffer_Request( + ODK_Message* msg, OPK_OutputBuffer** in, size_t* in_offset, + size_t* in_length, uint32_t* prev_crc, uint32_t** new_crc); +ODK_Message OPK_Pack_Crc32Cont_OutputBuffer_Response(OEMCryptoResult result, + const uint32_t* new_crc); +void OPK_Unpack_GetTrustedTime_Request(ODK_Message* msg, uint64_t** time_in_s); +ODK_Message OPK_Pack_GetTrustedTime_Response(OEMCryptoResult result, + const uint64_t* time_in_s); +void OPK_Unpack_InitializeClock_Request(ODK_Message* msg); +ODK_Message OPK_Pack_InitializeClock_Response(OEMCryptoResult result); +void OPK_Unpack_TerminateClock_Request(ODK_Message* msg); +ODK_Message OPK_Pack_TerminateClock_Response(OEMCryptoResult result); +void OPK_Unpack_GetClockType_Request(ODK_Message* msg); +ODK_Message OPK_Pack_GetClockType_Response( + OEMCrypto_Clock_Security_Level result); +#ifdef __cplusplus +} // extern "C" +#endif +#endif /* OPK_TEE_SERIALIZER_H_ */ diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/tee/tee.gyp b/oemcrypto/opk/oemcrypto_ta/wtpi_test/tee/tee.gyp new file mode 100644 index 0000000..c4ccd4c --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/tee/tee.gyp @@ -0,0 +1,54 @@ +# +# Builds the static library opk_tee.a from the +# generated sources based on oemcrypto_wtpi_macros.h. +# +{ + 'includes': [ + '../settings.gypi' + ], + 'target_defaults': { + 'variables': { + # If |enable_code_generator| is true, the GEN_* files will be + # updated if they are out of date. + 'enable_code_generator%': 'false', + }, + }, + 'targets': [ + { + 'target_name': 'opk_tee_wtpi_test', + 'type': 'static_library', + 'toolsets': ['target'], + 'include_dirs': [ + '<(serialization_dir)/tee/include', + '<(wtpi_dir)', + '<(wtpi_test_dir)/generator', + ], + 'sources': [ + 'GEN_dispatcher.c', + 'GEN_tee_serializer.c', + 'tee_special_cases.c', + '<(common_dir)/GEN_common_serializer.c', + '<(common_dir)/common_special_cases.c', + '<(odk_dir)/src/odk_message.c', + '<(odk_dir)/src/odk_overflow.c', + '<(serialization_common_dir)/bump_allocator.c', + '<(serialization_common_dir)/length_types.c', + '<(serialization_common_dir)/log_macros.c', + '<(serialization_common_dir)/marshaller_base.c', + '<(serialization_common_dir)/opk_init.c', + '<(serialization_common_dir)/opk_serialization_base.c', + '<(serialization_common_dir)/shared_buffer_allocator.c', + ], + 'conditions': [ + ['enable_code_generator=="true"', { + 'dependencies': [ + '<(wtpi_test_dir)/generator/make_source.gyp:make_common_serializer_src', + '<(wtpi_test_dir)/generator/make_source.gyp:make_dispatcher_src', + '<(wtpi_test_dir)/generator/make_source.gyp:make_tee_serializer_src', + ], + } + ] + ] + } + ] +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/tee/tee_special_cases.c b/oemcrypto/opk/oemcrypto_ta/wtpi_test/tee/tee_special_cases.c new file mode 100644 index 0000000..67ba929 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/tee/tee_special_cases.c @@ -0,0 +1,201 @@ +/* + * 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 "tee_special_cases.h" + +#include + +#include "GEN_common_serializer.h" +#include "GEN_tee_serializer.h" +#include "OEMCryptoCENC.h" +#include "bump_allocator.h" +#include "log_macros.h" +#include "marshaller_base.h" +#include "message_debug.h" +#include "oemcrypto_wall_clock.h" +#include "opk_dispatcher.h" +#include "shared_buffer_allocator.h" +#include "tos_shared_memory_interface.h" +#include "tos_transport_interface.h" +#include "wtpi_generation_number_interface.h" + +void OPK_Init_WTPI_K1_SymmetricKey_Handle(WTPI_K1_SymmetricKey_Handle* obj) { + if (obj) { + memset(obj, 0, sizeof(WTPI_K1_SymmetricKey_Handle)); + } +} +void OPK_Init_OPK_OutputBuffer(OPK_OutputBuffer* obj) { + if (obj) { + memset(obj, 0, sizeof(OPK_OutputBuffer)); + } +} + +void OPK_Init_OEMCrypto_CENCEncryptPatternDesc( + OEMCrypto_CENCEncryptPatternDesc* obj) { + if (obj) { + memset(obj, 0, sizeof(OEMCrypto_CENCEncryptPatternDesc)); + } +} + +void OPK_Init_SymmetricKeyType(SymmetricKeyType* obj) { + if (obj) { + memset(obj, 0, sizeof(SymmetricKeyType)); + } +} + +void OPK_Init_AsymmetricKeyType(AsymmetricKeyType* obj) { + if (obj) { + memset(obj, 0, sizeof(AsymmetricKeyType)); + } +} + +void OPK_Init_WTPI_AsymmetricKey_Handle(WTPI_AsymmetricKey_Handle* obj) { + if (obj) { + memset(obj, 0, sizeof(WTPI_AsymmetricKey_Handle)); + } +} + +void OPK_Init_RSA_Padding_Scheme(RSA_Padding_Scheme* obj) { + if (obj) { + memset(obj, 0, sizeof(RSA_Padding_Scheme)); + } +} + +void OPK_Init_KeySize(KeySize* obj) { + if (obj) { + memset(obj, 0, sizeof(KeySize)); + } +} + +void OPK_Init_OEMCrypto_Clock_Security_Level( + OEMCrypto_Clock_Security_Level* obj) { + if (obj) { + memset(obj, 0, sizeof(OEMCrypto_Clock_Security_Level)); + } +} + +void OPK_Unpack_WTPI_K1_SymmetricKey_Handle(ODK_Message* message, + WTPI_K1_SymmetricKey_Handle* value); +void OPK_Unpack_C1_HMAC_SHA256_Verify_Request(ODK_Message* msg, + WTPI_K1_SymmetricKey_Handle* key, + uint8_t** message, + size_t* message_length, + uint8_t** signature) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10009) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, message_length); + OPK_Unpack_WTPI_K1_SymmetricKey_Handle(msg, key); + OPK_UnpackInPlace(msg, message, OPK_FromSizeTPtr(message_length)); + size_t signature_len = 32; + OPK_UnpackInPlace(msg, signature, OPK_FromSizeTPtr(&signature_len)); + OPK_UnpackEOM(msg); +} + +void OPK_Unpack_C1_SHA256_Request(ODK_Message* msg, uint8_t** in_buffer, + size_t* in_buffer_length, + uint8_t** out_buffer) { + uint32_t api_value = UINT32_MAX; + LOGE("TEE special case 10007 OPK_Unpack_C1_SHA256_Request"); + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10007) { + LOGE("OPK_Unpack_C1_SHA256_Request api_value = %u", api_value); + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + } + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, in_buffer_length); + OPK_UnpackInPlace(msg, in_buffer, OPK_FromSizeTPtr(in_buffer_length)); + size_t out_length = 32; + *out_buffer = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_ToLengthType(out_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); +} + +ODK_Message OPK_Pack_C1_SHA256_Response(OEMCryptoResult result, + const uint8_t* out_buffer) { + LOGE("TEE special case 10007 OPK_Pack_C1_SHA256_Response"); + uint32_t api_value = 10007; /* from _tee10007 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + size_t out_length = 32; + OPK_PackMemory(&msg, out_buffer, OPK_ToLengthType(out_length)); + } + OPK_PackEOM(&msg); + return msg; +} + +void OPK_Unpack_C1_HMAC_SHA256_Request(ODK_Message* msg, + WTPI_K1_SymmetricKey_Handle* key, + uint8_t** in_buffer, + size_t* in_buffer_length, + uint8_t** out_buffer) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10008) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, in_buffer_length); + OPK_Unpack_WTPI_K1_SymmetricKey_Handle(msg, key); + OPK_UnpackInPlace(msg, in_buffer, OPK_FromSizeTPtr(in_buffer_length)); + size_t out_length = 32; + *out_buffer = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_ToLengthType(out_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); +} + +ODK_Message OPK_Pack_C1_HMAC_SHA256_Response(OEMCryptoResult result, + const uint8_t* out_buffer) { + uint32_t api_value = 10008; /* from _tee10008 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + size_t out_length = 32; + OPK_PackMemory(&msg, out_buffer, OPK_ToLengthType(out_length)); + } + OPK_PackEOM(&msg); + return msg; +} + +void OPK_Unpack_C1_HMAC_SHA1_Request(ODK_Message* msg, + WTPI_K1_SymmetricKey_Handle* key, + uint8_t** in_buffer, + size_t* in_buffer_length, + uint8_t** out_buffer) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10006) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, in_buffer_length); + OPK_Unpack_WTPI_K1_SymmetricKey_Handle(msg, key); + OPK_UnpackInPlace(msg, in_buffer, OPK_FromSizeTPtr(in_buffer_length)); + size_t out_length = 20; + *out_buffer = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_ToLengthType(out_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); +} + +ODK_Message OPK_Pack_C1_HMAC_SHA1_Response(OEMCryptoResult result, + const uint8_t* out_buffer) { + uint32_t api_value = 10006; /* from _tee10006 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + size_t out_length = 20; + OPK_PackMemory(&msg, out_buffer, OPK_ToLengthType(out_length)); + } + OPK_PackEOM(&msg); + return msg; +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/tee/tee_special_cases.h b/oemcrypto/opk/oemcrypto_ta/wtpi_test/tee/tee_special_cases.h new file mode 100644 index 0000000..040351e --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/tee/tee_special_cases.h @@ -0,0 +1,48 @@ +/* + * 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 WTPI_TEST_TEE_SPECIAL_CASES_H_ +#define WTPI_TEST_TEE_SPECIAL_CASES_H_ + +#include "odk_message.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" +#include "wtpi_crypto_asymmetric_interface.h" + +void OPK_Init_WTPI_K1_SymmetricKey_Handle(WTPI_K1_SymmetricKey_Handle* obj); +void OPK_Init_OPK_OutputBuffer(OPK_OutputBuffer* obj); +void OPK_Init_OEMCrypto_CENCEncryptPatternDesc( + OEMCrypto_CENCEncryptPatternDesc* obj); +void OPK_Init_SymmetricKeyType(SymmetricKeyType* obj); +void OPK_Init_AsymmetricKeyType(AsymmetricKeyType* obj); +void OPK_Init_WTPI_AsymmetricKey_Handle(WTPI_AsymmetricKey_Handle* obj); +void OPK_Init_RSA_Padding_Scheme(RSA_Padding_Scheme* obj); +void OPK_Init_KeySize(KeySize* obj); +void OPK_Init_OEMCrypto_Clock_Security_Level( + OEMCrypto_Clock_Security_Level* obj); +void OPK_Unpack_C1_HMAC_SHA256_Verify_Request(ODK_Message* msg, + WTPI_K1_SymmetricKey_Handle* key, + uint8_t** message, + size_t* message_length, + uint8_t** signature); +void OPK_Unpack_C1_SHA256_Request(ODK_Message* msg, uint8_t** in_buffer, + size_t* in_buffer_length, + uint8_t** out_buffer); +ODK_Message OPK_Pack_C1_SHA256_Response(OEMCryptoResult result, + const uint8_t* out_buffer); +void OPK_Unpack_C1_HMAC_SHA256_Request(ODK_Message* msg, + WTPI_K1_SymmetricKey_Handle* key, + uint8_t** in_buffer, + size_t* in_buffer_length, + uint8_t** out_buffer); +ODK_Message OPK_Pack_C1_HMAC_SHA256_Response(OEMCryptoResult result, + const uint8_t* out_buffer); +ODK_Message OPK_Pack_C1_HMAC_SHA1_Response(OEMCryptoResult result, + const uint8_t* out_buffer); +void OPK_Unpack_C1_HMAC_SHA1_Request(ODK_Message* msg, + WTPI_K1_SymmetricKey_Handle* key, + uint8_t** in_buffer, + size_t* in_buffer_length, + uint8_t** out_buffer); +#endif diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/test_rsa_key.cpp b/oemcrypto/opk/oemcrypto_ta/wtpi_test/test_rsa_key.cpp new file mode 100644 index 0000000..c55f0b8 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/test_rsa_key.cpp @@ -0,0 +1,147 @@ +/* + * Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine + * License Agreement. + */ + +/* + * Generated from oemcrypto/test/test_rsa_key.der + * + * xxd -i test_rsa_key.der > test_rsa_key.cpp + * + * This is a PKCS8 RSA key encoded in DER format. + */ + +#include "stdint.h" +uint8_t test_rsa_key_der[] = { + 0x30, 0x82, 0x04, 0xbc, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x04, 0xa6, 0x30, 0x82, 0x04, 0xa2, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, + 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, + 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd, 0xde, + 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, + 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d, 0xb4, + 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, + 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f, 0xce, + 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, + 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73, 0x9e, + 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, + 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13, 0x8b, + 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, + 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57, 0x54, + 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, + 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06, 0x51, + 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, + 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8, 0x12, + 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, + 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b, 0x3a, + 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, + 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d, 0xdb, + 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, + 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, + 0x82, 0x01, 0x00, 0x5e, 0x79, 0x65, 0x49, 0xa5, 0x76, 0x79, 0xf9, 0x05, + 0x45, 0x0f, 0xf4, 0x03, 0xbd, 0xa4, 0x7d, 0x29, 0xd5, 0xde, 0x33, 0x63, + 0xd8, 0xb8, 0xac, 0x97, 0xeb, 0x3f, 0x5e, 0x55, 0xe8, 0x7d, 0xf3, 0xe7, + 0x3b, 0x5c, 0x2d, 0x54, 0x67, 0x36, 0xd6, 0x1d, 0x46, 0xf5, 0xca, 0x2d, + 0x8b, 0x3a, 0x7e, 0xdc, 0x45, 0x38, 0x79, 0x7e, 0x65, 0x71, 0x5f, 0x1c, + 0x5e, 0x79, 0xb1, 0x40, 0xcd, 0xfe, 0xc5, 0xe1, 0xc1, 0x6b, 0x78, 0x04, + 0x4e, 0x8e, 0x79, 0xf9, 0x0a, 0xfc, 0x79, 0xb1, 0x5e, 0xb3, 0x60, 0xe3, + 0x68, 0x7b, 0xc6, 0xef, 0xcb, 0x71, 0x4c, 0xba, 0xa7, 0x79, 0x5c, 0x7a, + 0x81, 0xd1, 0x71, 0xe7, 0x00, 0x21, 0x13, 0xe2, 0x55, 0x69, 0x0e, 0x75, + 0xbe, 0x09, 0xc3, 0x4f, 0xa9, 0xc9, 0x68, 0x22, 0x0e, 0x97, 0x8d, 0x89, + 0x6e, 0xf1, 0xe8, 0x88, 0x7a, 0xd1, 0xd9, 0x09, 0x5d, 0xd3, 0x28, 0x78, + 0x25, 0x0b, 0x1c, 0x47, 0x73, 0x25, 0xcc, 0x21, 0xb6, 0xda, 0xc6, 0x24, + 0x5a, 0xd0, 0x37, 0x14, 0x46, 0xc7, 0x94, 0x69, 0xe4, 0x43, 0x6f, 0x47, + 0xde, 0x00, 0x33, 0x4d, 0x8f, 0x95, 0x72, 0xfa, 0x68, 0x71, 0x17, 0x66, + 0x12, 0x1a, 0x87, 0x27, 0xf7, 0xef, 0x7e, 0xe0, 0x35, 0x58, 0xf2, 0x4d, + 0x6f, 0x35, 0x01, 0xaa, 0x96, 0xe2, 0x3d, 0x51, 0x13, 0x86, 0x9c, 0x79, + 0xd0, 0xb7, 0xb6, 0x64, 0xe8, 0x86, 0x65, 0x50, 0xbf, 0xcc, 0x27, 0x53, + 0x1f, 0x51, 0xd4, 0xca, 0xbe, 0xf5, 0xdd, 0x77, 0x70, 0x98, 0x0f, 0xee, + 0xa8, 0x96, 0x07, 0x5f, 0x45, 0x6a, 0x7a, 0x0d, 0x03, 0x9c, 0x4f, 0x29, + 0xf6, 0x06, 0xf3, 0x5d, 0x58, 0x6c, 0x47, 0xd0, 0x96, 0xa9, 0x03, 0x17, + 0xbb, 0x4e, 0xc9, 0x21, 0xe0, 0xac, 0xcd, 0x78, 0x78, 0xb2, 0xfe, 0x81, + 0xb2, 0x51, 0x53, 0xa6, 0x1f, 0x98, 0x45, 0x02, 0x81, 0x81, 0x00, 0xcf, + 0x73, 0x8c, 0xbe, 0x6d, 0x45, 0x2d, 0x0c, 0x0b, 0x5d, 0x5c, 0x6c, 0x75, + 0x78, 0xcc, 0x35, 0x48, 0xb6, 0x98, 0xf1, 0xb9, 0x64, 0x60, 0x8c, 0x43, + 0xeb, 0x85, 0xab, 0x04, 0xb6, 0x7d, 0x1b, 0x71, 0x75, 0x06, 0xe2, 0xda, + 0x84, 0x68, 0x2e, 0x7f, 0x4c, 0xe3, 0x73, 0xb4, 0xde, 0x51, 0x4b, 0xb6, + 0x51, 0x86, 0x7b, 0xd0, 0xe6, 0x4d, 0xf3, 0xd1, 0xcf, 0x1a, 0xfe, 0x7f, + 0x3a, 0x83, 0xba, 0xb3, 0xe1, 0xff, 0x54, 0x13, 0x93, 0xd7, 0x9c, 0x27, + 0x80, 0xb7, 0x1e, 0x64, 0x9e, 0xf7, 0x32, 0x2b, 0x46, 0x29, 0xf7, 0xf8, + 0x18, 0x6c, 0xf7, 0x4a, 0xbe, 0x4b, 0xee, 0x96, 0x90, 0x8f, 0xa2, 0x16, + 0x22, 0x6a, 0xcc, 0x48, 0x06, 0x74, 0x63, 0x43, 0x7f, 0x27, 0x22, 0x44, + 0x3c, 0x2d, 0x3b, 0x62, 0xf1, 0x1c, 0xb4, 0x27, 0x33, 0x85, 0x26, 0x60, + 0x48, 0x16, 0xcb, 0xef, 0xf8, 0xcd, 0x37, 0x02, 0x81, 0x81, 0x00, 0xce, + 0x15, 0x43, 0x6e, 0x4b, 0x0f, 0xf9, 0x3f, 0x87, 0xc3, 0x41, 0x45, 0x97, + 0xb1, 0x49, 0xc2, 0x19, 0x23, 0x87, 0xe4, 0x24, 0x1c, 0x64, 0xe5, 0x28, + 0xcb, 0x43, 0x10, 0x14, 0x14, 0x0e, 0x19, 0xcb, 0xbb, 0xdb, 0xfd, 0x11, + 0x9d, 0x17, 0x68, 0x78, 0x6d, 0x61, 0x70, 0x63, 0x3a, 0xa1, 0xb3, 0xf3, + 0xa7, 0x5b, 0x0e, 0xff, 0xb7, 0x61, 0x11, 0x54, 0x91, 0x99, 0xe5, 0x91, + 0x32, 0x2d, 0xeb, 0x3f, 0xd8, 0x3e, 0xf7, 0xd4, 0xcb, 0xd2, 0xa3, 0x41, + 0xc1, 0xee, 0xc6, 0x92, 0x13, 0xeb, 0x7f, 0x42, 0x58, 0xf4, 0xd0, 0xb2, + 0x74, 0x1d, 0x8e, 0x87, 0x46, 0xcd, 0x14, 0xb8, 0x16, 0xad, 0xb5, 0xbd, + 0x0d, 0x6c, 0x95, 0x5a, 0x16, 0xbf, 0xe9, 0x53, 0xda, 0xfb, 0xed, 0x83, + 0x51, 0x67, 0xa9, 0x55, 0xab, 0x54, 0x02, 0x95, 0x20, 0xa6, 0x68, 0x17, + 0x53, 0xa8, 0xea, 0x43, 0xe5, 0xb0, 0xa3, 0x02, 0x81, 0x80, 0x67, 0x9c, + 0x32, 0x83, 0x39, 0x57, 0xff, 0x73, 0xb0, 0x89, 0x64, 0x8b, 0xd6, 0xf0, + 0x0a, 0x2d, 0xe2, 0xaf, 0x30, 0x1c, 0x2a, 0x97, 0xf3, 0x90, 0x9a, 0xab, + 0x9b, 0x0b, 0x1b, 0x43, 0x79, 0xa0, 0xa7, 0x3d, 0xe7, 0xbe, 0x8d, 0x9c, + 0xeb, 0xdb, 0xad, 0x40, 0xdd, 0xa9, 0x00, 0x80, 0xb8, 0xe1, 0xb3, 0xa1, + 0x6c, 0x25, 0x92, 0xe4, 0x33, 0xb2, 0xbe, 0xeb, 0x4d, 0x74, 0x26, 0x5f, + 0x37, 0x43, 0x9c, 0x6c, 0x17, 0x76, 0x0a, 0x81, 0x20, 0x82, 0xa1, 0x48, + 0x2c, 0x2d, 0x45, 0xdc, 0x0f, 0x62, 0x43, 0x32, 0xbb, 0xeb, 0x59, 0x41, + 0xf9, 0xca, 0x58, 0xce, 0x4a, 0x66, 0x53, 0x54, 0xc8, 0x28, 0x10, 0x1e, + 0x08, 0x71, 0x16, 0xd8, 0x02, 0x71, 0x41, 0x58, 0xd4, 0x56, 0xcc, 0xf5, + 0xb1, 0x31, 0xa3, 0xed, 0x00, 0x85, 0x09, 0xbf, 0x35, 0x95, 0x41, 0x29, + 0x40, 0x19, 0x83, 0x35, 0x24, 0x69, 0x02, 0x81, 0x80, 0x55, 0x10, 0x0b, + 0xcc, 0x3b, 0xa9, 0x75, 0x3d, 0x16, 0xe1, 0xae, 0x50, 0x76, 0x63, 0x94, + 0x49, 0x4c, 0xad, 0x10, 0xcb, 0x47, 0x68, 0x7c, 0xf0, 0xe5, 0xdc, 0xb8, + 0x6a, 0xab, 0x8e, 0xf7, 0x9f, 0x08, 0x2c, 0x1b, 0x8a, 0xa2, 0xb9, 0x8f, + 0xce, 0xec, 0x5e, 0x61, 0xa8, 0xcd, 0x1c, 0x87, 0x60, 0x4a, 0xc3, 0x1a, + 0x5f, 0xdf, 0x87, 0x26, 0xc6, 0xcb, 0x7c, 0x69, 0xe4, 0x8b, 0x01, 0x06, + 0x59, 0x22, 0xfa, 0x34, 0x4b, 0x81, 0x87, 0x3c, 0x03, 0x6d, 0x02, 0x0a, + 0x77, 0xe6, 0x15, 0xd8, 0xcf, 0xa7, 0x68, 0x26, 0x6c, 0xfa, 0x2b, 0xd9, + 0x83, 0x5a, 0x2d, 0x0c, 0x3b, 0x70, 0x1c, 0xd4, 0x48, 0xbe, 0xa7, 0x0a, + 0xd9, 0xbe, 0xdc, 0xc3, 0x0c, 0x21, 0x33, 0xb3, 0x66, 0xff, 0x1c, 0x1b, + 0xc8, 0x96, 0x76, 0xe8, 0x6f, 0x44, 0x74, 0xbc, 0x9b, 0x1c, 0x7d, 0xc8, + 0xac, 0x21, 0xa8, 0x6e, 0x37, 0x02, 0x81, 0x80, 0x2c, 0x7c, 0xad, 0x1e, + 0x75, 0xf6, 0x69, 0x1d, 0xe7, 0xa6, 0xca, 0x74, 0x7d, 0x67, 0xc8, 0x65, + 0x28, 0x66, 0xc4, 0x43, 0xa6, 0xbd, 0x40, 0x57, 0xae, 0xb7, 0x65, 0x2c, + 0x52, 0xf9, 0xe4, 0xc7, 0x81, 0x7b, 0x56, 0xa3, 0xd2, 0x0d, 0xe8, 0x33, + 0x70, 0xcf, 0x06, 0x84, 0xb3, 0x4e, 0x44, 0x50, 0x75, 0x61, 0x96, 0x86, + 0x4b, 0xb6, 0x2b, 0xad, 0xf0, 0xad, 0x57, 0xd0, 0x37, 0x0d, 0x1d, 0x35, + 0x50, 0xcb, 0x69, 0x22, 0x39, 0x29, 0xb9, 0x3a, 0xd3, 0x29, 0x23, 0x02, + 0x60, 0xf7, 0xab, 0x30, 0x40, 0xda, 0x8e, 0x4d, 0x45, 0x70, 0x26, 0xf4, + 0xa2, 0x0d, 0xd0, 0x64, 0x5d, 0x47, 0x3c, 0x18, 0xf4, 0xd4, 0x52, 0x95, + 0x00, 0xae, 0x84, 0x6b, 0x47, 0xb2, 0x3c, 0x82, 0xd3, 0x72, 0x53, 0xde, + 0x72, 0x2c, 0xf7, 0xc1, 0x22, 0x36, 0xd9, 0x18, 0x56, 0xfe, 0x39, 0x28, + 0x33, 0xe0, 0xdb, 0x03}; + +/* + * Generated with openssl: + * `echo 'Hello world!' | openssl pkeyutl -inkey test_rsa_key.der -keyform DER + * -encrypt -pkeyopt rsa_padding_mode:oaep -pkeyopt` + */ +uint8_t hello_world_encrypted[] = { + 0x81, 0xf5, 0x07, 0xe5, 0x3f, 0x18, 0xab, 0x88, 0x28, 0xac, 0x47, 0x88, + 0x93, 0xe0, 0x80, 0x90, 0x1e, 0x5b, 0x1a, 0x8f, 0xbc, 0xe1, 0xe7, 0xe0, + 0xd3, 0x42, 0x10, 0x23, 0x5f, 0x0a, 0x2f, 0x81, 0x71, 0x4a, 0xcb, 0x28, + 0x64, 0x08, 0x15, 0x0e, 0xf1, 0x15, 0x52, 0x5e, 0xc3, 0xd2, 0x21, 0x75, + 0x58, 0xfe, 0x8f, 0x76, 0x6e, 0x6a, 0x29, 0x94, 0xd5, 0x40, 0xd4, 0x29, + 0x05, 0x47, 0x37, 0x21, 0x3c, 0x4a, 0xa0, 0xcc, 0x24, 0xed, 0x6b, 0x24, + 0xc3, 0x4b, 0xee, 0xb6, 0x90, 0x3f, 0xe0, 0x74, 0x8b, 0x4d, 0xc1, 0x58, + 0x4f, 0xfa, 0x58, 0xf0, 0x76, 0x29, 0xe4, 0x43, 0x2e, 0x0f, 0xc0, 0x68, + 0x1b, 0x61, 0xba, 0x38, 0xea, 0x43, 0xc4, 0xa3, 0x08, 0xbd, 0x2b, 0xbe, + 0x8e, 0x39, 0xf5, 0x3a, 0x81, 0x67, 0x5f, 0x20, 0xc9, 0x90, 0x66, 0x0e, + 0xaf, 0xbc, 0xd8, 0xb3, 0xe0, 0xd8, 0xdc, 0x02, 0x42, 0x3b, 0x1a, 0xa2, + 0x33, 0x0e, 0x5f, 0x6f, 0x6d, 0x83, 0x46, 0x89, 0x05, 0xa6, 0x2a, 0xe2, + 0xec, 0x00, 0xaf, 0xb0, 0xc9, 0xb7, 0x96, 0x93, 0x89, 0xc1, 0x55, 0x7d, + 0xc2, 0x83, 0xfa, 0x15, 0xf2, 0xee, 0x5b, 0x30, 0xc7, 0xcb, 0x00, 0x72, + 0xbc, 0xe5, 0x30, 0xec, 0x4b, 0x94, 0x07, 0x1a, 0x4e, 0xf1, 0x59, 0xb1, + 0xfb, 0x1c, 0xd7, 0x75, 0x58, 0xde, 0xbb, 0xb3, 0x46, 0x8e, 0x2a, 0xf0, + 0x6e, 0x16, 0x3a, 0xb6, 0x4d, 0x0a, 0x84, 0x4d, 0xf5, 0xb8, 0xb4, 0x29, + 0x57, 0x2d, 0x0c, 0xd6, 0x4d, 0x39, 0x98, 0x67, 0xec, 0x11, 0x51, 0xa9, + 0xdb, 0x91, 0x52, 0x6a, 0x92, 0x0e, 0x31, 0x24, 0xe2, 0x7b, 0xcb, 0xac, + 0xfe, 0xba, 0x18, 0x86, 0x8e, 0x68, 0xf9, 0x83, 0x82, 0x2c, 0x3c, 0x88, + 0x7f, 0x77, 0x27, 0x16, 0x51, 0x0c, 0x8b, 0x8e, 0x26, 0x60, 0xf6, 0x96, + 0xf9, 0xdd, 0x37, 0x40}; diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/wtpi_test.gyp b/oemcrypto/opk/oemcrypto_ta/wtpi_test/wtpi_test.gyp new file mode 100644 index 0000000..0063652 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/wtpi_test.gyp @@ -0,0 +1,65 @@ +{ + 'includes': { + 'settings.gypi', + }, + 'variables': { + 'platform_specific_dir': '<(DEPTH)/linux/src', + 'opk_ree': 'ree/ree.gyp:opk_ree', + 'privacy_crypto_impl': 'boringssl', + 'boringssl_libcrypto_path': '<(third_party_dir)/boringssl/boringssl.gyp:crypto', + 'boringssl_libssl_path': '<(third_party_dir)/boringssl/boringssl.gyp:ssl', + 'use_linux_tos_impl%': 'false', + }, + 'target_defaults': { + 'include_dirs': [ + '.', + '<(wtpi_dir)', + '<(third_party_dir)/googletest/googletest/include', + '<(util_dir)/include', + ], + }, + 'targets': [ + { + 'target_name': 'wtpi_test', + 'type': 'executable', + 'toolsets': ['target'], + 'sources': [ + 'wtpi_test_main.cpp', + ], + 'dependencies': [ + '<(third_party_dir)/googletest.gyp:gtest', + 'wtpi_test_lib', + ], + 'libraries': ['-lrt', '-lpthread', '-ldl'], + 'ldflags': [ + '-Wl,--whole-archive', + 'libwtpi_test_lib.a', + '-Wl,--no-whole-archive', + ], + 'conditions': [ + ['use_linux_tos_impl=="true"', { + 'dependencies': [ + '<(opk_ree)', + ], + }], + ], + }, + { + 'target_name': 'wtpi_test_lib', + 'type': 'static_library', + 'toolsets': ['target'], + 'standalone_static_library': 1, + 'sources': [ + 'clock_interface_test.cpp', + 'crypto_test.cpp', + 'generation_number_interface_test.cpp', + 'ssl_util.cpp', + 'test_rsa_key.cpp', + '<(DEPTH)/linux/src/log.cpp', + ], + 'includes': [ + '../../../../util/libcrypto_dependency.gypi', + ], + }, + ] +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_test/wtpi_test_main.cpp b/oemcrypto/opk/oemcrypto_ta/wtpi_test/wtpi_test_main.cpp new file mode 100644 index 0000000..de45479 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_test/wtpi_test_main.cpp @@ -0,0 +1,10 @@ +// 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 + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_useless/README.md b/oemcrypto/opk/oemcrypto_ta/wtpi_useless/README.md new file mode 100644 index 0000000..44d31bf --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_useless/README.md @@ -0,0 +1,4 @@ +The files in this directory implement some of the WTPI functions required by +the OEMCrypto TA. These implementations will pass unit tests, but they are not +useful for production. To emphasize this, we have named this directory +"useless". Partners should NOT include this code for production builds. diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_useless/wtpi_device_key_access.c b/oemcrypto/opk/oemcrypto_ta/wtpi_useless/wtpi_device_key_access.c new file mode 100644 index 0000000..94e2815 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_useless/wtpi_device_key_access.c @@ -0,0 +1,41 @@ +/* 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_device_key_access_interface.h" + +#include +#include + +#include "wtpi_crypto_and_key_management_interface_layer1.h" + +/* This implementation generates a new device key on reboot. This is good enough + * to pass the unit tests but won't work for an actual device, since the device + * key needs to be constant across reboots. You should replace this file with an + * implementation that accesses a real device-unique secret on your device, + * preferably a key derived from your device's actual device-unique key. + */ + +static bool device_key_initialized; +static uint8_t fake_device_key[KEY_SIZE_128]; + +const uint8_t* WTPI_GetDeviceKey(void) { + if (!device_key_initialized) { + WTPI_C1_RandomBytes(fake_device_key, sizeof(fake_device_key)); + device_key_initialized = true; + } + + /* Note that, in a real implementation, you would need to do something to + * handle device renewal here, such as: + * + * 1) Generating a different device key for renewed and un-renewed devices + * 2) Deriving a new, renewed device key from the old, un-renewed device key + * using the shared renewal key used for keybox renewal + */ + + return fake_device_key; +} + +KeySize WTPI_GetDeviceKeySize(void) { + return (KeySize)(sizeof(fake_device_key)); +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_useless/wtpi_root_of_trust_layer2.c b/oemcrypto/opk/oemcrypto_ta/wtpi_useless/wtpi_root_of_trust_layer2.c new file mode 100644 index 0000000..fb211a3 --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_useless/wtpi_root_of_trust_layer2.c @@ -0,0 +1,46 @@ +/* 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_root_of_trust_interface_layer2.h" + +#include +#include + +#include "wtpi_config_interface.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" +#include "wtpi_logging_interface.h" + +// In practice, ROT_SIZE is the size of a keybox. +#define MAX_ROT_SIZE 1000 +static size_t gBufferSize = 0; +static uint8_t gBuffer[MAX_ROT_SIZE]; + +OEMCryptoResult WTPI_UnwrapRootOfTrust(const uint8_t* input, + size_t input_length, uint8_t* output, + size_t* output_length) { + if (input == NULL || output == NULL || output_length == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (input_length > *output_length) { + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *output_length = input_length; + memcpy(output, input, *output_length); // TODO: decrypt using system key. + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_SaveRootOfTrust(const uint8_t* input, size_t length) { + if (length > MAX_ROT_SIZE) return OEMCrypto_ERROR_INVALID_CONTEXT; + gBufferSize = length; + memcpy(&gBuffer, input, length); + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_LoadRootOfTrust(uint8_t* output, size_t* length) { + if (length == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT; + if (*length < gBufferSize) return OEMCrypto_ERROR_SHORT_BUFFER; + *length = gBufferSize; + memcpy(output, gBuffer, gBufferSize); + return OEMCrypto_SUCCESS; +} diff --git a/oemcrypto/opk/oemcrypto_ta/wtpi_useless/wtpi_secure_buffer_access.c b/oemcrypto/opk/oemcrypto_ta/wtpi_useless/wtpi_secure_buffer_access.c new file mode 100644 index 0000000..a9cc59b --- /dev/null +++ b/oemcrypto/opk/oemcrypto_ta/wtpi_useless/wtpi_secure_buffer_access.c @@ -0,0 +1,14 @@ +/* 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_secure_buffer_access_interface.h" + +/* Secure buffers are not supported by the test build. */ +OEMCryptoResult WTPI_GetSecureBufferAddress(void* secure, size_t offset, + uint8_t** out_addr) { + if (secure == NULL || out_addr == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} diff --git a/oemcrypto/opk/ports/optee/README.md b/oemcrypto/opk/ports/optee/README.md new file mode 100644 index 0000000..293a067 --- /dev/null +++ b/oemcrypto/opk/ports/optee/README.md @@ -0,0 +1,180 @@ +This is a port of the OEMCrypto Trusted App for OP-TEE using the OPK. + +### Quick build steps + +1. Make sure you have an OPTEE repo setup somewhere. Set that path to the + environment variable OPTEE_DIR. If the GCC toolchain is separate from the one + included in $OPTEE_DIR/toolchains, set that path to OPTEE_TOOLCHAIN_DIR. +2. Set up the third_party directory. The makefiles for unit tests reference + googletest and boringssl, and expect them to be under a top level directory + called "third_party": + ``` + # From the top level of the repo + $ mkdir third_party + $ git clone https://github.com/google/googletest.git googletest + $ mkdir boringssl && cd boringssl + $ mkdir kit && cd kit + $ git clone https://boringssl.googlesource.com/boringssl src && cd src + + # Commits after this change some filenames, which is incompatible with + # current OPK makefiles + $ git checkout 1e15682f1a4bb64c48b84884976a2b5c4201e878 + $ cd ../ + $ python ./src/util/generate_build_files.py gyp + ``` +2. From the top level of this repo (CDM), run `make -j32 -C + ./oemcrypto/opk/build -f Makefile.optee` + +The resulting artifacts will run on QEMU, but due to performance constraints not +all tests will pass there. For best results the executables should be built for +NXP and run on the iMX8 dev kit (see Makefile.optee for the variables that +specify the NXP target). Currently the following tests are expected to fail, +with fixes expected in future updates: + +WTPI unit tests that do not pass +- CryptoTest.ED25519SignSuccess +- CryptoTest.ECCKeyExchange +- CryptoTest.GenerateRandomCertificateKeyPairSuccess +- CryptoTest.WTPI_DeviceKeyCoseSign1Success + +OEMCrypto unit tests that do not pass +- OEMCryptoLoadsCertificate.TestMaxDRMKeys + +### Background + +In general, an end-to-end system consists of the following components on +the REE side: + +- A host application that calls OEMCrypto functions and expects them to execute + in the TEE. Not provided by the OPK. +- A serialization layer that intercepts those OEMCrypto function calls, converts + them to a byte stream, and sends them across the transport interface to the + TEE. Provided by the OPK. +- TOS function implementations. Sets up shared memory, message buffers, and + secure buffers for REE/TEE shared usage. Not provided by the OPK. + +And the following components on the TEE side: + +- A trusted application entry point for messages from the REE side. Not provided + by the OPK. +- TOS function implementations to match the REE-side shared memory expectations + for message sharing. Not provided by the OPK. +- A deserialization layer, which takes the input message, decodes it into the + desired OEMCrypto function name and arguments, then executes it. Provided by + the OPK. +- OEMCrypto function implementations. Provided by the OPK. +- WTPI function implementations. The OEMCrypto functions will call WTPI for + platform specific operations such as persistent storage, time, crypto, etc. + Not provided by the OPK. + +### OEMCrypto TA + +The Makefile in `ta/oemcrypto_ta` will build a Trusted App that implements +OEMCrypto functions and can be installed on OP-TEE. Using the above list as +a reference, the TEE-specific components include + +- A trusted application entry point for messages: TA_InvokeCommandEntryPoint() +- TOS function implementations: `host/common/tos` +- Deserialization layer: included as `libopk_tee` from the serialization + directory +- OEMCrypto function implementations: included as `liboemcrypto_ta` from the + oemcrypto_ta directory +- WTPI function implementations: `ta/common/wtpi_impl`. These are specific to + GlobalPlatform. + +A simple host application is in `host/oemcrypto_helloworld`. This targets the +`oemcrypto_ta` UUID, calls OEMCrypto_Initialize(), and prints "hello world". It +serves as a useful sanity check to make sure all of the components are working +correctly. It uses the same TOS and serialization code as the `oemcrypto_ta` +does, but REE-specific versions. + +`oemcrypto_unittests` will be compiled according to the makefile in +`host/oemcrypto_unittests` to run as a host application that also targets the +`oemcrypto_ta` UUID. This runs the Widevine OEMCrypto unit test suite against +the TA. Like the `oemcrypto_helloworld` app, the unit tests link against the +REE-specific TOS and serialization code to interact with `oemcrypto_ta`. + +Both of the host apps (`oemcrypto_helloworld` and `oemcrypto_unittests`) are +linked against the shared library `liboemcrypto.so`, which needs to be in the +LD_LIBRARY_PATH at runtime. + +### WTPI Test TA + +Since a large portion of the porting effort is concentrated in the WTPI function +implementations, a TA and host app are provided to unit test those functions +individually in the TEE. The makefiles are in `ta/wtpi_test_ta` and +`host/wtpi_unittests` respectively. + +The WTPI test TA and host app follow the same component structure as +described above for the OEMCrypto TA and host apps. The only difference is in the +serialization layer. The serialization layer for the WTPI tests only targets +WTPI functions, while the serialization layer for the OEMCrypto app targets +OEMCrypto functions. + +Unlike the OEMCrypto TA, the WTPI test host app does not link against +liboemcrypto.so + +### Build and run on QEMU + +To set up the OPTEE build environment for QEMU, see documentation at +https://optee.readthedocs.io/en/latest/building/devices/qemu.html + +Specifically the steps are: + +``` +export OPTEE_DIR=~/optee-qemu # change to your OPTEE root directory +mkdir $OPTEE_DIR + +# get the QEMU v7 devkit +cd $OPTEE_DIR +repo init -u https://github.com/OP-TEE/manifest.git +repo sync -c + +# build toolchains +cd $OPTEE_DIR/build +make toolchains + +export QEMU_VIRTFS_AUTOMOUNT=y +export PLATFORM=vexpress-qemu_virt + +cd $OPTEE_DIR/qemu +./configure --enable-virtfs +# you may need to sudo apt-get install libcap-ng-dev + +# NOTE: you need to run OPTEE on a local display, it won't work +# over ssh + +# To run OP_TEE: +cd $OPTEE_DIR/build +make -j30 QEMU_VIRTFS_ENABLE=y run # Use this the first time +make QEMU_VIRTFS_ENABLE=y run-only # Use this subsequently + +# three terminals open up: console, secure and non-secure +# in console, type ‘c’ +# in non-secure login as ‘root’ or ‘test’ + +# /mnt/host in QEMU REE should now link to $OPTEE_DIR outside of QEMU + +# Back outside of QEMU: +# Build the OP-TEE port. This includes the OEMCrypto TA and host apps, as well +# as the WTPI test TA and host app. +cd $CDM_DIR +make -j32 -C ./oemcrypto/opk/build -f Makefile.optee + +# Push the build artifacts to to $OPTEE_DIR, which is the virtfs directory for QEMU +cd oemcrypto/opk/ports/optee +./push.sh + +# in the QEMU REE terminal: +cd /mnt/host/oemcrypto/test +. ./install_ta.sh + +# run hello world +./oemcrypto_helloworld + +# run WTPI unit tests +./wtpi_unittests + +# run OEMCrypto unit tests +./oemcrypto_unittest +``` diff --git a/oemcrypto/opk/ports/optee/build/helloworld.gyp b/oemcrypto/opk/ports/optee/build/helloworld.gyp new file mode 100644 index 0000000..0fb33e0 --- /dev/null +++ b/oemcrypto/opk/ports/optee/build/helloworld.gyp @@ -0,0 +1,27 @@ +# This target is in its own file since it depends on the liboemcrypto host.gyp file. +# Since other targets in this directory's host.gyp file depend on the liboemcrypto host.gyp, +# we need to move this out to avoid circular dependency errors +{ + 'includes' : [ + '../../../serialization/settings.gypi', + ], + 'variables': { + 'optee_port_dir': '<(oemcrypto_dir)/opk/ports/optee', + }, + 'targets' : [ + { + 'target_name' : 'oemcrypto_helloworld', + 'toolsets' : [ 'target' ], + 'type' : 'executable', + 'sources' : [ + '<(optee_port_dir)/host/oemcrypto_helloworld/main.c', + ], + 'include_dirs' : [ + '<(optee_port_dir)/ta/wtpi_test_ta/include', + ], + 'dependencies': [ + '<(oemcrypto_dir)/opk/build/host.gyp:liboemcrypto', + ], + }, + ], +} diff --git a/oemcrypto/opk/ports/optee/build/host.gyp b/oemcrypto/opk/ports/optee/build/host.gyp new file mode 100644 index 0000000..690db7e --- /dev/null +++ b/oemcrypto/opk/ports/optee/build/host.gyp @@ -0,0 +1,48 @@ +{ + 'includes' : [ + '../../../serialization/settings.gypi', + ], + 'variables': { + 'optee_port_dir': '<(oemcrypto_dir)/opk/ports/optee', + 'tos_src_dir': '<(optee_port_dir)/host/common/tos', + 'optee_repo_dir': '$(OPTEE_DIR)', + }, + 'targets' : [ + { + # Transport layer implementation, REE side, for OEMCrypto TA + 'target_name' : 'ree_tos', + 'toolsets' : [ 'target' ], + 'type' : 'static_library', + 'standalone_static_library' : 1, + 'sources' : [ + '<(tos_src_dir)/load_library.c', + '<(tos_src_dir)/optee_ree_tos.c', + '<(tos_src_dir)/optee_secure_buffers.c', + '<(tos_src_dir)/optee_tos_log.c', + ], + 'include_dirs' : [ + '<(optee_port_dir)/ta/oemcrypto_ta/include', + '<(optee_repo_dir)/optee_client/public', + ] + }, + { + # Transport layer implementation, REE side. For WTPI unit tests. + # This differs from the `ree_tos` target in two ways. + # 1. No `load_library.c`, since OPK_Init is called from the unit tests + # 2. Including headers from wtpi_test_ta directory, so we target a different TA UUID + 'target_name' : 'ree_tos_wtpi', + 'toolsets' : [ 'target' ], + 'type' : 'static_library', + 'standalone_static_library' : 1, + 'sources' : [ + '<(tos_src_dir)/optee_ree_tos.c', + '<(tos_src_dir)/optee_secure_buffers.c', + '<(tos_src_dir)/optee_tos_log.c', + ], + 'include_dirs' : [ + '<(optee_port_dir)/ta/wtpi_test_ta/include', + '<(optee_repo_dir)/optee_client/public', + ] + }, + ], +} diff --git a/oemcrypto/opk/ports/optee/build/ta.gyp b/oemcrypto/opk/ports/optee/build/ta.gyp new file mode 100644 index 0000000..bfbf6a8 --- /dev/null +++ b/oemcrypto/opk/ports/optee/build/ta.gyp @@ -0,0 +1,70 @@ +{ + 'includes' : [ + '../../../serialization/settings.gypi', + ], + 'variables': { + 'optee_port_dir': '<(DEPTH)/oemcrypto/opk/ports/optee', + 'common': '<(optee_port_dir)/ta/common', + 'wtpi_impl': '<(optee_port_dir)/ta/common/wtpi_impl', + 'wtpi_ref': '<(oemcrypto_ta_dir)/wtpi_reference', + 'wtpi_stub': '<(DEPTH)/oemcrypto/opk/oemcrypto_ta/wtpi_useless', + 'optee_dir': '$(OPTEE_DIR)', + }, + 'targets' : [ + { + # WTPI implementation, packed into a shared library + 'target_name' : 'wtpi_impl', + 'toolsets' : [ 'target' ], + 'type' : 'static_library', + 'standalone_static_library' : 1, + 'sources' : [ + '<(common)/ta_log.c', + '<(common)/der_parse.c', + '<(optee_port_dir)/host/common/tos/optee_secure_buffers.c', + '<(wtpi_impl)/wtpi_abort.c', + '<(wtpi_impl)/wtpi_clock_layer2.c', + '<(wtpi_impl)/wtpi_config.c', + '<(wtpi_impl)/wtpi_crypto_and_key_management_layer1.c', + '<(wtpi_impl)/wtpi_crypto_asymmetric.c', + '<(wtpi_impl)/wtpi_decrypt_sample.c', + '<(wtpi_impl)/wtpi_initialize_terminate_interface.c', + '<(wtpi_impl)/wtpi_logging.c', + '<(wtpi_impl)/wtpi_persistent_storage_layer2.c', + '<(wtpi_stub)/wtpi_root_of_trust_layer2.c', + '<(wtpi_stub)/wtpi_secure_buffer_access.c', + '<(wtpi_ref)/renewal_util.c', + '<(wtpi_ref)/wtpi_clock_and_gn_layer1.c', + '<(wtpi_ref)/wtpi_crc32.c', + '<(wtpi_ref)/wtpi_crypto_wrap_asymmetric.c', + '<(wtpi_ref)/wtpi_device_key.c', + '<(wtpi_ref)/wtpi_idle.c', + '<(wtpi_ref)/wtpi_device_renewal_layer1.c', + '<(wtpi_ref)/wtpi_device_renewal_layer2.c', + '<(wtpi_ref)/wtpi_root_of_trust_layer1.c', + ], + 'defines': [ + # Needed for to work. + '_DEFAULT_SOURCE', + '__USE_MISC', + ], + 'include_dirs': [ + '<(wtpi_impl)', + '<(wtpi_ref)', + '<(DEPTH)/oemcrypto/include', + '<(DEPTH)/oemcrypto/odk/include', + '<(DEPTH)/oemcrypto/odk/src', + '<(DEPTH)/oemcrypto/opk/serialization/common/include', + '<(DEPTH)/oemcrypto/opk/serialization/os_interfaces', + '<(oemcrypto_ta_dir)', + '<(oemcrypto_ta_dir)/wtpi', + '<(common)', + '<(optee_dir)/optee_os/out/arm/export-ta_arm32/include', + '<(optee_dir)/optee_os/out/arm/export-ta_arm32/include/mbedtls', + ], + 'ldflags': [ + '-L<(optee_dir)/optee_os/out/arm/export-ta_arm32/lib', + '-lmbedtls', + ], + }, + ], +} diff --git a/oemcrypto/opk/ports/optee/host/common/tos/load_library.c b/oemcrypto/opk/ports/optee/host/common/tos/load_library.c new file mode 100644 index 0000000..1bead9a --- /dev/null +++ b/oemcrypto/opk/ports/optee/host/common/tos/load_library.c @@ -0,0 +1,16 @@ +/* + * 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 "opk_init.h" + +// When liboemcrypto is loaded, initialize the OPK +static void __attribute__((constructor)) Initialize_On_Load(void) { + OPK_Initialize(); +} + +static void __attribute__((destructor)) Terminate_On_Close(void) { + OPK_Terminate(); +} diff --git a/oemcrypto/opk/ports/optee/host/common/tos/optee_ree_tos.c b/oemcrypto/opk/ports/optee/host/common/tos/optee_ree_tos.c new file mode 100644 index 0000000..b71801e --- /dev/null +++ b/oemcrypto/opk/ports/optee/host/common/tos/optee_ree_tos.c @@ -0,0 +1,206 @@ +/* + * Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine + * License Agreement. + */ + +/* + * This is an implementation of the tos_transport_interface and + * tos_shared_memory interfaces for the OP-TEE port of the OPK. + */ +#include + +#include "log_macros.h" +#include "opk_init.h" +#include "tee_client_api.h" +#include "tos_shared_memory_interface.h" +#include "tos_transport_interface.h" +#include "user_ta_header_defines.h" + +static TEEC_Context teec_context_; +static TEEC_Session teec_session_; + +/* + * There are two shared memory blocks, the first is used for the + * request/response messages, the second is the block that the OPK + * uses for serialization and shared input/output buffers. + */ +static TEEC_SharedMemory transport_shared_memory_; +static TEEC_SharedMemory opk_shared_memory_; + +static bool initialized_ = false; + +static TEEC_Result AllocateTransportBuffer(void) { + transport_shared_memory_.buffer = NULL; + transport_shared_memory_.size = OPK_TRANSPORT_MESSAGE_SIZE; + transport_shared_memory_.flags = TEEC_MEM_INPUT | TEEC_MEM_OUTPUT; + return TEEC_AllocateSharedMemory(&teec_context_, &transport_shared_memory_); +} + +static void FreeTransportBuffer(void) { + TEEC_ReleaseSharedMemory(&transport_shared_memory_); +} + +static TEEC_Result AllocateSharedMemory(size_t size) { + opk_shared_memory_.buffer = NULL; + opk_shared_memory_.size = size; + opk_shared_memory_.flags = TEEC_MEM_INPUT | TEEC_MEM_OUTPUT; + return TEEC_AllocateSharedMemory(&teec_context_, &opk_shared_memory_); +} + +static void FreeSharedMemory(void) { + TEEC_ReleaseSharedMemory(&opk_shared_memory_); +} + +/* + * Called when the transport interface is initialized, this function + * creates the TEEC context, allocates the shared memory blocks and + * passes reference to the shared memory to the TEE in the + * TEEC_OpenSession call. + */ +static TEEC_Result OPTEE_Open(void) { + if (initialized_) { + return TEEC_ERROR_BAD_STATE; + } + TEEC_Result result = TEEC_InitializeContext(NULL, &teec_context_); + if (result != TEEC_SUCCESS) { + LOGE("TEEC_InitializeContext failed: 0x%08x", result); + return result; + } + result = AllocateTransportBuffer(); + if (result != TEEC_SUCCESS) { + LOGE("TEEC_OpenSession failed: 0x%08x", result); + TEEC_FinalizeContext(&teec_context_); + return result; + } + TEEC_UUID uuid = TA_UUID; + result = TEEC_OpenSession(&teec_context_, &teec_session_, &uuid, + TEEC_LOGIN_PUBLIC, NULL, NULL, NULL); + if (result != TEEC_SUCCESS) { + FreeTransportBuffer(); + TEEC_FinalizeContext(&teec_context_); + return result; + } + initialized_ = true; + return TEEC_SUCCESS; +} + +static void OPTEE_Close(void) { + if (initialized_) { + FreeTransportBuffer(); + TEEC_CloseSession(&teec_session_); + TEEC_FinalizeContext(&teec_context_); + initialized_ = false; + } +} + +/******************************************************************* + * See "tos_transport_interface.h" for documentation of each of the + * TOS_ interface functions below. + *******************************************************************/ + +bool TOS_Transport_Initialize(void) { + TEEC_Result result = OPTEE_Open(); + if (result != TEEC_SUCCESS) { + LOGE("OPTEE_Open: TEEC error 0x%08x", result); + return false; + } + return true; +} + +void TOS_Transport_Terminate(void) { OPTEE_Close(); } + +ODK_Message TOS_Transport_GetRequest(void) { + if (!transport_shared_memory_.buffer) { + LOGE("TOS_Transport_GetRequest: no shared memory!"); + return ODK_Message_Create(NULL, 0); + } else { + return ODK_Message_Create(transport_shared_memory_.buffer, + transport_shared_memory_.size); + } +} + +ODK_Message TOS_Transport_GetResponse(void) { + /* Use the same shared buffer for request & response */ + return TOS_Transport_GetRequest(); +} + +void TOS_Transport_ReleaseMessage(ODK_Message* message) { + // resources are static, nothing to do here +} + +OPK_TransportStatus TOS_Transport_SendMessage(ODK_Message* request, + ODK_Message* response) { + *response = ODK_Message_Create(NULL, 0); + + TEEC_Result res = TEEC_ERROR_GENERIC; + TEEC_Operation op = {}; + + op.params[0].memref.parent = &opk_shared_memory_; + op.params[0].memref.size = opk_shared_memory_.size; + op.params[1].memref.parent = &transport_shared_memory_; + op.params[1].memref.offset = 0; + op.params[1].memref.size = ODK_Message_GetSize(request); + + op.paramTypes = + TEEC_PARAM_TYPES(TEEC_MEMREF_WHOLE, /* opk_shared_memory */ + TEEC_MEMREF_PARTIAL_INOUT, /* transport_shared_memory */ + TEEC_NONE, TEEC_NONE); + res = TEEC_InvokeCommand(&teec_session_, TA_SEND_REQUEST_CMD, &op, NULL); + if (res != TEEC_SUCCESS) { + LOGE("SendMessage failed: 0x%08x", res); + return OPK_TRANSPORT_STATUS_IO_ERROR; + } + + *response = ODK_Message_Create(transport_shared_memory_.buffer, + transport_shared_memory_.size); + ODK_Message_SetSize(response, op.params[1].memref.size); + return OPK_TRANSPORT_STATUS_OK; +} + +/******************************************************************* + * See "tos_shared_memory_interface.h" for documentation + * of each of the TOS_SharedMemory interface functions below. + *******************************************************************/ + +bool TOS_SharedMemory_Allocate(size_t size) { + bool result = AllocateSharedMemory(size); + if (result != TEEC_SUCCESS) { + LOGE("TOS_SharedMemory_Allocate failed: 0x%08x", result); + } + return result == TEEC_SUCCESS; +} + +void TOS_SharedMemory_Release(void) { FreeSharedMemory(); } + +uint8_t* TOS_SharedMemory_GetAddress(void) { + return opk_shared_memory_.buffer ? opk_shared_memory_.buffer : NULL; +} + +size_t TOS_SharedMemory_GetSize(void) { return opk_shared_memory_.size; } + +void TOS_SharedMemory_Finalize(OPK_SharedMemory_CopyVector* in_vec, + size_t in_count, + OPK_SharedMemory_CopyVector* out_vec, + size_t out_count) { + uint8_t* address = opk_shared_memory_.buffer; + if (address) { + for (size_t i = 0; i < in_count; i++) { + if (in_vec[i].address) { + memcpy(address + in_vec[i].offset, in_vec[i].address, in_vec[i].length); + } + } + for (size_t i = 0; i < out_count; i++) { + if (out_vec[i].address) { + memcpy(out_vec[i].address, address + out_vec[i].offset, + out_vec[i].length); + } + } + } +} + +size_t TOS_SharedMemory_AvailableSize(void) { + const size_t KiB = 1024; + const size_t MiB = 1024 * KiB; + return 4 * MiB + 32 * KiB; +} diff --git a/oemcrypto/opk/ports/optee/host/common/tos/optee_secure_buffers.c b/oemcrypto/opk/ports/optee/host/common/tos/optee_secure_buffers.c new file mode 100644 index 0000000..dbe8064 --- /dev/null +++ b/oemcrypto/opk/ports/optee/host/common/tos/optee_secure_buffers.c @@ -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. + */ +#include "oemcrypto_compiler_attributes.h" +#include "opk_serialization_base.h" +#include "tos_secure_buffer_interface.h" + +/* + * The OP-TEE implementation doesn't do anything special with secure + * buffers yet just pass the fields through unmodified + */ + +void TOS_SecureBuffer_Pack(ODK_Message* message, + const OEMCrypto_DestBufferDesc* obj) { + /* secure memory - pack handle, length, offset */ + OPK_Pack_uint64_t(message, + (const uint64_t*)&obj->buffer.secure.secure_buffer); + OPK_Pack_size_t(message, &obj->buffer.secure.secure_buffer_length); + OPK_Pack_size_t(message, &obj->buffer.secure.offset); +} + +void TOS_SecureBuffer_Unpack(ODK_Message* message, + OEMCrypto_DestBufferDesc* obj) { + /* secure memory - unpack handle, length, offset */ + OPK_Unpack_uint64_t(message, (uint64_t*)&obj->buffer.secure.secure_buffer); + OPK_Unpack_size_t(message, &obj->buffer.secure.secure_buffer_length); + OPK_Unpack_size_t(message, &obj->buffer.secure.offset); +} + +bool TOS_SecureBuffer_CheckSize(UNUSED void* handle, UNUSED size_t size) { + /* + * secure memory not hooked up yet, there's nothing we can do here + * TODO: b/197489423 + */ + return true; +} diff --git a/oemcrypto/opk/ports/optee/host/common/tos/optee_tos_log.c b/oemcrypto/opk/ports/optee/host/common/tos/optee_tos_log.c new file mode 100644 index 0000000..ddf8844 --- /dev/null +++ b/oemcrypto/opk/ports/optee/host/common/tos/optee_tos_log.c @@ -0,0 +1,67 @@ +// +// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine +// License Agreement. +// + +// This is an implementation of the REE-side tos_logging_interface for +// the OP-TEE port of the OPK. + +#include +#include +#include +#include + +#include "tos_logging_interface.h" + +/******************************************************************* + * See "tos_logging_interface.h" for documentation of TOS_Log. + *******************************************************************/ + +static const char* LevelString(OPK_LogLevel level) { + switch (level) { + case OPK_LogLevel_Fatal: + return "FATAL"; + case OPK_LogLevel_Error: + return "ERROR"; + case OPK_LogLevel_Warning: + return "WARNING"; + case OPK_LogLevel_Info: + return "INFO"; + case OPK_LogLevel_Debug: + return "DEBUG"; + case OPK_LogLevel_Verbose: + return "VERBOSE"; + } + return ""; +} + +__attribute__((format(printf, 5, 6))) void TOS_Log(const char* file, + const char* func, int line, + OPK_LogLevel level, + const char* fmt, ...) { + /* + * By default, messages with levels at or higher than min_log_level + * will be logged. You can change the defaulit value of + * min_log_level here at compile time, or set the environment + * variable TOS_LOG_LEVEL to the integer value of the minimum + * log level desired at runtime. + */ + static OPK_LogLevel min_log_level = OPK_LogLevel_Warning; + static bool set_log_level = true; + if (set_log_level) { + const char* env_level = getenv("TOS_LOG_LEVEL"); + if (env_level) { + min_log_level = atoi(env_level); + } + set_log_level = false; + } + if (level >= min_log_level) { + va_list args; + va_start(args, fmt); + fprintf(stderr, "[%s:%s(%d):%s] ", LevelString(level), file, line, func); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); + } +} diff --git a/oemcrypto/opk/ports/optee/host/oemcrypto_helloworld/README.md b/oemcrypto/opk/ports/optee/host/oemcrypto_helloworld/README.md new file mode 100644 index 0000000..d38d63b --- /dev/null +++ b/oemcrypto/opk/ports/optee/host/oemcrypto_helloworld/README.md @@ -0,0 +1,19 @@ +This folder has source and makefiles for a simple hello world REE-side +executable. + +Most of the magic comes from linking code already provided by the OPK such as +function serialization. + +The TOS is not part of the OPK. This layer is called by the serialization code +and allows us to specify a mechanism for getting REE messages across to the TEE. +The implementation here is in `tos`. This is where the UUID for the targeted TA +is specified. In this case, we are targeting the `oemcrypto_ta` app. + +Easiest way to build is to use the `Makefile.optee` rules, which builds the +appropriate prerequisite libraries. The output is a binary named +`oemcrypto_helloworld`. + +For QEMU normal world running, make sure `liboemcrypto.so` is in the +`LD_LIBRARY_PATH`, then just run `./oemcrypto_helloworld`. You should see "hello +world" pop on the screen, and in the secure world log you should see evidence of +OEMCrypto being initialized. diff --git a/oemcrypto/opk/ports/optee/host/oemcrypto_helloworld/main.c b/oemcrypto/opk/ports/optee/host/oemcrypto_helloworld/main.c new file mode 100644 index 0000000..0581b00 --- /dev/null +++ b/oemcrypto/opk/ports/optee/host/oemcrypto_helloworld/main.c @@ -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 + * License Agreement. + */ + +#include +#include "OEMCryptoCENC.h" + +int main(void) { + printf("Hello world\n"); + + OEMCryptoResult res; + res = OEMCrypto_Initialize(); + printf("OEMCrypto_Initialize() returned %d", res); + + return 0; +} diff --git a/oemcrypto/opk/ports/optee/install_ta.sh b/oemcrypto/opk/ports/optee/install_ta.sh new file mode 100755 index 0000000..44fa009 --- /dev/null +++ b/oemcrypto/opk/ports/optee/install_ta.sh @@ -0,0 +1,46 @@ +# Run this script in the non-secure QEMU login shell to copy the Widevine +# OEMCrypto TA and WTPI Test TA to the directory where the TAs are located and +# set environment variables to access the liboecmrypto.so + +OEMCRYPTO_TA=a92d116c-ce27-4917-b30c-4a416e2d9351.ta +if test -f $OEMCRYPTO_TA; then + echo "Installing OEMCrypto TA" + cp $OEMCRYPTO_TA /lib/optee_armtz && rm $OEMCRYPTO_TA + xtest --install-ta /lib/optee_armtz/$OEMCRYPTO_TA +else + if test -f /lib/optee_armtz/$OEMCRYPTO_TA; then + echo "OEMCrypto TA is already installed" + else + echo "OEMCrypto TA not found, run push.sh from host" + fi +fi + +WTPI_TEST_TA=b0f42504-01ec-11ec-9a03-0242ac130003.ta +if test -f $WTPI_TEST_TA; then + echo "Installing WTPI Test TA" + cp $WTPI_TEST_TA /lib/optee_armtz && rm $WTPI_TEST_TA + xtest --install-ta /lib/optee_armtz/$WTPI_TEST_TA +else + if test -f /lib/optee_armtz/$WTPI_TEST_TA; then + echo "WTPI Test TA is already installed" + else + echo "WTPI TEST TA not found, run push.sh from host" + fi +fi + +# restart tee-supplicant +PID=$(ps | grep tee-supplicant | grep -v grep | sed 's/ tee.*//') +if test -z $PID; then + echo "tee-supplicant is not running" +else + echo "stopping tee-supplicant" + kill $PID +fi +echo "starting tee-supplicant" +su tee -c "tee-supplicant -d /dev/teepriv0" & + +# run as "source" to get this to actually propagate to the environment +# eg. `. ./install_ta.sh` instead of just `./install_ta.sh` +export LD_LIBRARY_PATH=/mnt/host/oemcrypto/test +export TOS_LOG_LEVEL=4 + diff --git a/oemcrypto/opk/ports/optee/push.sh b/oemcrypto/opk/ports/optee/push.sh new file mode 100755 index 0000000..40787e1 --- /dev/null +++ b/oemcrypto/opk/ports/optee/push.sh @@ -0,0 +1,21 @@ +#!/bin/bash +[ -z "${OPTEE_DIR}" ] && echo "Set \$OPTEE_DIR to your OPTEE SDK root. See README.md" && exit +[ -z "${CDM_DIR}" ] && echo "Set \$CDM_DIR to your CDM_DIR repo root" && exit + +echo "copy oemcrypto_ta (a92d116c-ce27-4917-b30c-4a416e2d9351) to $OPTEE_DIR/oemcrypto/test" +cp ta/oemcrypto_ta/out/a92d116c-ce27-4917-b30c-4a416e2d9351.ta $OPTEE_DIR/oemcrypto/test +cp $CDM_DIR/out/opk_optee/debug/oemcrypto_helloworld $OPTEE_DIR/oemcrypto/test + +echo "copy wtpi_test_ta (b0f42504-01ec-11ec-9a03-0242ac130003) to $OPTEE_DIR/oemcrypto/test" +cp ta/wtpi_test_ta/out/b0f42504-01ec-11ec-9a03-0242ac130003.ta $OPTEE_DIR/oemcrypto/test +cp $CDM_DIR/out/opk_optee/debug/wtpi_unittests $OPTEE_DIR/oemcrypto/test + +test -d $OPTEE_DIR/oemcrypto/host || mkdir -p $OPTEE_DIR/oemcrypto/test +echo "copy oemcrypto unit tests to $OPTEE_DIR/oemcrypto/test" +cp $CDM_DIR/out/opk_optee/debug/oemcrypto_unittests $OPTEE_DIR/oemcrypto/test + +echo "copy liboemcrypto.so $OPTEE_DIR/oemcrypto/test" +cp $CDM_DIR/out/opk_optee/debug/lib.target/liboemcrypto.so $OPTEE_DIR/oemcrypto/test + +echo "copy install_ta.sh $OPTEE_DIR/oemcrypto/test" +cp ./install_ta.sh $OPTEE_DIR/oemcrypto/test diff --git a/oemcrypto/opk/ports/optee/ta/common/der_parse.c b/oemcrypto/opk/ports/optee/ta/common/der_parse.c new file mode 100644 index 0000000..12f7648 --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/common/der_parse.c @@ -0,0 +1,346 @@ +/* + * 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 "der_parse.h" +#include + +#include "asn1write.h" +#include "bignum.h" +#include "ecp.h" +#include "pk.h" +#include "rsa.h" + +// Helper for the mbedtls_asn1_write_* functions. Assumes the existence of +// a variable named `ret` with type `int`. +#define CHK_ASN1_WRITE(b, f) \ + do { \ + if ((ret = f) < 0) \ + return OEMCrypto_ERROR_UNKNOWN_FAILURE; \ + else \ + b += ret; \ + } while (0) + +/* + * Takes an mbedtls_mpi pointer |src|, an empty buffer pointer |dest|, and + * a 0 value |dest_len|. + * + * Extracts the length value from |src| and places that into |dest_len| + * + * Malloc's a buffer with size |dest_len| and assigns the buffer pointer to + * |dest| + * + * Extracts the data from |src| to |dest| with the mbed_mpi_write_binary + * function + * + */ +static OEMCryptoResult extract_mbedtls_mpi_param(mbedtls_mpi* src, + uint8_t** dest, + size_t* dest_len) { + if (src == NULL || dest == NULL || dest_len == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + size_t len = mbedtls_mpi_size(src); + + uint8_t* data = TEE_Malloc(len, 0); + if (data == TEE_HANDLE_NULL) { + EMSG("Malloc failed, out of memory"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + int ret = mbedtls_mpi_write_binary(src, data, len); + if (ret != 0 || len == 0) { + TEE_Free(data); + EMSG("mbedtls_mpi_write_binary failed"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + *dest = data; + *dest_len = len; + + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult DecodePKCS8RSAPrivateKey(const uint8_t* input, + size_t input_length, + pkcs1_rsa* output) { + OEMCryptoResult res; + + mbedtls_pk_context pk_ctx; + mbedtls_pk_init(&pk_ctx); + int ret = mbedtls_pk_parse_key(&pk_ctx, input, input_length, NULL, 0); + if (ret != 0) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + mbedtls_mpi N, P, Q, D, E, DP, DQ, QP; + + mbedtls_mpi_init(&N); + mbedtls_mpi_init(&P); + mbedtls_mpi_init(&Q); + mbedtls_mpi_init(&D); + mbedtls_mpi_init(&E); + mbedtls_mpi_init(&DP); + mbedtls_mpi_init(&DQ); + mbedtls_mpi_init(&QP); + + mbedtls_rsa_context* rsa_ctx = mbedtls_pk_rsa(pk_ctx); + + ret = mbedtls_rsa_export(rsa_ctx, &N, &P, &Q, &D, &E); + if (ret != 0) { + res = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup; + } + + ret = mbedtls_rsa_export_crt(rsa_ctx, &DP, &DQ, &QP); + if (ret != 0) { + res = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup; + } + + if ((res = extract_mbedtls_mpi_param( + &N, &output->modulus, &output->modulus_len)) != OEMCrypto_SUCCESS) + goto cleanup; + + if ((res = extract_mbedtls_mpi_param(&E, &output->public_exp, + &output->public_exp_len)) != + OEMCrypto_SUCCESS) + goto cleanup; + + if ((res = extract_mbedtls_mpi_param(&D, &output->private_exp, + &output->private_exp_len)) != + OEMCrypto_SUCCESS) + goto cleanup; + + if ((res = extract_mbedtls_mpi_param( + &P, &output->prime1, &output->prime1_len)) != OEMCrypto_SUCCESS) + goto cleanup; + + if ((res = extract_mbedtls_mpi_param( + &Q, &output->prime2, &output->prime2_len)) != OEMCrypto_SUCCESS) + goto cleanup; + + if ((res = extract_mbedtls_mpi_param(&DP, &output->exp1, + &output->exp1_len)) != OEMCrypto_SUCCESS) + goto cleanup; + + if ((res = extract_mbedtls_mpi_param(&DQ, &output->exp2, + &output->exp2_len)) != OEMCrypto_SUCCESS) + goto cleanup; + + if ((res = extract_mbedtls_mpi_param(&QP, &output->coefficient, + &output->coefficient_len)) != + OEMCrypto_SUCCESS) + goto cleanup; + + res = OEMCrypto_SUCCESS; + +cleanup: + + mbedtls_mpi_free(&N); + mbedtls_mpi_free(&P); + mbedtls_mpi_free(&Q); + mbedtls_mpi_free(&D); + mbedtls_mpi_free(&E); + mbedtls_mpi_free(&DP); + mbedtls_mpi_free(&DQ); + mbedtls_mpi_free(&QP); + + mbedtls_pk_free(&pk_ctx); + + return res; +} + +static uint32_t GlobalPlatformCurveId(mbedtls_ecp_group_id id) { + switch (id) { + case MBEDTLS_ECP_DP_SECP192R1: + return TEE_ECC_CURVE_NIST_P192; + + case MBEDTLS_ECP_DP_SECP224R1: + return TEE_ECC_CURVE_NIST_P224; + + case MBEDTLS_ECP_DP_SECP256R1: + return TEE_ECC_CURVE_NIST_P256; + + case MBEDTLS_ECP_DP_SECP384R1: + return TEE_ECC_CURVE_NIST_P384; + + case MBEDTLS_ECP_DP_SECP521R1: + return TEE_ECC_CURVE_NIST_P521; + + default: + return TEE_CRYPTO_ELEMENT_NONE; + } + + return TEE_CRYPTO_ELEMENT_NONE; +} + +static size_t CurveNumBits(mbedtls_ecp_group_id id) { + switch (id) { + case MBEDTLS_ECP_DP_SECP192R1: + return 192; + + case MBEDTLS_ECP_DP_SECP224R1: + return 224; + + case MBEDTLS_ECP_DP_SECP256R1: + return 256; + + case MBEDTLS_ECP_DP_SECP384R1: + return 384; + + case MBEDTLS_ECP_DP_SECP521R1: + return 521; + + default: + return 0; + } + + return 0; +} + +static size_t ECCSize(size_t num_bits) { + // ECC signature size is the length of the ASN1-encoded SEQUENCE containing + // two INTEGER elements, each large enough to contain |num_bits| of data. + // These integers represent the curve point and proof portions of the + // signature. + + if (num_bits == 0) { + return 0; + } + + size_t len = 0; + + // start with INTEGER tag portions + // tag itself is 1 byte + len++; + + // add enough space to hold num_bits + size_t num_bytes = (num_bits + 7) / 8; + len += num_bytes; + + // we have to encode this size value in ASN1, so figure out how many bytes + // that requires + while (num_bytes > 0) { + len++; + num_bytes = num_bytes >> 8; + } + + // plus 1 in case of a negative number + len++; + + // there are two such INTEGER portions. + len += len; + + // encode sequence size information + num_bytes = len; + + while (num_bytes > 0) { + len++; + num_bytes = num_bytes >> 8; + } + + // one last byte for the SEQ tag + len++; + + return len; +} + +OEMCryptoResult DecodePKCS8ECCPrivateKey(const uint8_t* input, + size_t input_length, + rfc5915_eckey* output) { + OEMCryptoResult res; + + mbedtls_pk_context pk_ctx; + mbedtls_pk_init(&pk_ctx); + int ret = mbedtls_pk_parse_key(&pk_ctx, input, input_length, NULL, 0); + if (ret != 0) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + mbedtls_ecp_keypair* ec_ctx = mbedtls_pk_ec(pk_ctx); + + output->ecc_curve_type = GlobalPlatformCurveId(ec_ctx->grp.id); + output->ecc_curve_bits = CurveNumBits(ec_ctx->grp.id); + output->max_signature_size = ECCSize(output->ecc_curve_bits); + + if ((res = extract_mbedtls_mpi_param(&(ec_ctx->d), &output->private_val, + &output->private_val_len)) != + OEMCrypto_SUCCESS) + goto cleanup; + if ((res = extract_mbedtls_mpi_param(&(ec_ctx->Q.X), &output->public_x, + &output->public_x_len)) != + OEMCrypto_SUCCESS) + goto cleanup; + if ((res = extract_mbedtls_mpi_param(&(ec_ctx->Q.Y), &output->public_y, + &output->public_y_len)) != + OEMCrypto_SUCCESS) + goto cleanup; + + res = OEMCrypto_SUCCESS; + +cleanup: + mbedtls_pk_free(&pk_ctx); + return res; +} + +// OP-TEE does not DER-encode the ECDSA signature. Instead it writes the raw +// R and S values of the signature to a buffer of key_size*2 length. The values +// are front-padded with zero so that they are each key_size in length. +OEMCryptoResult EncodeECDSASignature(const uint8_t* sig, size_t sig_length, + uint8_t* output, size_t* output_length) { + // Signature is assumed to be evenly split between R and S values. + if (sig_length % 2 != 0 || sig == NULL || output == NULL || + output_length == NULL) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + // mbedtls_asn1_write_* functions work backwards from the end of the output + // buffer. Therefore, start with the last elements and work forward. + int ret = 0; + uint8_t* p = output + *output_length; + size_t b = 0; // bytes written; + size_t element_len = sig_length / 2; + + size_t s_len = element_len; + size_t r_len = element_len; + + // Write S value raw data, then length, and integer tag. + CHK_ASN1_WRITE(b, mbedtls_asn1_write_raw_buffer(&p, output, sig + element_len, + element_len)); + if ((sig[element_len] & 0x80) != 0) { + CHK_ASN1_WRITE(b, mbedtls_asn1_write_tag(&p, output, 0)); + s_len++; + } + + CHK_ASN1_WRITE(b, mbedtls_asn1_write_len(&p, output, s_len)); + CHK_ASN1_WRITE(b, mbedtls_asn1_write_tag(&p, output, MBEDTLS_ASN1_INTEGER)); + + // Write R value raw data, then length, and integer tag + CHK_ASN1_WRITE(b, + mbedtls_asn1_write_raw_buffer(&p, output, sig, element_len)); + if ((sig[0] & 0x80) != 0) { + CHK_ASN1_WRITE(b, mbedtls_asn1_write_tag(&p, output, 0)); + r_len++; + } + CHK_ASN1_WRITE(b, mbedtls_asn1_write_len(&p, output, r_len)); + CHK_ASN1_WRITE(b, mbedtls_asn1_write_tag(&p, output, MBEDTLS_ASN1_INTEGER)); + + // Write length of data so far, then final sequence tag. "Constructed" flags + // needed for X509 format + CHK_ASN1_WRITE(b, mbedtls_asn1_write_len(&p, output, b)); + CHK_ASN1_WRITE( + b, mbedtls_asn1_write_tag( + &p, output, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)); + + // Since we've been writing backwards in the buffer, we might have written + // less than we initially thought. Realign everything to the beginning of the + // buffer. + TEE_MemMove(output, p, b); + *output_length = b; + + return OEMCrypto_SUCCESS; +} diff --git a/oemcrypto/opk/ports/optee/ta/common/der_parse.h b/oemcrypto/opk/ports/optee/ta/common/der_parse.h new file mode 100644 index 0000000..5831302 --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/common/der_parse.h @@ -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. + */ + +#ifndef _DER_PARSE_H_ +#define _DER_PARSE_H_ + +#include "OEMCryptoCENC.h" + +/* + * GlobalPlatform does not take in DER-encoded PKCS8 RSA keys directly. We need + * to extract the relevant data and then pass it to + * TEE_PopulateTransientObject(). + */ +typedef struct pkcs1_rsa { + uint32_t version; + uint8_t* modulus; + uint8_t* public_exp; + uint8_t* private_exp; + uint8_t* prime1; + uint8_t* prime2; + uint8_t* exp1; + uint8_t* exp2; + uint8_t* coefficient; + + size_t modulus_len; + size_t public_exp_len; + size_t private_exp_len; + size_t prime1_len; + size_t prime2_len; + size_t exp1_len; + size_t exp2_len; + size_t coefficient_len; +} pkcs1_rsa; + +/* + * Parses |input| data, which is a DER-encoded PKCS8 RSA private key. Extracts + * the RSA private key components such as modulus, exponents, primes, + * coefficient, etc., malloc's space for them in the |output| struct, and copies + * the data over. + * + * If there is a parsing error, returns OEMCrypto_ERROR_UNKNOWN_FAILURE + */ +OEMCryptoResult DecodePKCS8RSAPrivateKey(const uint8_t* input, + size_t input_length, + pkcs1_rsa* output); + +typedef struct rfc5915_eckey { + uint8_t* private_val; + uint8_t* public_x; + uint8_t* public_y; + + size_t private_val_len; + size_t public_x_len; + size_t public_y_len; + + // Number of bits in ECC private key + size_t ecc_curve_bits; + + // Size of the largest ASN1-encoded ECC signature that can be generated + size_t max_signature_size; + + // Integer identifier defined by GlobalPlatform to specify the curve type, + // outlined in Table 6-14 of GlobalPlatform TEE spec + uint32_t ecc_curve_type; +} rfc5915_eckey; + +/* + * Parses |input| data, which is a DER-encoded PKCS8 ECC keypair. Allocates + * buffers for the pointer types in the output. + * + * If there is a parsing error, returns OEMCrypto_ERROR_UNKNOWN_FAILURE + */ +OEMCryptoResult DecodePKCS8ECCPrivateKey(const uint8_t* input, + size_t input_length, + rfc5915_eckey* output); + +/* + * Takes the raw ECDSA signature data from |sig| and encodes it as an ASN1 + * SEQUENCE of two INTEGERs as specified by RFC 3279. Assumes that the |sig| + * buffer contains the R and S portions of the signature in order, with each + * portion being exactly sig_length/2 bytes long. + * + * |output| is the destination buffer to hold the ASN1 encoded signature. + * |output_length| will need to be larger than sig_length, otherwise this + * function will return an error. + */ +OEMCryptoResult EncodeECDSASignature(const uint8_t* sig, size_t sig_length, + uint8_t* output, size_t* output_length); + +#endif diff --git a/oemcrypto/opk/ports/optee/ta/common/ta_log.c b/oemcrypto/opk/ports/optee/ta/common/ta_log.c new file mode 100644 index 0000000..1e922b5 --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/common/ta_log.c @@ -0,0 +1,63 @@ +/* + * 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 "ta_log.h" +#include +#include "tos_logging_interface.h" + +/* This acts as a common function for both WTPI_Log and TOS_Log to redirect to. + * Since neither of them have `va_list` types in their function signatures, we + * can't just forward one to the other, we have to have this common `vlog` + * helper instead. + */ +#ifdef __GNUC__ +__attribute__((format(printf, 5, 0))) +#endif + void vlog(const char* file, + const char* function, int line, + LogPriority level, + const char* fmt, va_list varg) { + char buffer[128]; + vsnprintf(buffer, sizeof(buffer), fmt, varg); + if (level == LOG_ERROR) { + EMSG("Error: %s at [%s:%d]", buffer, file, line); + } else { + DMSG("%s: %s", function, buffer); + } +} + +static LogPriority OPK_To_WTPI_LogLevel(OPK_LogLevel level) { + switch (level) { + case OPK_LogLevel_Fatal: + case OPK_LogLevel_Error: + return LOG_ERROR; + case OPK_LogLevel_Warning: + case OPK_LogLevel_Info: + case OPK_LogLevel_Debug: + case OPK_LogLevel_Verbose: + return LOG_DEBUG; + default: + break; + } + return LOG_NONE; +} + +/* + * See "tos_logging_interface.h" for documentation of TOS_Log. + */ +#ifdef __GNUC__ +__attribute__((format(printf, 5, 6))) +#endif + void TOS_Log(const char* file, + const char* func, int line, + OPK_LogLevel level, + const char* fmt, ...) { + // Just forward the log message to common variadic log fn + va_list args; + va_start(args, fmt); + vlog(file, func, line, OPK_To_WTPI_LogLevel(level), fmt, args); + va_end(args); +} diff --git a/oemcrypto/opk/ports/optee/ta/common/ta_log.h b/oemcrypto/opk/ports/optee/ta/common/ta_log.h new file mode 100644 index 0000000..796a33c --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/common/ta_log.h @@ -0,0 +1,17 @@ +/* + * 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 _TA_LOG_H_ +#define _TA_LOG_H_ + +#include +#include +#include "wtpi_logging_interface.h" + +void vlog(const char* file, const char* function, int line, LogPriority level, + const char* fmt, va_list varg); + +#endif diff --git a/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_abort.c b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_abort.c new file mode 100644 index 0000000..ef20f39 --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_abort.c @@ -0,0 +1,21 @@ +/* + * 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 +#include "wtpi_abort_interface.h" + +void WTPI_Abort(void) { + TEE_Panic(TEE_ERROR_BAD_STATE); + while (1) { + } /* keep the compiler happy about this noreturn function */ +} + +/* + * Some code uses which isn't defined in the TEE. + * Resolve the dependency here. + */ +void __assert_fail(void) NORETURN; +void __assert_fail(void) { WTPI_Abort(); } diff --git a/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_clock_layer2.c b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_clock_layer2.c new file mode 100644 index 0000000..e6dcab9 --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_clock_layer2.c @@ -0,0 +1,44 @@ +/* + * 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 +#include "wtpi_clock_interface_layer2.h" + +OEMCryptoResult WTPI_GetSecureTimer(uint64_t* time_in_s) { + if (time_in_s == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + TEE_Time current_time; + TEE_Result res = TEE_GetTAPersistentTime(¤t_time); + if (res != TEE_SUCCESS) { + if (res == TEE_ERROR_TIME_NOT_SET || res == TEE_ERROR_TIME_NEEDS_RESET) { + // Try to set the time to 0 (aka first run) + current_time.seconds = 0; + current_time.millis = 0; + res = TEE_SetTAPersistentTime(¤t_time); + if (res != TEE_SUCCESS) { + EMSG("Problem setting TA persistent time, result 0x%x", res); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + // Try to get the time again + res = TEE_GetTAPersistentTime(¤t_time); + if (res != TEE_SUCCESS) { + EMSG( + "Problem getting TA persistent time after initial set, result 0x%x", + res); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } else { + EMSG("TEE_GetTAPersistentTime() returned error 0x%x", res); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } + + *time_in_s = (uint64_t)current_time.seconds; + return OEMCrypto_SUCCESS; +} diff --git a/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_config.c b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_config.c new file mode 100644 index 0000000..838d83b --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_config.c @@ -0,0 +1,82 @@ +/* + * 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_config_interface.h" + +OEMCrypto_Security_Level WTPI_GetSecurityLevel(void) { + return OEMCrypto_Level1; +} + +OEMCrypto_ProvisioningMethod WTPI_GetProvisioningMethod(void) { + return OEMCrypto_Keybox; +} + +uint32_t WTPI_GetResourceRatingTier(void) { return 1; } + +OPK_FeatureStatus WTPI_IsTAAntiRollbackEnabled(void) { + return OPK_FEATURE_NOT_SUPPORTED; +} + +bool WTPI_IsProductionReady(void) { return false; } + +OEMCrypto_WatermarkingSupport WTPI_GetWatermarkingSupport(void) { + return OEMCrypto_WatermarkingNotSupported; +} + +OEMCrypto_DTCP2_Capability WTPI_GetDTCP2Capability(void) { + return OEMCrypto_NO_DTCP2; +} + +OEMCryptoResult WTPI_GetCurrentSRMVersion(uint32_t* srm_version) { + if (srm_version == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + *srm_version = 0; + return OEMCrypto_SUCCESS; +} + +bool WTPI_IsAntiRollbackHWPresent(void) { return false; } + +OEMCryptoResult WTPI_ApplyCGMS(uint8_t cgms_field) { + (void)cgms_field; + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +bool WTPI_IsCGMS_AActive(void) { return false; } + +bool WTPI_SupportsCGMS_A(void) { return false; } + +bool WTPI_HasAnalogDisplay(void) { return false; } + +bool WTPI_IsAnalogDisplayActive(void) { return false; } + +bool WTPI_CanDisableAnalogDisplay(void) { return false; } + +bool WTPI_DisableAnalogDisplay(void) { return false; } + +size_t WTPI_MaxBufferSizeForDecrypt(void) { return 0; } + +size_t WTPI_MaxOutputSizeForDecrypt(void) { return 0; } + +bool WTPI_IsClosedPlatform(void) { return false; } + +OEMCrypto_HDCP_Capability WTPI_CurrentHDCPCapability(void) { + return HDCP_NO_DIGITAL_OUTPUT; +} + +OEMCrypto_HDCP_Capability WTPI_MaxHDCPCapability(void) { + return HDCP_NO_DIGITAL_OUTPUT; +} + +size_t WTPI_MaxBufferSizeForGenericCrypto(void) { return 0; } + +size_t WTPI_MaxSampleSize(void) { return 0; } + +// GlobalPlatform spec says "256, 512, 768, 1024, 1536 and 2048 bit keys SHALL +// be supported." +uint32_t WTPI_SupportedCertificates(void) { + return OEMCrypto_Supports_RSA_2048bit; +} diff --git a/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_config_macros.h b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_config_macros.h new file mode 100644 index 0000000..7750c38 --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_config_macros.h @@ -0,0 +1,44 @@ +/* + * 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 _WTPI_CONFIG_MACROS_H_ +#define _WTPI_CONFIG_MACROS_H_ + +/** + * Following should not be more than UINT32_MAX - 1. + */ +#define MAX_NUMBER_OF_SESSIONS 20 +#define MAX_NUMBER_OF_ENTITLED_KEY_SESSIONS 10 +#define MAX_NUMBER_OF_KEYS 100 +#define CONTENT_KEYS_PER_SESSION 10 +#define ENTITLEMENT_KEYS_PER_SESSION 10 +/** 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 +#define MAX_SYMMETRIC_KEY_SIZE 32 +#define MAX_WRAPPED_SYMMETRIC_KEY_SIZE MAX_SYMMETRIC_KEY_SIZE +#define MAX_WRAPPED_ASYMMETRIC_KEY_SIZE \ + (PKCS8_DRM_KEY_MAX_SIZE + ENCRYPT_AND_SIGN_EXTRA) + +#endif // _WTPI_CONFIG_MACROS_H_ diff --git a/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_crypto_and_key_management_layer1.c b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_crypto_and_key_management_layer1.c new file mode 100644 index 0000000..dcde3b7 --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_crypto_and_key_management_layer1.c @@ -0,0 +1,700 @@ +/* + * 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 +#include +#include +#include "der_parse.h" +#include "malloc.h" +#include "oemcrypto_compiler_attributes.h" +#include "oemcrypto_key_types.h" +#include "oemcrypto_math.h" +#include "oemcrypto_overflow.h" +#include "tos_shared_memory_interface.h" +#include "wtpi_abort_interface.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" +#include "wtpi_crypto_asymmetric_interface.h" +#include "wtpi_device_key_interface.h" + +/* + * + * TODO(b/206651517): Replace raw key data with pre-allocated + * TEE_OperationHandle and TEE_ObjectHandle. + * + * GlobalPlatform requires that a TEE_ObjectHandle is constructed with + * a specific operation in mind. For example, if we want to construct a key + * handle for a SHA1 operation, we specify SHA1 when we assign the key to the + * handle. Similarly for SHA256, we specify SHA256 during TEE_ObjectHandle + * construction. + * + * Unfortunately, the current SymmetricKeyType enum does not have that level of + * granularity. We use MAC_KEY_CLIENT for both SHA1 and SHA256 operations, so + * a CreateKeyHandle() request does not have enough information for us to + * guarantee a valid TEE_ObjectHandle result. + * + * To get around this, we re-allocate the TEE_OperationHandle (for the crypto + * operation we want) and TEE_ObjectHandle (for the key associated with that + * crypto operation) for every symmetric key crypto function; we don't know + * enough information to do this until the actual WTPI function is called. At + * the end of the function, we free the op/key handles. + * + * Asymmetric keys still have this problem, but to a lesser degree. The + * AsymmetricKeyType enum does not specify signing vs decrypting, so we don't + * know which operation handle to generate if we're given that enum value. + * Thankfully, those are the only two operations we do, so it's not burdensome + * to pre-allocate both options. The key object is even easier, there's only one + * choice (TEE_TYPE_RSA_KEYPAIR). + * + * TODO: also maybe add key type as a member? good to check compatibility with + * desired operation, eg. don't try to AES encrypt with an HMAC key + */ +typedef struct wtpi_k1_symmetric_key_handle { + uint8_t* key; + uint32_t key_size; // TODO: change to KeySize type +} wtpi_k1_symmetric_key_handle; + +OEMCryptoResult WTPI_K1_GetKeySize(WTPI_K1_SymmetricKey_Handle key, + KeySize* size) { + if (size == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT; + *size = OPK_LengthToKeySize(key->key_size); + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult Helper_AESEncryptBlock_ECB(WTPI_K1_SymmetricKey_Handle key, + const uint8_t* input, + uint8_t* output) { + if (input == NULL || output == NULL || key == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + TEE_OperationHandle op_handle; + TEE_ObjectHandle key_handle; + uint32_t algo = TEE_ALG_AES_ECB_NOPAD; + uint32_t mode = TEE_MODE_ENCRYPT; + uint32_t size = key->key_size * 8; + TEE_Result res = TEE_AllocateOperation(&op_handle, algo, mode, size); + if (res != TEE_SUCCESS) { + EMSG("TEE_AllocateOperation() failed with result 0x%x", res); + goto err; + } + + res = TEE_AllocateTransientObject(TEE_TYPE_AES, size, &key_handle); + if (res != TEE_SUCCESS) { + EMSG("TEE_AllocateTransientObject() failed with result 0x%x", res); + goto err; + } + TEE_Attribute attr; + TEE_InitRefAttribute(&attr, TEE_ATTR_SECRET_VALUE, key->key, key->key_size); + + res = TEE_PopulateTransientObject(key_handle, &attr, 1); + if (res != TEE_SUCCESS) { + EMSG("TEE_PopulateTransientObject() failed with result 0x%x", res); + goto err; + } + + res = TEE_SetOperationKey(op_handle, key_handle); + if (res != TEE_SUCCESS) { + EMSG("TEE_SetOperationKey() failed with result 0x%x", res); + goto err; + } + + TEE_CipherInit(op_handle, NULL, 0); + + size_t output_len = 128; + size_t in_length = AES_BLOCK_SIZE; + res = TEE_CipherUpdate(op_handle, input, in_length, output, &output_len); + if (res != TEE_SUCCESS) { + EMSG("TEE_CipherUpdate() failed with result 0x%x", res); + if (output_len != 128) { + EMSG("output_len was changed to %d", output_len); + } + goto err; + } + + if (op_handle != TEE_HANDLE_NULL) TEE_FreeOperation(op_handle); + if (key_handle != TEE_HANDLE_NULL) TEE_FreeTransientObject(key_handle); + return OEMCrypto_SUCCESS; + +err: + if (op_handle != TEE_HANDLE_NULL) TEE_FreeOperation(op_handle); + if (key_handle != TEE_HANDLE_NULL) TEE_FreeTransientObject(key_handle); + + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} +static OEMCryptoResult Helper_AESCBC(WTPI_K1_SymmetricKey_Handle key, + size_t key_length_to_use, + const uint8_t* in, size_t in_length, + const uint8_t* iv, uint8_t* out, + uint32_t mode) { + if (mode != TEE_MODE_ENCRYPT && mode != TEE_MODE_DECRYPT) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + TEE_OperationHandle op_handle; + TEE_ObjectHandle key_handle; + uint32_t algo = TEE_ALG_AES_CBC_NOPAD; + uint32_t size = key_length_to_use * 8; + TEE_Result res = TEE_AllocateOperation(&op_handle, algo, mode, size); + if (res != TEE_SUCCESS) { + EMSG("TEE_AllocateOperation() failed with result 0x%x", res); + goto err; + } + + res = TEE_AllocateTransientObject(TEE_TYPE_AES, size, &key_handle); + if (res != TEE_SUCCESS) { + EMSG("TEE_AllocateTransientObject() failed with result 0x%x", res); + goto err; + } + + TEE_Attribute attr; + TEE_InitRefAttribute(&attr, TEE_ATTR_SECRET_VALUE, key->key, + key_length_to_use); + + res = TEE_PopulateTransientObject(key_handle, &attr, 1); + if (res != TEE_SUCCESS) { + EMSG("TEE_PopulateTransientObject() failed with result 0x%x", res); + goto err; + } + + res = TEE_SetOperationKey(op_handle, key_handle); + if (res != TEE_SUCCESS) { + EMSG("TEE_SetOperationKey() failed with result 0x%x", res); + goto err; + } + + TEE_CipherInit(op_handle, iv, 16); + + size_t output_len = in_length; + res = TEE_CipherUpdate(op_handle, in, in_length, out, &output_len); + if (res != TEE_SUCCESS) { + EMSG("TEE_CipherUpdate() failed with result 0x%x", res); + if (output_len != in_length) { + EMSG("output_len was changed to %d", output_len); + } + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (op_handle != TEE_HANDLE_NULL) TEE_FreeOperation(op_handle); + if (key_handle != TEE_HANDLE_NULL) TEE_FreeTransientObject(key_handle); + return OEMCrypto_SUCCESS; + +err: + if (op_handle != TEE_HANDLE_NULL) TEE_FreeOperation(op_handle); + if (key_handle != TEE_HANDLE_NULL) TEE_FreeTransientObject(key_handle); + + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +OEMCryptoResult WTPI_C1_AESCBCDecrypt(WTPI_K1_SymmetricKey_Handle key, + size_t key_length, const uint8_t* in, + size_t in_length, const uint8_t* iv, + uint8_t* out) { + if (key == NULL || key_length == 0 || + (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; + } + + return Helper_AESCBC(key, key_length, in, in_length, iv, out, + TEE_MODE_DECRYPT); +} + +OEMCryptoResult WTPI_C1_AESCBCEncrypt(WTPI_K1_SymmetricKey_Handle key, + const uint8_t* in, size_t in_length, + const uint8_t* iv, uint8_t* out) { + if (key == NULL || in == NULL || in_length == 0 || + in_length % AES_BLOCK_SIZE != 0 || iv == NULL || out == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + return Helper_AESCBC(key, key->key_size, in, in_length, iv, out, + TEE_MODE_ENCRYPT); +} + +OEMCryptoResult WTPI_C1_HMAC_SHA1(WTPI_K1_SymmetricKey_Handle key, + const uint8_t* message, size_t message_length, + uint8_t* out) { + if (key == NULL || message == NULL || message_length == 0 || out == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + TEE_OperationHandle op_handle; + TEE_ObjectHandle key_handle; + uint32_t algo = TEE_ALG_HMAC_SHA1; + uint32_t mode = TEE_MODE_MAC; + uint32_t size = key->key_size * 8; + TEE_Result res = TEE_AllocateOperation(&op_handle, algo, mode, size); + if (res != TEE_SUCCESS) { + EMSG("Could not allocate op with result 0x%x", res); + goto err; + } + + res = TEE_AllocateTransientObject(TEE_TYPE_HMAC_SHA1, size, &key_handle); + if (res != TEE_SUCCESS) { + EMSG("TEE_AllocateTransientObject() failed with result 0x%x", res); + goto err; + } + TEE_Attribute attr; + TEE_InitRefAttribute(&attr, TEE_ATTR_SECRET_VALUE, key->key, key->key_size); + + res = TEE_PopulateTransientObject(key_handle, &attr, 1); + if (res != TEE_SUCCESS) { + EMSG("TEE_PopulateTransientObject() failed with result 0x%x", res); + goto err; + } + + res = TEE_SetOperationKey(op_handle, key_handle); + if (res != TEE_SUCCESS) { + EMSG("TEE_SetOperationKey() failed with result 0x%x", res); + goto err; + } + + TEE_MACInit(op_handle, NULL, 0); + TEE_MACUpdate(op_handle, message, message_length); + + size_t output_len = 32; + res = TEE_MACComputeFinal(op_handle, message, 0, out, &output_len); + if (res != TEE_SUCCESS) { + EMSG("TEE_MACComputeFinal() failed with result 0x%x", res); + if (output_len != 32) { + EMSG("output_len was changed to %d", output_len); + } + goto err; + } + + if (op_handle != TEE_HANDLE_NULL) TEE_FreeOperation(op_handle); + if (key_handle != TEE_HANDLE_NULL) TEE_FreeTransientObject(key_handle); + return OEMCrypto_SUCCESS; +err: + if (op_handle != TEE_HANDLE_NULL) TEE_FreeOperation(op_handle); + if (key_handle != TEE_HANDLE_NULL) TEE_FreeTransientObject(key_handle); + + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +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; + } + + TEE_Result res; + + TEE_OperationHandle op_handle; + uint32_t algo = TEE_ALG_SHA256; + uint32_t mode = TEE_MODE_DIGEST; + res = TEE_AllocateOperation(&op_handle, algo, mode, 0); + if (res != TEE_SUCCESS) { + EMSG("TEE_AllocateOperation() failed with result 0x%x", res); + goto err; + } + + size_t chunk_len = 32; + size_t remaining_len = message_length; + for (size_t i = 0; i < message_length; i += chunk_len) { + if (remaining_len < chunk_len) { + chunk_len = remaining_len; + } + TEE_DigestUpdate(op_handle, message + i, chunk_len); + remaining_len -= chunk_len; + } + + size_t output_len = 32; + res = TEE_DigestDoFinal(op_handle, message, 0, out, &output_len); + if (res != TEE_SUCCESS) { + EMSG("TEE_DigestDoFinal() failed with result 0x%x", res); + if (output_len != 32) { + EMSG("output_len was changed to %d", output_len); + } + goto err; + } + + if (op_handle != TEE_HANDLE_NULL) TEE_FreeOperation(op_handle); + + return OEMCrypto_SUCCESS; + +err: + if (op_handle != TEE_HANDLE_NULL) TEE_FreeOperation(op_handle); + + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +OEMCryptoResult WTPI_C1_HMAC_SHA256(WTPI_K1_SymmetricKey_Handle key, + const uint8_t* message, + size_t message_length, uint8_t* out) { + if (key == NULL || message == NULL || message_length == 0 || out == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + TEE_OperationHandle op_handle; + TEE_ObjectHandle key_handle; + uint32_t algo = TEE_ALG_HMAC_SHA256; + uint32_t mode = TEE_MODE_MAC; + uint32_t size = key->key_size * 8; + TEE_Result res = TEE_AllocateOperation(&op_handle, algo, mode, size); + if (res != TEE_SUCCESS) { + EMSG("Could not allocate op with result 0x%x, %u, %u, %u", res, algo, mode, + size); + goto err; + } + + res = TEE_AllocateTransientObject(TEE_TYPE_HMAC_SHA256, size, &key_handle); + if (res != TEE_SUCCESS) { + EMSG("TEE_AllocateTransientObject() failed with result 0x%x", res); + goto err; + } + TEE_Attribute attr; + TEE_InitRefAttribute(&attr, TEE_ATTR_SECRET_VALUE, key->key, key->key_size); + + res = TEE_PopulateTransientObject(key_handle, &attr, 1); + if (res != TEE_SUCCESS) { + EMSG("TEE_PopulateTransientObject() failed with result 0x%x", res); + goto err; + } + res = TEE_SetOperationKey(op_handle, key_handle); + if (res != TEE_SUCCESS) { + EMSG("Could not set op key with result 0x%x", res); + goto err; + } + + TEE_MACInit(op_handle, NULL, 0); + TEE_MACUpdate(op_handle, message, message_length); + + size_t output_len = 32; + res = TEE_MACComputeFinal(op_handle, message, 0, out, &output_len); + if (res != TEE_SUCCESS) { + EMSG("TEE_MACComputeFinal() failed with result 0x%x", res); + if (output_len != 32) { + EMSG("output_len was changed to %d", output_len); + } + goto err; + } + + if (op_handle != TEE_HANDLE_NULL) TEE_FreeOperation(op_handle); + if (key_handle != TEE_HANDLE_NULL) TEE_FreeTransientObject(key_handle); + return OEMCrypto_SUCCESS; +err: + if (op_handle != TEE_HANDLE_NULL) TEE_FreeOperation(op_handle); + if (key_handle != TEE_HANDLE_NULL) TEE_FreeTransientObject(key_handle); + + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +OEMCryptoResult WTPI_C1_HMAC_SHA256_Verify(WTPI_K1_SymmetricKey_Handle key, + const uint8_t* message, + size_t message_length, + const uint8_t* signature) { + if (signature == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + uint8_t computed_signature[SHA256_DIGEST_LENGTH]; + OEMCryptoResult result = + WTPI_C1_HMAC_SHA256(key, message, message_length, computed_signature); + if (result != OEMCrypto_SUCCESS) return result; + if (TEE_MemCompare(signature, computed_signature, SHA256_DIGEST_LENGTH) != + 0) { + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_C1_CopyToOutputBuffer(const uint8_t* in, size_t size, + const OPK_OutputBuffer* out, + size_t output_offset) { + size_t total_size; + if (OPK_AddOverflowUX(output_offset, size, &total_size)) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + if (in == NULL || out == NULL || size == 0 || total_size > out->size) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + // TODO: secure buffer + if (out->type == OPK_SECURE_OUTPUT_BUFFER) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } else if (out->type != OPK_CLEAR_INSECURE_OUTPUT_BUFFER) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + uint8_t* const dest = out->buffer.clear_insecure + output_offset; + TEE_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; + } + + // TODO: running into memory access issues for values greater than 4096, + // causing TA to panic. I don't think there's a reason to have random bytes + // in a buffer this large anyway, but I might be wrong. + if (size >= 4096) { + return OEMCrypto_ERROR_BUFFER_TOO_LARGE; + } + + TEE_GenerateRandom((void*)out, size); + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_K1_InitializeKeyManagement(void) { + 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; + } + + if (size != KEY_SIZE_128 && size != KEY_SIZE_256) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + WTPI_K1_SymmetricKey_Handle sess = + TEE_Malloc(sizeof(wtpi_k1_symmetric_key_handle), TEE_MALLOC_FILL_ZERO); + if (sess == NULL) { + EMSG("Malloc failed, out of memory"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + uint8_t* key_space = TEE_Malloc(size, 0); + if (key_space == NULL) { + EMSG("Malloc failed, out of memory"); + goto err; + } + TEE_MemMove(key_space, serialized_bytes, size); + sess->key = key_space; + + sess->key_size = (uint32_t)size; + + *out_key_handle = sess; + return OEMCrypto_SUCCESS; + +err: + WTPI_K1_FreeKeyHandle(sess); + + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +OEMCryptoResult WTPI_K1_DeriveDeviceKeyIntoHandle( + uint32_t context, SymmetricKeyType out_key_type, + WTPI_K1_SymmetricKey_Handle* out_key_handle, KeySize out_key_size) { + // TODO (b/201702904): Use a persistent, device-unique key + const uint8_t fake_device_key[KEY_SIZE_128] = { + 0xa2, 0xe5, 0x11, 0x54, 0x12, 0x60, 0xf0, 0xe1, + 0xa8, 0x72, 0xeb, 0x15, 0x48, 0x41, 0xc7, 0x87}; + + WTPI_K1_SymmetricKey_Handle temp_key_handle; + OEMCryptoResult result = WTPI_K1_CreateKeyHandle( + fake_device_key, KEY_SIZE_128, DERIVING_KEY, &temp_key_handle); + if (result != OEMCrypto_SUCCESS) return result; + + uint8_t full_context[16] = {'.', '.', '.', '.', 'W', 'i', 'd', 'e', + 'v', 'i', 'n', 'e', ' ', 'O', 'P', 'K'}; + + const size_t context_length = sizeof(full_context); + + // Set the first four bytes to the specific use for this key. + TEE_MemMove(full_context, &context, 4); + const uint8_t counter = 1; + result = WTPI_K1_DeriveKeyFromKeyHandle( + temp_key_handle, counter, full_context, context_length, out_key_type, + out_key_size, out_key_handle); + WTPI_K1_FreeKeyHandle(temp_key_handle); + return result; +} + +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 || + iv == NULL || out_key_handle == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + uint32_t size; + OEMCryptoResult res = WTPI_K1_GetKeySize(decrypt_key_handle, &size); + if (res != OEMCrypto_SUCCESS) { + return res; + } + + uint8_t decrypted[MAC_KEY_SIZE]; + res = WTPI_C1_AESCBCDecrypt(decrypt_key_handle, size, enc_key, enc_key_length, + iv, decrypted); + if (res != OEMCrypto_SUCCESS) { + return res; + } + + return WTPI_K1_CreateKeyHandle(decrypted, 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 || iv == NULL || out_mac_key_server == NULL || + out_mac_key_client == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + uint32_t size; + OEMCryptoResult res = WTPI_K1_GetKeySize(decrypt_key_handle, &size); + if (res != OEMCrypto_SUCCESS) { + return res; + } + + uint8_t decrypted[MAC_KEY_SIZE * 2]; + res = WTPI_C1_AESCBCDecrypt(decrypt_key_handle, size, enc_mac_keys, + enc_mac_keys_length, iv, decrypted); + if (res != OEMCrypto_SUCCESS) { + return res; + } + + res = WTPI_K1_CreateKeyHandle(decrypted, MAC_KEY_SIZE, MAC_KEY_SERVER, + out_mac_key_server); + if (res != OEMCrypto_SUCCESS) { + return res; + } + return WTPI_K1_CreateKeyHandle(decrypted + MAC_KEY_SIZE, MAC_KEY_SIZE, + MAC_KEY_CLIENT, out_mac_key_client); +} + +OEMCryptoResult WTPI_K1_DeriveKeyFromKeyHandle( + WTPI_K1_SymmetricKey_Handle key, 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 == NULL || context == NULL || context_length == 0 || + out_key_handle == NULL || + (out_key_size != KEY_SIZE_256 && out_key_size != KEY_SIZE_128)) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + TEE_OperationHandle op_handle; + TEE_ObjectHandle key_handle; + uint32_t algo = TEE_ALG_AES_CMAC; + uint32_t mode = TEE_MODE_MAC; + + uint32_t size; + OEMCryptoResult oec_res = WTPI_K1_GetKeySize(key, &size); + if (oec_res != OEMCrypto_SUCCESS) { + return oec_res; + } + size = size * 8; + + TEE_Result res = TEE_AllocateOperation(&op_handle, algo, mode, size); + + if (res != TEE_SUCCESS) { + EMSG("TEE_AllocateOperation() failed with result 0x%x", res); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + res = TEE_AllocateTransientObject(TEE_TYPE_AES, size, &key_handle); + if (res != TEE_SUCCESS) { + EMSG("TEE_AllocateTransientObject() failed with result 0x%x", res); + goto err; + } + TEE_Attribute attr; + TEE_InitRefAttribute(&attr, TEE_ATTR_SECRET_VALUE, key->key, key->key_size); + + res = TEE_PopulateTransientObject(key_handle, &attr, 1); + if (res != TEE_SUCCESS) { + EMSG("TEE_PopulateTransientObject() failed with result 0x%x", res); + goto err; + } + + res = TEE_SetOperationKey(op_handle, key_handle); + if (res != TEE_SUCCESS) { + EMSG("TEE_SetOperationKey() failed with result 0x%x", res); + goto err; + } + + uint8_t buffer[KEY_SIZE_256]; + for (uint8_t i = 0; i < out_key_size; i += KEY_SIZE_128) { + TEE_MACInit(op_handle, NULL, 0); + TEE_MACUpdate(op_handle, &counter, sizeof(counter)); + counter++; + size_t output_len = KEY_SIZE_128; + res = TEE_MACComputeFinal(op_handle, context, context_length, buffer + i, + &output_len); + if (res != TEE_SUCCESS) { + EMSG("TEE_MACComputeFinal() failed with result 0x%x", res); + if (output_len != KEY_SIZE_128) { + EMSG("output_len was changed to %d", output_len); + } + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } + + if (op_handle != TEE_HANDLE_NULL) TEE_FreeOperation(op_handle); + if (key_handle != TEE_HANDLE_NULL) TEE_FreeTransientObject(key_handle); + return WTPI_K1_CreateKeyHandle(buffer, out_key_size, out_key_type, + out_key_handle); + +err: + if (op_handle != TEE_HANDLE_NULL) TEE_FreeOperation(op_handle); + if (key_handle != TEE_HANDLE_NULL) TEE_FreeTransientObject(key_handle); + + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +OEMCryptoResult WTPI_K1_WrapKey(UNUSED uint32_t context, + WTPI_K1_SymmetricKey_Handle key, + SymmetricKeyType key_type, uint8_t* wrapped_key, + size_t wrapped_key_length) { + if (key == NULL || wrapped_key == NULL || wrapped_key_length == 0) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + if (wrapped_key_length != key->key_size) { + /* The caller should give us the correct buffer size. */ + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + /* TODO: encrypt the data. */ + TEE_MemMove(wrapped_key, key->key, wrapped_key_length); + + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_K1_UnwrapIntoKeyHandle( + UNUSED 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; + } + if (wrapped_key_length != KEY_SIZE_128 && + wrapped_key_length != KEY_SIZE_256) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + // TODO: just passing straight through since we aren't wrapping right now + 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 (key_handle == NULL) { + return OEMCrypto_SUCCESS; + } + + TEE_Free(key_handle->key); + TEE_Free(key_handle); + + return OEMCrypto_SUCCESS; +} diff --git a/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_crypto_asymmetric.c b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_crypto_asymmetric.c new file mode 100644 index 0000000..06fb63b --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_crypto_asymmetric.c @@ -0,0 +1,487 @@ +/* + * 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 +#include +#include +#include "der_parse.h" +#include "malloc.h" +#include "oemcrypto_compiler_attributes.h" +#include "oemcrypto_key_types.h" +#include "oemcrypto_math.h" +#include "oemcrypto_overflow.h" +#include "tos_shared_memory_interface.h" +#include "wtpi_abort_interface.h" +#include "wtpi_crypto_asymmetric_interface.h" + +#define ECC_KEY_MAX_BITS 521 +#define ECC_KEY_MAX_BYTES ((ECC_KEY_MAX_BITS + 7) / 8 + 1) + +typedef struct tee_asymmetric_key_handle { + TEE_ObjectHandle key_handle; + uint32_t max_signature_size; + uint32_t ecc_curve_type; // only used for ECC operations +} tee_asymmetric_key_handle; + +static OEMCryptoResult Helper_CreateRSAKeyHandle( + const uint8_t* serialized_bytes, size_t size, + WTPI_AsymmetricKey_Handle* key_handle) { + WTPI_AsymmetricKey_Handle sess = + TEE_Malloc(sizeof(tee_asymmetric_key_handle), TEE_MALLOC_FILL_ZERO); + if (sess == NULL) { + EMSG("Malloc failed, out of memory"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + TEE_Result res = TEE_SUCCESS; + pkcs1_rsa* sess_key = TEE_Malloc(sizeof(pkcs1_rsa), TEE_MALLOC_FILL_ZERO); + if (sess_key == TEE_HANDLE_NULL) { + EMSG("Malloc failed, out of memory"); + goto cleanup; + } + + res = DecodePKCS8RSAPrivateKey(serialized_bytes, size, sess_key); + if (res != OEMCrypto_SUCCESS) { + EMSG("RSA DER parsing failed with result %d", res); + WTPI_FreeAsymmetricKeyHandle(sess); + TEE_Free(sess_key); + return OEMCrypto_ERROR_INVALID_KEY; + } + + sess->max_signature_size = sess_key->modulus_len; + sess->ecc_curve_type = 0; + + res = TEE_AllocateTransientObject(TEE_TYPE_RSA_KEYPAIR, 2048, + &sess->key_handle); + if (res != TEE_SUCCESS) { + EMSG("TEE_AllocateTransientObject() failed with result 0x%x", res); + goto cleanup; + } + + TEE_Attribute attrs[8]; + TEE_InitRefAttribute(&attrs[0], TEE_ATTR_RSA_MODULUS, sess_key->modulus, + sess_key->modulus_len); + TEE_InitRefAttribute(&attrs[1], TEE_ATTR_RSA_PUBLIC_EXPONENT, + sess_key->public_exp, sess_key->public_exp_len); + TEE_InitRefAttribute(&attrs[2], TEE_ATTR_RSA_PRIVATE_EXPONENT, + sess_key->private_exp, sess_key->private_exp_len); + TEE_InitRefAttribute(&attrs[3], TEE_ATTR_RSA_PRIME1, sess_key->prime1, + sess_key->prime1_len); + TEE_InitRefAttribute(&attrs[4], TEE_ATTR_RSA_PRIME2, sess_key->prime2, + sess_key->prime2_len); + TEE_InitRefAttribute(&attrs[5], TEE_ATTR_RSA_EXPONENT1, sess_key->exp1, + sess_key->exp1_len); + TEE_InitRefAttribute(&attrs[6], TEE_ATTR_RSA_EXPONENT2, sess_key->exp2, + sess_key->exp2_len); + TEE_InitRefAttribute(&attrs[7], TEE_ATTR_RSA_COEFFICIENT, + sess_key->coefficient, sess_key->coefficient_len); + + res = TEE_PopulateTransientObject(sess->key_handle, + (const TEE_Attribute*)(&attrs), 8); + if (res != TEE_SUCCESS) { + EMSG("TEE_PopulateTransientObject() failed with result 0x%x", res); + goto cleanup; + } + +cleanup: + if (sess_key != TEE_HANDLE_NULL) { + TEE_Free(sess_key->modulus); + TEE_Free(sess_key->exp1); + TEE_Free(sess_key->exp2); + TEE_Free(sess_key->prime1); + TEE_Free(sess_key->prime2); + TEE_Free(sess_key->coefficient); + TEE_Free(sess_key->public_exp); + TEE_Free(sess_key->private_exp); + } + TEE_Free(sess_key); + + if (res != TEE_SUCCESS) { + WTPI_FreeAsymmetricKeyHandle(sess); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + *key_handle = sess; + return OEMCrypto_SUCCESS; +} + +static OEMCryptoResult Helper_CreateECCKeyHandle( + const uint8_t* serialized_bytes, size_t size, + WTPI_AsymmetricKey_Handle* key_handle) { + WTPI_AsymmetricKey_Handle sess = + TEE_Malloc(sizeof(tee_asymmetric_key_handle), TEE_MALLOC_FILL_ZERO); + if (sess == NULL) { + EMSG("Malloc failed, out of memory"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + TEE_Result res = TEE_SUCCESS; + rfc5915_eckey* sess_key = + TEE_Malloc(sizeof(rfc5915_eckey), TEE_MALLOC_FILL_ZERO); + if (sess_key == TEE_HANDLE_NULL) { + EMSG("Malloc failed, out of memory"); + goto cleanup; + } + + res = DecodePKCS8ECCPrivateKey(serialized_bytes, size, sess_key); + if (res != OEMCrypto_SUCCESS) { + EMSG("ECC DER parsing failed with result %d", res); + WTPI_FreeAsymmetricKeyHandle(sess); + TEE_Free(sess_key); + return OEMCrypto_ERROR_INVALID_RSA_KEY; + } + + sess->max_signature_size = sess_key->max_signature_size; + sess->ecc_curve_type = sess_key->ecc_curve_type; + + res = TEE_AllocateTransientObject( + TEE_TYPE_ECDSA_KEYPAIR, sess_key->private_val_len * 8, &sess->key_handle); + if (res != TEE_SUCCESS) { + EMSG("TEE_AllocateTransientObject() failed with result 0x%x", res); + goto cleanup; + } + + DMSG("Private key length is %d", sess_key->private_val_len); + + TEE_Attribute attrs[4]; + TEE_InitRefAttribute(&attrs[0], TEE_ATTR_ECC_PRIVATE_VALUE, + sess_key->private_val, sess_key->private_val_len); + TEE_InitRefAttribute(&attrs[1], TEE_ATTR_ECC_PUBLIC_VALUE_X, + sess_key->public_x, sess_key->public_x_len); + TEE_InitRefAttribute(&attrs[2], TEE_ATTR_ECC_PUBLIC_VALUE_Y, + sess_key->public_y, sess_key->public_y_len); + TEE_InitValueAttribute(&attrs[3], TEE_ATTR_ECC_CURVE, + sess_key->ecc_curve_type, 0); + + res = TEE_PopulateTransientObject(sess->key_handle, + (const TEE_Attribute*)(&attrs), 4); + if (res != TEE_SUCCESS) { + EMSG("TEE_PopulateTransientObject() failed with result 0x%x", res); + goto cleanup; + } + +cleanup: + if (sess_key != TEE_HANDLE_NULL) { + TEE_Free(sess_key->private_val); + TEE_Free(sess_key->public_x); + TEE_Free(sess_key->public_y); + } + TEE_Free(sess_key); + + if (res != TEE_SUCCESS) { + WTPI_FreeAsymmetricKeyHandle(sess); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + *key_handle = sess; + return OEMCrypto_SUCCESS; +} + +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 || key_handle == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + switch (key_type) { + case DRM_RSA_PRIVATE_KEY: + return Helper_CreateRSAKeyHandle(serialized_bytes, size, key_handle); + case DRM_ECC_PRIVATE_KEY: + return Helper_CreateECCKeyHandle(serialized_bytes, size, key_handle); + default: + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult WTPI_CreateTestRSAKeyHandle( + WTPI_AsymmetricKey_Handle* key_handle UNUSED) { + // TODO: wtpi_test_impl/crypto_interface has this unimplemented. What is + // it supposed to do? + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult WTPI_FreeAsymmetricKeyHandle( + WTPI_AsymmetricKey_Handle key_handle) { + if (key_handle == TEE_HANDLE_NULL) { + return OEMCrypto_SUCCESS; + } + + TEE_FreeTransientObject(key_handle->key_handle); + + TEE_Free(key_handle); + + return OEMCrypto_SUCCESS; +} + +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) { + if (key == NULL || message == NULL || message_length == 0 || + signature_length == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + size_t private_key_size = key->max_signature_size; + if (signature == NULL || *signature_length < private_key_size) { + *signature_length = private_key_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + if (padding_scheme == kSign_RSASSA_PSS) { + // generate SHA1 digest + uint8_t digest[20]; + TEE_OperationHandle sha1_op; + TEE_Result res = + TEE_AllocateOperation(&sha1_op, TEE_ALG_SHA1, TEE_MODE_DIGEST, 0); + if (res != TEE_SUCCESS) { + EMSG("TEE_AllocateOperation failed with result 0x%x", res); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + size_t digest_len = 20; + res = TEE_DigestDoFinal(sha1_op, message, message_length, digest, + &digest_len); + if (res != TEE_SUCCESS) { + EMSG("TEE_DigestDoFinal failed with result 0x%x", res); + TEE_FreeOperation(sha1_op); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + TEE_ObjectInfo key_info; + res = TEE_GetObjectInfo1(key->key_handle, &key_info); + if (res != TEE_SUCCESS) { + EMSG("TEE_GetObjectInfo1 failed with error 0x%x", res); + TEE_FreeOperation(sha1_op); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + TEE_FreeOperation(sha1_op); + + TEE_OperationHandle sign_op_handle; + res = TEE_AllocateOperation(&sign_op_handle, + TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA1, + TEE_MODE_SIGN, key_info.keySize); + if (res != TEE_SUCCESS) { + EMSG("TEE_AllocateOperation() failed with result 0x%x", res); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + res = TEE_SetOperationKey(sign_op_handle, key->key_handle); + if (res != TEE_SUCCESS) { + EMSG("TEE_SetOperationKey() failed with result 0x%x", res); + TEE_FreeOperation(sign_op_handle); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // sign the digest. PSS padding is built in by the + // TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA1 selection in the asymmetric key + // handle + res = TEE_AsymmetricSignDigest(sign_op_handle, NULL, 0, digest, 20, + signature, signature_length); + if (res != TEE_SUCCESS) { + EMSG("TEE_AsymmetricSignDigest failed with result 0x%x", res); + TEE_FreeOperation(sign_op_handle); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + TEE_FreeOperation(sign_op_handle); + } else { + // TODO: add support for cast-specific RSA padding? + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_RSADecrypt(WTPI_AsymmetricKey_Handle key, + const uint8_t* in, size_t in_length, + uint8_t* out, size_t* out_length) { + if (key == NULL || in == NULL || in_length == 0 || out == NULL || + out_length == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + size_t private_key_size = key->max_signature_size; + if (out == NULL || *out_length < private_key_size) { + *out_length = private_key_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + TEE_ObjectInfo key_info; + TEE_Result res = TEE_GetObjectInfo1(key->key_handle, &key_info); + if (res != TEE_SUCCESS) { + EMSG("TEE_GetObjectInfo1 failed with error 0x%x", res); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + TEE_OperationHandle decrypt_op_handle; + res = TEE_AllocateOperation(&decrypt_op_handle, + TEE_ALG_RSAES_PKCS1_OAEP_MGF1_SHA1, + TEE_MODE_DECRYPT, key_info.keySize); + if (res != TEE_SUCCESS) { + EMSG("TEE_AllocateOperation() failed with result 0x%x", res); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + res = TEE_SetOperationKey(decrypt_op_handle, key->key_handle); + if (res != TEE_SUCCESS) { + EMSG("TEE_SetOperationKey() failed with result 0x%x", res); + TEE_FreeOperation(decrypt_op_handle); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + res = TEE_AsymmetricDecrypt(decrypt_op_handle, NULL, 0, in, in_length, out, + out_length); + if (res != TEE_SUCCESS) { + EMSG("TEE_AsymmetricDecrypt() failed with result 0x%x", res); + TEE_FreeOperation(decrypt_op_handle); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + TEE_FreeOperation(decrypt_op_handle); + + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_GetSignatureSize(WTPI_AsymmetricKey_Handle key, + size_t* key_size) { + if (key == NULL || key_size == NULL) { + DMSG("Returning result %d", OEMCrypto_ERROR_INVALID_CONTEXT); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + *key_size = key->max_signature_size; + + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_ECCSign(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; + } + + size_t max_signature_size = key->max_signature_size; + if (signature == NULL || *signature_length < max_signature_size) { + *signature_length = max_signature_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + uint32_t sha_algo = 0; + size_t sha_length = SHA512_DIGEST_LENGTH; + uint32_t sign_algo = 0; + switch (key->ecc_curve_type) { + case TEE_ECC_CURVE_NIST_P224: + sha_algo = TEE_ALG_SHA224; + sign_algo = TEE_ALG_ECDSA_P224; + break; + case TEE_ECC_CURVE_NIST_P256: + sha_algo = TEE_ALG_SHA256; + sign_algo = TEE_ALG_ECDSA_P256; + break; + case TEE_ECC_CURVE_NIST_P384: + sha_algo = TEE_ALG_SHA384; + sign_algo = TEE_ALG_ECDSA_P384; + break; + case TEE_ECC_CURVE_NIST_P521: + sha_algo = TEE_ALG_SHA512; + sign_algo = TEE_ALG_ECDSA_P521; + break; + default: + // Other curve types are not supported at this time. + EMSG("WTPI_ECCSign called with unsupported curve type 0x%x", + key->ecc_curve_type); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + // generate SHA digest + uint8_t digest[SHA512_DIGEST_LENGTH]; + TEE_OperationHandle sha_op; + TEE_Result res = TEE_AllocateOperation(&sha_op, sha_algo, TEE_MODE_DIGEST, 0); + if (res != TEE_SUCCESS) { + EMSG("TEE_AllocateOperation failed with result 0x%x", res); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + res = TEE_DigestDoFinal(sha_op, message, message_length, digest, &sha_length); + if (res != TEE_SUCCESS) { + EMSG("TEE_DigestDoFinal failed with result 0x%x", res); + TEE_FreeOperation(sha_op); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + TEE_ObjectInfo key_info; + res = TEE_GetObjectInfo1(key->key_handle, &key_info); + if (res != TEE_SUCCESS) { + EMSG("TEE_GetObjectInfo1 failed with error 0x%x", res); + TEE_FreeOperation(sha_op); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + TEE_FreeOperation(sha_op); + + // Sign digest + TEE_OperationHandle sign_op; + res = TEE_AllocateOperation(&sign_op, sign_algo, TEE_MODE_SIGN, + key_info.keySize); + if (res != TEE_SUCCESS) { + EMSG("TEE_AllocateOperation() failed with result 0x%x", res); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + res = TEE_SetOperationKey(sign_op, key->key_handle); + if (res != TEE_SUCCESS) { + EMSG("TEE_SetOperationKey() failed with result 0x%x", res); + TEE_FreeOperation(sign_op); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + uint8_t raw_sig[ECC_KEY_MAX_BYTES * 2]; + size_t raw_sig_len = ECC_KEY_MAX_BYTES * 2; + res = TEE_AsymmetricSignDigest(sign_op, NULL, 0, digest, sha_length, raw_sig, + &raw_sig_len); + if (res != TEE_SUCCESS) { + EMSG("TEE_AsymmetricSignDigest failed with result 0x%x", res); + TEE_FreeOperation(sign_op); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + TEE_FreeOperation(sign_op); + + // ASN1-encode the signature. + OEMCryptoResult encode_res = + EncodeECDSASignature(raw_sig, raw_sig_len, signature, signature_length); + + return encode_res; +} + +OEMCryptoResult WTPI_ECCDeriveSessionKey(UNUSED WTPI_AsymmetricKey_Handle key, + UNUSED const uint8_t* key_source, + UNUSED size_t key_source_length, + UNUSED uint8_t* session_key, + UNUSED size_t* session_key_length) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult WTPI_ED25519Sign(WTPI_AsymmetricKey_Handle key, + const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult WTPI_GetBootCertificateChain(uint8_t* out, size_t* out_length) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +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) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult WTPI_DeviceKeyCoseSign1(const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} diff --git a/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_decrypt_sample.c b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_decrypt_sample.c new file mode 100644 index 0000000..1d94a12 --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_decrypt_sample.c @@ -0,0 +1,276 @@ +/* + * 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 +#include +#include +#include "malloc.h" +#include "oemcrypto_key_types.h" +#include "oemcrypto_math.h" +#include "oemcrypto_output.h" +#include "oemcrypto_overflow.h" +#include "wtpi_abort_interface.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" +#include "wtpi_decrypt_sample_interface.h" + +OEMCryptoResult Helper_AESEncryptBlock_ECB(WTPI_K1_SymmetricKey_Handle key, + const uint8_t* input, + uint8_t* output); + +static OEMCryptoResult WTPI_DecryptToOutputBuffer_CTR( + WTPI_K1_SymmetricKey_Handle key, const uint8_t* initial_iv, + size_t initial_block_offset, const uint8_t* in, size_t size, + const OPK_OutputBuffer* out, size_t output_offset) { + size_t total_size; + if (OPK_AddOverflowUX(output_offset, size, &total_size)) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + size_t block_offset = initial_block_offset; + if (initial_iv == NULL || block_offset >= AES_BLOCK_SIZE || in == NULL || + size == 0 || out == NULL || total_size > out->size || key == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (out->type == OPK_SECURE_OUTPUT_BUFFER) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } else if (out->type != OPK_CLEAR_INSECURE_OUTPUT_BUFFER) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + uint8_t* const dest = out->buffer.clear_insecure + output_offset; + uint8_t iv[AES_BLOCK_SIZE]; + TEE_MemMove(iv, &initial_iv[0], AES_BLOCK_SIZE); + size_t l = 0; + while (l < size) { + uint8_t aes_output[AES_BLOCK_SIZE]; + Helper_AESEncryptBlock_ECB(key, iv, aes_output); + for (uint8_t n = block_offset; n < AES_BLOCK_SIZE && l < size; ++n, ++l) { + dest[l] = aes_output[n] ^ in[l]; + } + /* Increment the lower 8 bytes of the iv only. */ + for (size_t n = 15; n > 7; --n) { + if (++iv[n] != 0) break; + } + block_offset = 0; + } + return OEMCrypto_SUCCESS; +} + +static OEMCryptoResult WTPI_DecryptToOutputBuffer_CBC( + WTPI_K1_SymmetricKey_Handle key, const uint8_t* initial_iv, + const OEMCrypto_CENCEncryptPatternDesc* pattern, const uint8_t* in, + size_t size, const OPK_OutputBuffer* out, size_t output_offset) { + size_t total_size; + if (OPK_AddOverflowUX(output_offset, size, &total_size)) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (initial_iv == NULL || pattern == NULL || in == NULL || size == 0 || + out == NULL || total_size > out->size || key == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + // TODO: somehow add support for secure buffers + if (out->type == OPK_SECURE_OUTPUT_BUFFER) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } else if (out->type != OPK_CLEAR_INSECURE_OUTPUT_BUFFER) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + uint8_t* const dest = out->buffer.clear_insecure + output_offset; + const size_t pattern_length = pattern->encrypt + pattern->skip; + + uint8_t iv[AES_BLOCK_SIZE]; + uint8_t next_iv[AES_BLOCK_SIZE]; + TEE_MemMove(iv, &initial_iv[0], AES_BLOCK_SIZE); + + size_t offset = 0; + while (offset < size) { + const size_t next_block_size = OPK_MinUX(size - offset, AES_BLOCK_SIZE); + const size_t pattern_offset = + (pattern_length > 0) ? (offset / AES_BLOCK_SIZE) % pattern_length : 0; + const bool is_skip_block = + (pattern_length > 0) && (pattern_offset >= pattern->encrypt); + if (is_skip_block || (next_block_size < AES_BLOCK_SIZE)) { + TEE_MemMove(&dest[offset], &in[offset], next_block_size); + } else { + uint8_t aes_output[AES_BLOCK_SIZE]; + + /* Save the iv for the next block, in case |in| is in the same buffer + as |dest|. */ + TEE_MemMove(next_iv, &in[offset], AES_BLOCK_SIZE); + + // TODO: check key size + + /* NOTE: the next few lines can be used instead of WTPI_AESCBCDecrypt, + * just to show parity with wtpi_test_impl implementation + */ + // OEMCryptoResult result = + // Helper_AESEncryptBlock_ECB(key, &in[offset], aes_output); + // if (result != OEMCrypto_SUCCESS) return result; + // for (size_t n = 0; n < AES_BLOCK_SIZE; n++) { + // dest[offset + n] = aes_output[n] ^ iv[n]; + // } + + OEMCryptoResult result = WTPI_C1_AESCBCDecrypt( + key, KEY_SIZE_128, &in[offset], AES_BLOCK_SIZE, iv, aes_output); + if (result != OEMCrypto_SUCCESS) return result; + TEE_MemMove(&dest[offset], aes_output, AES_BLOCK_SIZE); + + TEE_MemMove(iv, next_iv, AES_BLOCK_SIZE); + } + offset += next_block_size; + } + return OEMCrypto_SUCCESS; +} +/* Advance an IV according to ISO-CENC's CTR modes. The lower half of the IV is + * split off and treated as an unsigned 64-bit integer, then incremented by the + * number of complete crypto blocks decrypted. The resulting value is then + * copied back into the IV over the previous lower half. */ +UBSAN_IGNORE_UNSIGNED_OVERFLOW static void AdvanceIVandCounter( + uint8_t (*subsample_iv)[AES_BLOCK_SIZE], size_t bytes) { + static const size_t kCounterIndex = KEY_IV_SIZE / 2; + static const size_t kCounterSize = KEY_IV_SIZE / 2; + uint64_t counter; + /* Defensive copy because the elements of the array may not be properly + * aligned. */ + TEE_MemMove(&counter, &(*subsample_iv)[kCounterIndex], kCounterSize); + /* The truncation here is intentional. */ + const size_t increment = bytes / AES_BLOCK_SIZE; + /* The potential overflow here is intentional. */ + counter = (counter) + increment; + TEE_MemMove(&(*subsample_iv)[kCounterIndex], &counter, kCounterSize); +} + +static OEMCryptoResult DecryptSubsample( + WTPI_K1_SymmetricKey_Handle key_handle, + const OEMCrypto_SubSampleDescription* subsample, + const OEMCrypto_CENCEncryptPatternDesc* pattern, const uint8_t* cipher_data, + const uint8_t* iv, const OPK_OutputBuffer* output_buffer, + size_t output_offset, OEMCryptoCipherMode cipher_mode) { + if (key_handle == NULL || subsample == NULL || pattern == NULL || + cipher_data == NULL || iv == NULL || output_buffer == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + size_t subsample_length; + if (OPK_AddOverflowUX(subsample->num_bytes_clear, + subsample->num_bytes_encrypted, &subsample_length)) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + OEMCryptoResult result = OEMCrypto_SUCCESS; + /* Handle the clear portion of the subsample. */ + if (subsample->num_bytes_clear > 0) { + result = WTPI_C1_CopyToOutputBuffer(cipher_data, subsample->num_bytes_clear, + output_buffer, output_offset); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to copy clear data to output buffer with result: %u", + result); + return result; + } + } + /* Handle the encrypted portion of the subsample. */ + if (subsample->num_bytes_encrypted > 0) { + const uint8_t* source = cipher_data + subsample->num_bytes_clear; + const size_t source_length = subsample->num_bytes_encrypted; + size_t decrypt_offset; + if (OPK_AddOverflowUX(output_offset, subsample->num_bytes_clear, + &decrypt_offset)) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + if (cipher_mode == OEMCrypto_CipherMode_CBCS) { + if (subsample->block_offset > 0 || pattern->encrypt == 0) { + LOGE( + "Invalid block offset or pattern encrypt for CBC mode, " + "block_offset " + "= %zu, encrypt = %zu", + subsample->block_offset, pattern->encrypt); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + result = WTPI_DecryptToOutputBuffer_CBC(key_handle, iv, pattern, source, + source_length, output_buffer, + decrypt_offset); + } else { + if (pattern->skip != 0 || pattern->encrypt != 0) { + LOGE( + "Invalid pattern skip or encrypt for CTR mode, skip = %zu, encrypt " + "= " + "%zu", + pattern->skip, pattern->encrypt); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + result = WTPI_DecryptToOutputBuffer_CTR( + key_handle, iv, subsample->block_offset, source, source_length, + output_buffer, decrypt_offset); + } + } + return result; +} + +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) { + if (key_handle == NULL || sample == NULL || pattern == NULL || + output_buffer == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + OEMCryptoResult result = OEMCrypto_SUCCESS; + + size_t starting_output_offset = output_offset; + /* Iterate through all the subsamples and decrypt each one. */ + uint8_t subsample_iv[KEY_IV_SIZE]; + ABORT_IF(sizeof(sample->iv) != KEY_IV_SIZE, + "The IV in OEMCrypto_SampleDescription has the wrong length."); + TEE_MemMove(subsample_iv, sample->iv, KEY_IV_SIZE); + size_t offset = 0; + for (size_t subsample_index = 0; subsample_index < sample->subsamples_length; + ++subsample_index) { + const OEMCrypto_SubSampleDescription* subsample = + &(sample->subsamples[subsample_index]); + size_t subsample_length; + if (OPK_AddOverflowUX(subsample->num_bytes_clear, + subsample->num_bytes_encrypted, &subsample_length)) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + size_t current_offset; + if (OPK_AddOverflowUX(starting_output_offset, offset, ¤t_offset)) { + LOGE("Output buffer overflow"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + result = + OPK_CheckOutputBounds(output_buffer, current_offset, subsample_length); + if (result != OEMCrypto_SUCCESS) { + LOGE("Output bounds check failed with result: %u", result); + return result; + } + + const uint8_t* const subsample_source = sample->buffers.input_data + offset; + result = DecryptSubsample(key_handle, subsample, pattern, subsample_source, + subsample_iv, output_buffer, current_offset, + cipher_mode); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failure %u at subsample index %zu", result, subsample_index); + return result; + } + + /* Advance the offset and (if necessary) the IV. */ + if (OPK_AddOverflowUX(offset, subsample_length, &offset)) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (subsample->num_bytes_encrypted > 0 && + cipher_mode == OEMCrypto_CipherMode_CENC) { + size_t distance; + if (OPK_AddOverflowUX(subsample->block_offset, + subsample->num_bytes_encrypted, &distance)) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + AdvanceIVandCounter(&subsample_iv, distance); + } + } /* Subsample loop */ + return result; +} diff --git a/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_initialize_terminate_interface.c b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_initialize_terminate_interface.c new file mode 100644 index 0000000..a83f0ad --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_initialize_terminate_interface.c @@ -0,0 +1,12 @@ +/* + * 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_initialize_terminate_interface.h" +#include "tee_internal_api.h" + +OEMCryptoResult WTPI_Initialize(void) { return OEMCrypto_SUCCESS; } + +OEMCryptoResult WTPI_Terminate(void) { return OEMCrypto_SUCCESS; } diff --git a/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_logging.c b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_logging.c new file mode 100644 index 0000000..66cc641 --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_logging.c @@ -0,0 +1,19 @@ +/* + * 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 "ta_log.h" +#include "wtpi_logging_interface.h" + +#include +#include + +void WTPI_Log(const char* file, const char* function, int line, + LogPriority level, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + vlog(file, function, line, level, fmt, args); + va_end(args); +} diff --git a/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_persistent_storage_layer2.c b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_persistent_storage_layer2.c new file mode 100644 index 0000000..e5fc7a7 --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_persistent_storage_layer2.c @@ -0,0 +1,145 @@ +/* + * 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 +#include "wtpi_persistent_storage.h" + +static const char kDataId[] = "GenerationNumPersistentData"; +static const size_t kDataIdLength = sizeof(kDataId); + +static TEE_Result write_raw_object(const char* obj_id, size_t obj_id_length, + const uint8_t* in_data, + size_t in_data_length) { + TEE_ObjectHandle object; + TEE_Result res; + char* data; + uint32_t obj_data_flag; + + data = TEE_Malloc(in_data_length, 0); + if (!data) return TEE_ERROR_OUT_OF_MEMORY; + TEE_MemMove(data, in_data, in_data_length); + + /* + * Create object in secure storage and fill with data + */ + obj_data_flag = + TEE_DATA_FLAG_ACCESS_READ | /* we can later read the oject */ + TEE_DATA_FLAG_ACCESS_WRITE | /* we can later write into the object */ + TEE_DATA_FLAG_ACCESS_WRITE_META | /* we can later destroy or rename the + object */ + TEE_DATA_FLAG_OVERWRITE; /* destroy existing object of same ID */ + + res = TEE_CreatePersistentObject(TEE_STORAGE_PRIVATE, obj_id, obj_id_length, + obj_data_flag, TEE_HANDLE_NULL, NULL, + 0, /* we may not fill it right now */ + &object); + if (res != TEE_SUCCESS) { + EMSG("TEE_CreatePersistentObject failed 0x%08x", res); + TEE_Free(data); + return res; + } + + res = TEE_WriteObjectData(object, data, in_data_length); + if (res != TEE_SUCCESS) { + EMSG("TEE_WriteObjectData failed 0x%08x", res); + TEE_CloseAndDeletePersistentObject1(object); + } else { + TEE_CloseObject(object); + } + + TEE_Free(data); + return res; +} + +static TEE_Result read_raw_object(const char* obj_id, size_t obj_id_length, + uint8_t* out, size_t out_length, + size_t* bytes_read, size_t* expected_size) { + TEE_ObjectHandle object; + TEE_ObjectInfo object_info; + TEE_Result res; + + uint8_t* data = TEE_Malloc(out_length, 0); + if (!data) return TEE_ERROR_OUT_OF_MEMORY; + + res = TEE_OpenPersistentObject( + TEE_STORAGE_PRIVATE, obj_id, obj_id_length, + TEE_DATA_FLAG_ACCESS_READ | TEE_DATA_FLAG_SHARE_READ, &object); + if (res != TEE_SUCCESS) { + EMSG("Failed to open persistent object, res=0x%08x", res); + TEE_Free(data); + return res; + } + + res = TEE_GetObjectInfo1(object, &object_info); + if (res != TEE_SUCCESS) { + EMSG("Failed to create persistent object, res=0x%08x", res); + goto exit; + } + + if (object_info.dataSize > out_length) { + *expected_size = object_info.dataSize; + res = TEE_ERROR_SHORT_BUFFER; + goto exit; + } + + size_t read_bytes = 0; + res = TEE_ReadObjectData(object, data, object_info.dataSize, &read_bytes); + if (res == TEE_SUCCESS) TEE_MemMove(out, data, read_bytes); + if (res != TEE_SUCCESS || read_bytes != object_info.dataSize) { + EMSG("TEE_ReadObjectData failed 0x%08x, read %" PRIu32 " over %u", res, + read_bytes, object_info.dataSize); + goto exit; + } + + *bytes_read = read_bytes; + +exit: + TEE_CloseObject(object); + TEE_Free(data); + return res; +} + +/****************************************************************************** + * WTPI funcs + ******************************************************************************/ +OEMCryptoResult WTPI_PrepareStoredPersistentData(void) { + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_LoadPersistentData(uint8_t* data, size_t* data_length) { + TEE_Result res; + size_t bytes_read; + size_t short_buf_expected_size; + + res = read_raw_object(kDataId, kDataIdLength, data, *data_length, &bytes_read, + &short_buf_expected_size); + + *data_length = bytes_read; + + switch (res) { + case TEE_SUCCESS: + break; + case TEE_ERROR_SHORT_BUFFER: + return OEMCrypto_ERROR_SHORT_BUFFER; + case TEE_ERROR_ITEM_NOT_FOUND: + return OPK_ERROR_NO_PERSISTENT_DATA; + default: + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_StorePersistentData(const uint8_t* data, + size_t data_length) { + TEE_Result res; + + res = write_raw_object(kDataId, kDataIdLength, data, data_length); + if (res != TEE_SUCCESS) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + return OEMCrypto_SUCCESS; +} diff --git a/oemcrypto/opk/ports/optee/ta/oemcrypto_ta/README.md b/oemcrypto/opk/ports/optee/ta/oemcrypto_ta/README.md new file mode 100644 index 0000000..3638c90 --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/oemcrypto_ta/README.md @@ -0,0 +1,25 @@ +This directory contains the makefile and top-level entry points for the +OEMCrypto Trusted App for OP-TEE. + +When the TA is initialized by the REE-side host app, TA_CreateEntryPoint() is +called, which subsequently calls OPK_Initialize(). This sets up shared buffers +for messages to pass through. + +Every time the REE-side app calls an OEMCrypto function, the serialization code +in the REE will convert that function and its arguments into a serialized byte +stream that is packed into a shared buffer. Then TEEC_InvokeCommand() is called +to pass along this shared buffer, which causes the TEE-side +TA_InvokeCommandEntryPoint() function to execute. This will extract the function +and argument data from the shared buffer, then execute that function in the TEE. +The results are passed back through the shared buffer to the REE. + +The WTPI functions are called by the OEMCrypto TEE-side code when something +platform-specific needs to happen. In the case of OP-TEE, all of the crypto, +persistent data, and clock functions need to be written using the GlobalPlatform +API. + +Caveats +- Currently the OEMCrypto TA uses a fake keybox and device key. These cannot be + used in production. +- Execution is very slow on QEMU as well as on STM32MP1 development kits. +- There is no secure buffer support yet. diff --git a/oemcrypto/opk/ports/optee/ta/oemcrypto_ta/include/oemcrypto_ta.h b/oemcrypto/opk/ports/optee/ta/oemcrypto_ta/include/oemcrypto_ta.h new file mode 100644 index 0000000..f356958 --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/oemcrypto_ta/include/oemcrypto_ta.h @@ -0,0 +1,24 @@ +/* + * 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 TA_OPTEE_OEMCRYPTO_H +#define TA_OPTEE_OEMCRYPTO_H + +/* + * This UUID is generated with uuidgen + * the ITU-T UUID generator at http://www.itu.int/ITU-T/asn1/uuid.html + */ +#define TA_OPTEE_OEMCRYPTO_UUID \ + { \ + 0xa92d116c, 0xce27, 0x4917, { \ + 0xb3, 0x0c, 0x4a, 0x41, 0x6e, 0x2d, 0x93, 0x51 \ + } \ + } + +/* The function IDs implemented in this TA */ +#define TA_SEND_REQUEST_CMD 0 + +#endif /* TA_OPTEE_OEMCRYPTO_H */ diff --git a/oemcrypto/opk/ports/optee/ta/oemcrypto_ta/include/user_ta_header_defines.h b/oemcrypto/opk/ports/optee/ta/oemcrypto_ta/include/user_ta_header_defines.h new file mode 100644 index 0000000..03a9841 --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/oemcrypto_ta/include/user_ta_header_defines.h @@ -0,0 +1,40 @@ +/* + * Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine + * License Agreement. + */ + +/* + * The name of this file must not be modified + */ + +#ifndef USER_TA_HEADER_DEFINES_H +#define USER_TA_HEADER_DEFINES_H + +/* To get the TA UUID definition */ +#include "oemcrypto_ta.h" + +#define TA_UUID TA_OPTEE_OEMCRYPTO_UUID + +/* + * TA properties: multi-instance TA, no specific attribute + * TA_FLAG_EXEC_DDR is meaningless but mandated. + */ +#define TA_FLAGS TA_FLAG_EXEC_DDR + +/* Provisioned stack size */ +#define TA_STACK_SIZE (16 * 1024) + +/* Provisioned heap size for TEE_Malloc() and friends */ +#define TA_DATA_SIZE (64 * 1024) + +/* The gpd.ta.version property */ +#define TA_VERSION "1.0" + +/* The gpd.ta.description property */ +#define TA_DESCRIPTION "Widevine OEMCrypto OP-TEE Trusted Application" + +/* Extra properties */ +#define TA_CURRENT_TA_EXT_PROPERTIES + +#endif /* USER_TA_HEADER_DEFINES_H */ diff --git a/oemcrypto/opk/ports/optee/ta/oemcrypto_ta/oemcrypto_ta.c b/oemcrypto/opk/ports/optee/ta/oemcrypto_ta/oemcrypto_ta.c new file mode 100644 index 0000000..b442337 --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/oemcrypto_ta/oemcrypto_ta.c @@ -0,0 +1,183 @@ +/* + * 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 +#include +#include + +/* oemcrypto_ta TEE interface */ +#include "ta_log.h" + +#include "odk_message.h" +#include "oemcrypto_ta.h" +#include "opk_dispatcher.h" +#include "opk_init.h" +#include "tos_logging_interface.h" +#include "tos_transport_interface.h" + +#define UNUSED(x) (void)(x) + +static void* shared_memory_address_; +static size_t shared_memory_size_; +static void* transport_buffer_; + +/* + * Called when the instance of the TA is created. This is the first + * call in the TA. + */ +TEE_Result TA_CreateEntryPoint(void) { + if (OPK_Initialize()) { + return TEE_SUCCESS; + } else { + return TEE_ERROR_BAD_STATE; + } +} + +/* + * Called when the instance of the TA is destroyed if the TA has not + * crashed or panicked. This is the last call in the TA. + */ +void TA_DestroyEntryPoint(void) { OPK_Terminate(); } + +/* + * Called when a new session is opened to the TA. *session_context_ can be + * updated with a value to be able to identify this session in + * subsequent calls to the TA. In this function you will normally do + * the global initialization for the TA. + */ +TEE_Result TA_OpenSessionEntryPoint(uint32_t param_types, + TEE_Param __maybe_unused params[4], + void __maybe_unused** session_context) { + UNUSED(params); + UNUSED(session_context); + uint32_t expected_param_types = + TEE_PARAM_TYPES(TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE); + if (param_types != expected_param_types) { + DMSG("Bad params in OpenSessionEntryPoint\n"); + return TEE_ERROR_BAD_PARAMETERS; + } + return TEE_SUCCESS; +} + +/* + * Called when a session is closed, session_context_ holds the value + * that was assigned by TA_OpenSessionEntryPoint(). + */ +void TA_CloseSessionEntryPoint(void __maybe_unused* session_context) { + UNUSED(session_context); + IMSG("TA_CloseSessionEntryPoint\n"); +} + +/* + * params[] contains the parameters in the received command. The first + * parameter is the opk_shared_memory block and the second is the + * transport_shared_memory which contains the request message data. + * + * Initialize a request message from the recieved parameters, pass + * the request to DispatchMessage then use the response message to + * fill out the parameters that will be sent back to the REE. + */ +static TEE_Result HandleRequest(uint32_t param_types, TEE_Param params[4]) { + uint32_t expected_param_types = + TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INOUT, TEE_PARAM_TYPE_MEMREF_INOUT, + TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE); + + if (param_types != expected_param_types) { + DMSG("Bad parameters\n"); + return TEE_ERROR_BAD_PARAMETERS; + } + + shared_memory_address_ = params[0].memref.buffer; + shared_memory_size_ = params[0].memref.size; + transport_buffer_ = params[1].memref.buffer; + if (shared_memory_address_ == NULL || transport_buffer_ == NULL) { + DMSG("NULL address\n"); + return TEE_ERROR_BAD_PARAMETERS; + } + + size_t request_size = params[1].memref.size; + if (request_size > OPK_TRANSPORT_MESSAGE_SIZE) { + DMSG("Size too large. Input is %d, limit is %d\n", request_size, + OPK_TRANSPORT_MESSAGE_SIZE); + return TEE_ERROR_BAD_PARAMETERS; + } + + /* + * The request message data must be copied into a local buffer + * so the contents can't be modified while being parsed. + */ + static uint8_t local_buffer[OPK_TRANSPORT_MESSAGE_SIZE]; + memcpy(local_buffer, transport_buffer_, request_size); + + ODK_Message request = ODK_Message_Create(local_buffer, request_size); + ODK_Message_SetSize(&request, request_size); + ODK_Message response; + ODK_MessageStatus status = OPK_DispatchMessage(&request, &response); + if (status != MESSAGE_STATUS_OK) { + DMSG("OPK_DispatchMessage returned %d", status); + return TEE_ERROR_BAD_FORMAT; + } + + size_t* response_size_param = ¶ms[1].memref.size; + *response_size_param = ODK_Message_GetSize(&response); + + return TEE_SUCCESS; +} + +/* + * Called when a TA is invoked. session_context_ hold that value that was + * assigned by TA_OpenSessionEntryPoint(). The rest of the paramters + * comes from normal world. + */ +TEE_Result TA_InvokeCommandEntryPoint(void __maybe_unused* session_context_, + uint32_t cmd_id, uint32_t param_types, + TEE_Param params[4]) { + (void)&session_context_; /* Unused parameter */ + + switch (cmd_id) { + case TA_SEND_REQUEST_CMD: + return HandleRequest(param_types, params); + default: + DMSG("Bad params in InvokeCommandEntryPoint\n"); + return TEE_ERROR_BAD_PARAMETERS; + } +} + +/******************************************************************* + * See "tos_transport_interface.h" for documentation of each of the + * TOS_ interface functions below. + *******************************************************************/ + +bool TOS_Transport_Initialize(void) { return true; } + +void TOS_Transport_Terminate(void) {} + +ODK_Message TOS_Transport_GetRequest(void) { + if (!transport_buffer_) { + return ODK_Message_Create(NULL, 0); + } else { + return ODK_Message_Create(transport_buffer_, OPK_TRANSPORT_MESSAGE_SIZE); + } +} + +ODK_Message TOS_Transport_GetResponse(void) { + /* Use the same shared buffer for request & response */ + return TOS_Transport_GetRequest(); +} + +void TOS_Transport_ReleaseMessage(ODK_Message* message) { + UNUSED(message); + /* resources are static, nothing to do here */ +} + +/******************************************************************* + * See "tos_shared_memory_interface.h" for documentation + * of each of the TOS_SharedMemory interface functions below. + *******************************************************************/ + +uint8_t* TOS_SharedMemory_GetAddress(void) { return shared_memory_address_; } + +size_t TOS_SharedMemory_GetSize(void) { return shared_memory_size_; } diff --git a/oemcrypto/opk/ports/optee/ta/wtpi_test_ta/README.md b/oemcrypto/opk/ports/optee/ta/wtpi_test_ta/README.md new file mode 100644 index 0000000..b3f8e5f --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/wtpi_test_ta/README.md @@ -0,0 +1,20 @@ +This directory contains the makefile and top-level entry points for the WTPI +test Trusted App for OP-TEE. + +While the OEMCrypto unit tests exercise the OEMCrypto functions in realistic +scenarios, they do not provide adequate feedback for implementations of the +underlying WTPI functions. This project aims to simplify the development of WTPI +implementations by providing clear unit tests. + +Since WTPI functions are called by OEMCrypto in a trusted environment, the unit +tests must do the same. Here, the host app runs Google Test and calls the WTPI +functions from the REE. The WTPI functions are serialized and sent to the TEE +through a shared buffer, causing TA_InvokeCommandEntryPoint() to run. The +WTPI function is deserialized and executed in the TEE, and the results go back +to the REE through the shared buffer. + +This is very similar to how the OEMCrypto TA works, only with different +serialization code. + +The implementations of the WTPI functions are the same for this TA and for the +OEMCrypto TA. diff --git a/oemcrypto/opk/ports/optee/ta/wtpi_test_ta/include/user_ta_header_defines.h b/oemcrypto/opk/ports/optee/ta/wtpi_test_ta/include/user_ta_header_defines.h new file mode 100644 index 0000000..99af759 --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/wtpi_test_ta/include/user_ta_header_defines.h @@ -0,0 +1,40 @@ +/* + * Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine + * License Agreement. + */ + +/* + * The name of this file must not be modified + */ + +#ifndef USER_TA_HEADER_DEFINES_H +#define USER_TA_HEADER_DEFINES_H + +/* To get the TA UUID definition */ +#include "wtpi_test_ta.h" + +#define TA_UUID TA_OPTEE_WTPI_TEST_UUID + +/* + * TA properties: multi-instance TA, no specific attribute + * TA_FLAG_EXEC_DDR is meaningless but mandated. + */ +#define TA_FLAGS TA_FLAG_EXEC_DDR + +/* Provisioned stack size */ +#define TA_STACK_SIZE (16 * 1024) + +/* Provisioned heap size for TEE_Malloc() and friends */ +#define TA_DATA_SIZE (64 * 1024) + +/* The gpd.ta.version property */ +#define TA_VERSION "1.0" + +/* The gpd.ta.description property */ +#define TA_DESCRIPTION "Widevine WTPI Test OP-TEE Trusted Application" + +/* Extra properties */ +#define TA_CURRENT_TA_EXT_PROPERTIES + +#endif /* USER_TA_HEADER_DEFINES_H */ diff --git a/oemcrypto/opk/ports/optee/ta/wtpi_test_ta/include/wtpi_test_ta.h b/oemcrypto/opk/ports/optee/ta/wtpi_test_ta/include/wtpi_test_ta.h new file mode 100644 index 0000000..9e89aa7 --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/wtpi_test_ta/include/wtpi_test_ta.h @@ -0,0 +1,24 @@ +/* + * 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 TA_OPTEE_WTPI_TEST_H +#define TA_OPTEE_WTPI_TEST_H + +/* + * This UUID is generated with uuidgen + * the ITU-T UUID generator at http://www.itu.int/ITU-T/asn1/uuid.html + */ +#define TA_OPTEE_WTPI_TEST_UUID \ + { \ + 0xb0f42504, 0x01ec, 0x11ec, { \ + 0x9a, 0x03, 0x02, 0x42, 0xac, 0x13, 0x00, 0x03 \ + } \ + } + +/* The function IDs implemented in this TA */ +#define TA_SEND_REQUEST_CMD 0 + +#endif /* TA_OPTEE_WTPI_TEST_H */ diff --git a/oemcrypto/opk/ports/optee/ta/wtpi_test_ta/wtpi_test_ta.c b/oemcrypto/opk/ports/optee/ta/wtpi_test_ta/wtpi_test_ta.c new file mode 100644 index 0000000..c13a98f --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/wtpi_test_ta/wtpi_test_ta.c @@ -0,0 +1,177 @@ +/* + * 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_test_ta.h" + +#include +#include +#include + +/* oemcrypto_ta TEE interface */ + +#include "odk_message.h" +#include "opk_dispatcher.h" +#include "opk_init.h" +#include "tos_logging_interface.h" +#include "tos_transport_interface.h" + +#define UNUSED(x) (void)(x) + +static void* shared_memory_address_; +static size_t shared_memory_size_; +static void* transport_buffer_; +/* + * Called when the instance of the TA is created. This is the first + * call in the TA. + */ +TEE_Result TA_CreateEntryPoint(void) { return TEE_SUCCESS; } + +/* + * Called when the instance of the TA is destroyed if the TA has not + * crashed or panicked. This is the last call in the TA. + */ +void TA_DestroyEntryPoint(void) {} + +/* + * Called when a new session is opened to the TA. *session_context_ can be + * updated with a value to be able to identify this session in + * subsequent calls to the TA. In this function you will normally do + * the global initialization for the TA. + */ +TEE_Result TA_OpenSessionEntryPoint(uint32_t param_types, + TEE_Param __maybe_unused params[4], + void __maybe_unused** session_context) { + UNUSED(params); + UNUSED(session_context); + uint32_t expected_param_types = + TEE_PARAM_TYPES(TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE); + if (param_types != expected_param_types) { + DMSG("Bad params in OpenSessionEntryPoint\n"); + return TEE_ERROR_BAD_PARAMETERS; + } + return TEE_SUCCESS; +} + +/* + * Called when a session is closed, session_context_ holds the value + * that was assigned by TA_OpenSessionEntryPoint(). + */ +void TA_CloseSessionEntryPoint(void __maybe_unused* session_context) { + UNUSED(session_context); + IMSG("TA_CloseSessionEntryPoint\n"); +} + +/* + * params[] contains the parameters in the received command. The first + * parameter is the opk_shared_memory block and the second is the + * transport_shared_memory which contains the request message data. + * + * Initialize a request message from the recieved parameters, pass + * the request to DispatchMessage then use the response message to + * fill out the parameters that will be sent back to the REE. + */ +static TEE_Result HandleRequest(uint32_t param_types, TEE_Param params[4]) { + uint32_t expected_param_types = + TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INOUT, TEE_PARAM_TYPE_MEMREF_INOUT, + TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE); + + if (param_types != expected_param_types) { + DMSG("Bad parameters\n"); + return TEE_ERROR_BAD_PARAMETERS; + } + + shared_memory_address_ = params[0].memref.buffer; + shared_memory_size_ = params[0].memref.size; + transport_buffer_ = params[1].memref.buffer; + if (shared_memory_address_ == NULL || transport_buffer_ == NULL) { + DMSG("NULL address\n"); + return TEE_ERROR_BAD_PARAMETERS; + } + + size_t request_size = params[1].memref.size; + if (request_size > OPK_TRANSPORT_MESSAGE_SIZE) { + DMSG("Size too large. Input is %d, limit is %d\n", request_size, + OPK_TRANSPORT_MESSAGE_SIZE); + return TEE_ERROR_BAD_PARAMETERS; + } + + /* + * The request message data must be copied into a local buffer + * so the contents can't be modified while being parsed. + */ + static uint8_t local_buffer[OPK_TRANSPORT_MESSAGE_SIZE]; + memcpy(local_buffer, transport_buffer_, request_size); + + DMSG("Request size is %d\n", request_size); + ODK_Message request = ODK_Message_Create(local_buffer, request_size); + ODK_Message_SetSize(&request, request_size); + ODK_Message response; + ODK_MessageStatus status = OPK_DispatchMessage(&request, &response); + if (status != MESSAGE_STATUS_OK) { + DMSG("OPK_DispatchMessage returned %d", status); + return TEE_ERROR_BAD_FORMAT; + } else { + size_t* response_size_param = ¶ms[1].memref.size; + *response_size_param = ODK_Message_GetSize(&response); + } + return TEE_SUCCESS; +} + +/* + * Called when a TA is invoked. session_context_ hold that value that was + * assigned by TA_OpenSessionEntryPoint(). The rest of the paramters + * comes from normal world. + */ +TEE_Result TA_InvokeCommandEntryPoint(void __maybe_unused* session_context_, + uint32_t cmd_id, uint32_t param_types, + TEE_Param params[4]) { + (void)&session_context_; /* Unused parameter */ + + switch (cmd_id) { + case TA_SEND_REQUEST_CMD: + return HandleRequest(param_types, params); + default: + DMSG("Bad params in InvokeCommandEntryPoint\n"); + return TEE_ERROR_BAD_PARAMETERS; + } +} + +/******************************************************************* + * See "tos_transport_interface.h" for documentation of each of the + * TOS_ interface functions below. + *******************************************************************/ + +bool TOS_Transport_Initialize(void) { return true; } + +void TOS_Transport_Terminate(void) {} + +ODK_Message TOS_Transport_GetRequest(void) { + if (!transport_buffer_) { + return ODK_Message_Create(NULL, 0); + } else { + return ODK_Message_Create(transport_buffer_, OPK_TRANSPORT_MESSAGE_SIZE); + } +} + +ODK_Message TOS_Transport_GetResponse(void) { + /* Use the same shared buffer for request & response */ + return TOS_Transport_GetRequest(); +} + +void TOS_Transport_ReleaseMessage(ODK_Message* message) { + UNUSED(message); + /* resources are static, nothing to do here */ +} + +/******************************************************************* + * See "tos_shared_memory_interface.h" for documentation + * of each of the TOS_SharedMemory interface functions below. + *******************************************************************/ + +uint8_t* TOS_SharedMemory_GetAddress(void) { return shared_memory_address_; } + +size_t TOS_SharedMemory_GetSize(void) { return shared_memory_size_; } diff --git a/oemcrypto/opk/ports/trusty/ta/README.md b/oemcrypto/opk/ports/trusty/ta/README.md new file mode 100644 index 0000000..c26280d --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/README.md @@ -0,0 +1,61 @@ +# Widevine TA for Trusty + +This directory contains the OEMCrypto TA code for Trusty, specifically the +Trusty-specific portions such as the transport layer, secure buffer, and some +WTPI implementations. This also includes the build files that are used by the +Trusty build system to build the TA and bundle it in the lk.bin image. This code +is currently used on production devices. + +This folder also provides code and an Android.mk file for the non-secure +liboemcrypto.so. This is built with the Android NDK. + +## Quick start + +The TA must be built with the Trusty build system, which compiles the entire +Trusty OS into a single binary image and packages all TAs and dependent modules +within. In order to build a single TA such as OEMCrypto, the entire Trusty +system must be built. This will be improved in the future to build just the +TA and sideload it into an existing Trusty build, but for now everything must be +built together. + +The output of this build process is a Trusty image `lk.bin` located under +`$TRUSTY_DIR/build-root/qemu-generic-arm64-test-debug` containing the OEMCrypto +TA. Within this directory, liboemcrypto.so is under the `lib/arm64-v8`. If +desired, the individual OEMCrypto TA ELF file can be found relative to the +`user_tasks` folder, depending on the location of the Trusty codebase relative +to the CDM folder. For example, if the CDM repo can be reached from Trusty by +going up two directories, then the OEMCrypto TA build output will be two +directories up from the `user_tasks` folder. + +The project name can be changed to other supported formats, provided that the +OEMCrypto TA path is added to the appropriate makefile. Type `make list` at the +top of the Trusty codebase to see a list of supported project names. + +```bash +# Download AOSP Trusty +mkdir -p $TRUSTY_DIR && cd $TRUSTY_DIR +repo init -u https://android.googlesource.com/trusty/manifest -b master +repo sync -c -j32 + +# Download an Android NDK prebuilt to compile liboemcrypto.so +cd $TRUSTY_DIR/prebuilts +git clone https://android.googlesource.com/toolchain/prebuilts/ndk/r21 ndk + +# Add the OEMCrypto TA path as a user module. The OEMCRYPTO_TA_DIR must expand +# to a path that is relative to the Trusty directory, eg +# "../../cdm/oemcrypto/opk/ports/trusty/ta" +cd $TRUSTY_DIR/trusty/device/arm/generic-arm64/project +sed -i "/TRUSTY_BUILTIN_USER_TASKS/a ${OEMCRYPTO_TA_DIR} \\\\" generic-arm-inc.mk + +# Add a required build variable +echo "WIDEVINE_PROVISION_METHOD := 2" >> generic-arm-inc.mk + +# Run the build script. `make $TRUSTY_PROJECT` from the top level is also +# sufficient +TRUSTY_PROJECT=qemu-generic-arm64-test-debug +$TRUSTY_DIR/trusty/vendor/google/aosp/scripts/build.py $TRUSTY_PROJECT --skip-tests +``` + +Currently the best way to exercise the TA is to run it against an Android app +that uses liboemcrypto.so. Future updates will include WTPI unit tests and +support for running those tests on Trusty in QEMU. diff --git a/oemcrypto/opk/ports/trusty/ta/include/system_specific.h b/oemcrypto/opk/ports/trusty/ta/include/system_specific.h new file mode 100644 index 0000000..d45f235 --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/include/system_specific.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +/* + * Does this IPC buffer have the security properties we expect? + * The exact details are system-specific, but in general this function should + * check that the buffer is non-secure memory that the HAL can write to. We + * don't depend on the integrity of IPC buffers for security, but we want to + * make sure the client is not trying to use this TA to extract information from + * a buffer it should not have access to. + * + * Returns 0 on success, and a negative number on failiure. + */ +int system_specific_validate_ipc_buffer(int handle); + +/* + * Is it safe to use this buffer as a secure output buffer? + * The exact details are system-specific, but in general this function should + * check the memory is protected from the non-secure world, readable by the + * video decoder, and generally not readable by other hardware. + * + * Returns 0 on success, and a negative number on failiure. + */ +int system_specific_validate_secure_output_buffer(int handle); diff --git a/oemcrypto/opk/ports/trusty/ta/include/tee_context.h b/oemcrypto/opk/ports/trusty/ta/include/tee_context.h new file mode 100644 index 0000000..fcb615b --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/include/tee_context.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +typedef struct tee_context { + storage_session_t user_data_session; + storage_session_t provisioning_session; +} tee_context; + +tee_context* get_tee_context(); diff --git a/oemcrypto/opk/ports/trusty/ta/interface_impls/device_key_v0.c b/oemcrypto/opk/ports/trusty/ta/interface_impls/device_key_v0.c new file mode 100644 index 0000000..5e4bc99 --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/interface_impls/device_key_v0.c @@ -0,0 +1,203 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define TLOG_TAG "DEVICEKEY_V0" + +#include "device_key_v0_interface.h" + +#include +#include +#include +#include +#include +#include + +#include "wtpi_crypto_and_key_management_interface_layer1.h" +#include "wtpi_device_key_interface.h" + +/** Legacy context value used for signing the private key in the DRM + * certificate. This should be used as a key derivation context in + * DeriveDeviceKeyIntoHandle_V0() for unwrapping the DRM key in the old format. + * Do not change. + */ +#define DEVICE_KEY_SIGN_DRM_CERT 0x85a65596 + +#define MAX_KEY_SIZE 32 + +static const uint8_t DERIVATION_DATA[MAX_KEY_SIZE + 1] = + "....TrustyWidevineDerivationData"; + +/* Structure for holding a wrapped RSA key in old format used on Pixel 6. It + contains the encrypted RSA key plus a header containing the non-secret info + needed to decrypt it. The size of the key is not known at compile-time, so it + is stored in a Flexible Array Member. Note that the code that uses this + struct relies on the signature being the first field when calculating the + signature. */ +typedef struct WrappedRSAKey_V0 { + uint8_t signature[MAC_KEY_SIZE]; + uint8_t iv[KEY_IV_SIZE]; + uint8_t enc_rsa_key[]; +} WrappedRSAKey_V0; + +static void generate_derivation_data(uint32_t context, + uint8_t* data, + size_t data_len) { + assert(data != NULL); + + /* + * The varying data must be included in the derivation data. + * Unfortunately the low-level key derivation interface requires the + * derivation data to be equal to the output key size, and we can generate + * two different key sizes. + * It's OK to truncate the constant part on smaller keys. + */ + assert(data_len > 4); + + /* Initialize with a constant payload. */ + memcpy(data, DERIVATION_DATA, data_len); + + /* Set the first four bytes to the specific use for this key. */ + memcpy(data, &context, sizeof(context)); +} + +static long derive_key(uint8_t* derivation_data, + uint8_t* key, + size_t key_size) { + long rc = hwkey_open(); + if (rc < 0) { + return rc; + } + hwkey_session_t session = (hwkey_session_t)rc; + uint32_t kdf_version = HWKEY_KDF_VERSION_1; + rc = hwkey_derive(session, &kdf_version, derivation_data, key, key_size); + hwkey_close(session); + return rc; +} + +static OEMCryptoResult DeriveDeviceKeyIntoHandle_V0( + uint32_t context, + SymmetricKeyType out_key_type, + WTPI_K1_SymmetricKey_Handle* out_key_handle, + KeySize out_key_size) { + assert(out_key_handle != NULL); + if (out_key_size > MAX_KEY_SIZE) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + uint8_t derivation_data[MAX_KEY_SIZE]; + generate_derivation_data(context, derivation_data, out_key_size); + + uint8_t key_data[MAX_KEY_SIZE]; + if (derive_key(derivation_data, key_data, out_key_size) < 0) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + OEMCryptoResult result = WTPI_K1_CreateKeyHandle( + key_data, (size_t)out_key_size, out_key_type, out_key_handle); + + /* Wipe the key off the stack. */ + memset(key_data, 0, sizeof(key_data)); + + return result; +} + +OEMCryptoResult VerifyAndDecryptDRMKeyData_V0(const uint8_t* wrapped, + size_t wrapped_size, + uint8_t* out) { + if (wrapped == NULL || wrapped_size == 0 || out == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (wrapped_size < sizeof(WrappedRSAKey_V0)) { + TLOGE("wrapped rsa key length is too short."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + const size_t enc_rsa_key_length = wrapped_size - sizeof(WrappedRSAKey_V0); + if (enc_rsa_key_length > PKCS8_RSA_KEY_MAX_SIZE) { + TLOGE("wrapped rsa key length is too long."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + const WrappedRSAKey_V0* wrapped_key = (const WrappedRSAKey_V0*)(wrapped); + WTPI_K1_SymmetricKey_Handle signing_key = NULL; + OEMCryptoResult result = DeriveDeviceKeyIntoHandle_V0( + DEVICE_KEY_SIGN_DRM_CERT, MAC_KEY_SERVER, &signing_key, + KEY_SIZE_256); + if (result != OEMCrypto_SUCCESS) { + WTPI_K1_FreeKeyHandle(signing_key); + return result; + } + const size_t offset = sizeof(wrapped_key->signature); + result = WTPI_C1_HMAC_SHA256_Verify(signing_key, wrapped + offset, + wrapped_size - offset, + wrapped_key->signature); + WTPI_K1_FreeKeyHandle(signing_key); + if (result != OEMCrypto_SUCCESS) + return result; + WTPI_K1_SymmetricKey_Handle encryption_key = NULL; + result = DeriveDeviceKeyIntoHandle_V0(DEVICE_KEY_WRAP_DRM_CERT, + ENCRYPTION_KEY, &encryption_key, + KEY_SIZE_128); + if (result != OEMCrypto_SUCCESS) { + WTPI_K1_FreeKeyHandle(encryption_key); + return result; + } + result = WTPI_C1_AESCBCDecrypt(encryption_key, KEY_SIZE_128, + wrapped_key->enc_rsa_key, enc_rsa_key_length, + wrapped_key->iv, out); + WTPI_K1_FreeKeyHandle(encryption_key); + return result; +} + +OEMCryptoResult VerifyAndDecryptUsageData_V0(const uint8_t* wrapped, + size_t wrapped_size, + const uint8_t* signature, + const uint8_t* iv, + uint8_t* out) { + if (wrapped == NULL || wrapped_size == 0 || signature == NULL || + iv == NULL || out == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + WTPI_K1_SymmetricKey_Handle signing_key = NULL; + /* Ideally the context for deriving signing_key below should be + * DEVICE_KEY_SIGN_USAGE_TABLE but not DEVICE_KEY_WRAP_USAGE_TABLE. However, + * the latter was used in the legacy code to sign DRM key, which is already + * in production. This could be a bug we overlooked when the code was + * merged. To work with the existing wrapped key on these devices, we have + * to stick to DEVICE_KEY_WRAP_USAGE_TABLE. */ + OEMCryptoResult result = DeriveDeviceKeyIntoHandle_V0( + DEVICE_KEY_WRAP_USAGE_TABLE, MAC_KEY_SERVER, &signing_key, + KEY_SIZE_256); + if (result != OEMCrypto_SUCCESS) { + TLOGE("Failed to derive signing key with result: %u", result); + WTPI_K1_FreeKeyHandle(signing_key); + return result; + } + result = WTPI_C1_HMAC_SHA256_Verify(signing_key, wrapped, wrapped_size, + signature); + WTPI_K1_FreeKeyHandle(signing_key); + if (result != OEMCrypto_SUCCESS) + return result; + WTPI_K1_SymmetricKey_Handle encryption_key = NULL; + result = DeriveDeviceKeyIntoHandle_V0(DEVICE_KEY_WRAP_USAGE_TABLE, + ENCRYPTION_KEY, &encryption_key, + KEY_SIZE_128); + if (result != OEMCrypto_SUCCESS) { + WTPI_K1_FreeKeyHandle(encryption_key); + return result; + } + result = WTPI_C1_AESCBCDecrypt(encryption_key, KEY_SIZE_128, wrapped, + wrapped_size, iv, out); + WTPI_K1_FreeKeyHandle(encryption_key); + return result; +} \ No newline at end of file diff --git a/oemcrypto/opk/ports/trusty/ta/interface_impls/device_key_v0_interface.h b/oemcrypto/opk/ports/trusty/ta/interface_impls/device_key_v0_interface.h new file mode 100644 index 0000000..354a286 --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/interface_impls/device_key_v0_interface.h @@ -0,0 +1,29 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "OEMCryptoCENCCommon.h" + +OEMCryptoResult VerifyAndDecryptDRMKeyData_V0(const uint8_t* wrapped, + size_t wrapped_size, + uint8_t* out); + +OEMCryptoResult VerifyAndDecryptUsageData_V0(const uint8_t* wrapped, + size_t wrapped_size, + const uint8_t* signature, + const uint8_t* iv, + uint8_t* out); diff --git a/oemcrypto/opk/ports/trusty/ta/interface_impls/transport_interface.c b/oemcrypto/opk/ports/trusty/ta/interface_impls/transport_interface.c new file mode 100644 index 0000000..25f814e --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/interface_impls/transport_interface.c @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define TLOG_TAG "widevine" + +#include +#include +#include + +#include "tos_transport_interface.h" + +static uint8_t message_buffer[OPK_TRANSPORT_MESSAGE_SIZE]; + +bool TOS_Transport_Initialize(void) { + return true; +} + +void TOS_Transport_Terminate(void) {} + +ODK_Message TOS_Transport_GetRequest(void) { + return ODK_Message_Create(message_buffer, sizeof(message_buffer)); +} + +ODK_Message TOS_Transport_GetResponse(void) { + return TOS_Transport_GetRequest(); +} + +void TOS_Transport_ReleaseMessage(ODK_Message* message) {} diff --git a/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_clock_layer2.c b/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_clock_layer2.c new file mode 100644 index 0000000..a9a7ac4 --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_clock_layer2.c @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define TLOG_TAG "generation" +#include + +#include +#include + +#include + +OEMCryptoResult WTPI_GetSecureTimer(uint64_t* time_in_s) { + int64_t now; + trusty_gettime(0, &now); + *time_in_s = now / 1000000000ll; + return OEMCrypto_SUCCESS; +} diff --git a/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_config.c b/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_config.c new file mode 100644 index 0000000..5cdd626 --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_config.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +OEMCrypto_Security_Level WTPI_GetSecurityLevel(void) { + return OEMCrypto_Level1; +} + +/* + * WIDEVINE_PROVISION_METHOD is a mandatory variable set by the platform's build + * file (*-inc.mk) + */ +OEMCrypto_ProvisioningMethod WTPI_GetProvisioningMethod(void) { +#if WIDEVINE_PROVISION_METHOD == 2 + return OEMCrypto_Keybox; +#elif WIDEVINE_PROVISION_METHOD == 4 + return OEMCrypto_BootCertificateChain; +#else +#error message "Unsupported provisioning method." +#endif +} + +uint32_t WTPI_GetResourceRatingTier(void) { + return 3; +} + +uint32_t TEE_GetRSAPaddingSchemes(void) { + return kSign_RSASSA_PSS ^ kSign_PKCS1_Block1; +} + +OEMCryptoResult WTPI_GetCurrentSRMVersion(uint32_t* srm_version) { + if (srm_version == NULL) + return OEMCrypto_ERROR_INVALID_CONTEXT; + *srm_version = 0; + return OEMCrypto_SUCCESS; +} + +bool WTPI_IsAntiRollbackHWPresent() { + return false; +} + +OEMCryptoResult WTPI_ApplyCGMS(uint8_t cgms_field) { + /* No CGMS for test impl. */ + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +bool WTPI_IsCGMS_AActive(void) { + return false; +} + +bool WTPI_SupportsCGMS_A(void) { + return false; +} + +bool WTPI_HasAnalogDisplay(void) { + return false; +} + +bool WTPI_IsAnalogDisplayActive(void) { + return false; +} + +bool WTPI_CanDisableAnalogDisplay(void) { + return false; +} + +bool WTPI_DisableAnalogDisplay(void) { + return false; +} + +size_t WTPI_MaxOutputSizeForDecrypt(void) { + return 0; /* Unrestricted. */ +} + +bool WTPI_IsClosedPlatform(void) { + return false; +} + +OEMCrypto_HDCP_Capability WTPI_CurrentHDCPCapability(void) { + return HDCP_NO_DIGITAL_OUTPUT; +} + +OEMCrypto_HDCP_Capability WTPI_MaxHDCPCapability(void) { + return HDCP_NO_DIGITAL_OUTPUT; +} + +size_t WTPI_MaxBufferSizeForGenericCrypto(void) { + return 500 * 1024; /* 500 KiB according to resource rating tier. */ +} + +size_t WTPI_MaxSampleSize(void) { + return 16 * 1024 * 1024; /* 16 MiB according to resource rating tier. */ +} + +uint32_t WTPI_SupportedCertificates(void) { + return OEMCrypto_Supports_RSA_2048bit | OEMCrypto_Supports_RSA_3072bit; +} + +bool WTPI_IsProductionReady(void) { + // TODO(b/224065807): revisit this for checking for production readiness + return true; +} + +OEMCrypto_DTCP2_Capability WTPI_GetDTCP2Capability(void) { + return OEMCrypto_NO_DTCP2; +} + +OEMCrypto_WatermarkingSupport WTPI_GetWatermarkingSupport(void) { + return OEMCrypto_WatermarkingNotSupported; +} + +OPK_FeatureStatus WTPI_IsTAAntiRollbackEnabled(void) { + // TODO(b/224065807): revisit this for checking for production readiness + return OPK_FEATURE_NOT_SUPPORTED; +} diff --git a/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_crypto_and_key_management_layer1_openssl.c b/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_crypto_and_key_management_layer1_openssl.c new file mode 100644 index 0000000..ac1f22c --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_crypto_and_key_management_layer1_openssl.c @@ -0,0 +1,786 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wtpi_crypto_and_key_management_interface_layer1.h" + +#include +#include +#include +#include /* THIS IS TEMPORARY UNTIL WE GET AN ALLOCATOR. */ +#include + +#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_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; + } +} + +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) + 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; +} + +/****************************************************************************** + * Trusty specific device key derivation + ******************************************************************************/ + +#define MAX_KEY_SIZE 32 + +static const uint8_t DERIVATION_DATA[MAX_KEY_SIZE + 1] = + "..........TrustyWvDerivationData"; + +static void generate_derivation_data(uint32_t context, + uint32_t key_type, + uint8_t* data, + size_t data_len) { + assert(data != NULL); + + /* + * The varying data must be included in the derivation data. + * Unfortunately the low-level key derivation interface requires the + * derivation data to be equal to the output key size, and we can generate + * two different key sizes. + * It's OK to truncate the constant part on smaller keys. + */ + assert(data_len > 4); + + /* Initialize with a constant payload. */ + memcpy(data, DERIVATION_DATA, data_len); + + /* Set the first four bytes to the specific use for this key. */ + memcpy(data, &context, sizeof(context)); + + /* Set the next four bytes to the key type. */ + memcpy(data + sizeof(context), &key_type, sizeof(key_type)); +} + +static long derive_key(uint8_t* derivation_data, + uint8_t* key, + size_t key_size) { + long rc = hwkey_open(); + if (rc < 0) { + return rc; + } + hwkey_session_t session = (hwkey_session_t)rc; + uint32_t kdf_version = HWKEY_KDF_VERSION_1; + rc = hwkey_derive(session, &kdf_version, derivation_data, key, key_size); + hwkey_close(session); + return rc; +} + +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"); + + // 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; + + uint8_t derivation_data[MAX_KEY_SIZE]; + generate_derivation_data(context, type_32, derivation_data, + (size_t)out_key_size); + + if (derive_key(derivation_data, out_key, (size_t)out_key_size) < 0) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +/****************************************************************************** + * End Trusty specific device key derivation + ******************************************************************************/ + +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_size != KEY_SIZE_256) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (!IsKeyHandleValid(key_handle)) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + OEMCryptoResult result = PrepareCachedKey(key_handle, key_length); + 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; +} diff --git a/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_crypto_asymmetric.c b/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_crypto_asymmetric.c new file mode 100644 index 0000000..2c9ac6e --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_crypto_asymmetric.c @@ -0,0 +1,483 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wtpi_crypto_asymmetric_interface.h" + +#include +#include +#include +#include + +#include "OEMCryptoCENCCommon.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); + } + // TODO(b/201581141): Renamed to be more applicable to both RSA and ECC + // keys. + return OEMCrypto_ERROR_INVALID_RSA_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_RSA_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) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult WTPI_GetBootCertificateChain(uint8_t* out, size_t* out_length) { + if (out_length == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (out == NULL || *out_length < HWBCC_MAX_RESP_PAYLOAD_SIZE) { + *out_length = HWBCC_MAX_RESP_PAYLOAD_SIZE; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + // Call with an all zero key and ignore the signature. + uint8_t key_unused[32]; + memset(key_unused, 0, sizeof(key_unused)); + uint8_t signature_unused[HWBCC_MAX_RESP_PAYLOAD_SIZE]; + size_t signature_size_unused = 0; + int result = hwbcc_get_protected_data( + /*test_mode=*/false, /*cose_algorithm=*/HWBCC_ALGORITHM_ED25519, + /*key=*/key_unused, /*key_size=*/sizeof(key_unused), + /*aad=*/NULL, /*aad_size=*/0, /*cose_sign1=*/signature_unused, + /*cose_sign1_buf_size=*/sizeof(signature_unused), + /*cose_sign1_size=*/&signature_size_unused, /*bcc=*/out, + /*bcc_buf_size=*/*out_length, /*bcc_size=*/out_length); + + return result == 0 ? OEMCrypto_SUCCESS : OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +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 == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + // The message is expected to be an EC p256 certificate public key. + // Notice that the signature contains |message| itself. + if (message_length > HWBCC_MAX_ENCODED_KEY_SIZE) { + return OEMCrypto_ERROR_BUFFER_TOO_LARGE; + } + if (signature == NULL || *signature_length < HWBCC_MAX_RESP_PAYLOAD_SIZE) { + *signature_length = HWBCC_MAX_RESP_PAYLOAD_SIZE; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + // Ignore the BCC returned. + uint8_t bcc_unused[HWBCC_MAX_RESP_PAYLOAD_SIZE]; + size_t bcc_size_unused = 0; + int result = hwbcc_get_protected_data( + /*test_mode=*/false, /*cose_algorithm=*/HWBCC_ALGORITHM_ED25519, + /*key=*/message, /*key_size=*/message_length, /*aad=*/NULL, + /*aad_size=*/0, /*cose_sign1=*/signature, + /*cose_sign1_buf_size=*/*signature_length, + /*cose_sign1_size=*/signature_length, /*bcc=*/bcc_unused, + /*bcc_buf_size=*/sizeof(bcc_unused), /*bcc_size=*/&bcc_size_unused); + + return result == 0 ? OEMCrypto_SUCCESS : OEMCrypto_ERROR_UNKNOWN_FAILURE; +} diff --git a/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_device_key.c b/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_device_key.c new file mode 100644 index 0000000..ca50327 --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_device_key.c @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define TLOG_TAG "DEVICEKEY" + +#include "wtpi_device_key_interface.h" + +#include +#include + +#include "device_key_v0_interface.h" +#include "oemcrypto_key_types.h" +#include "oemcrypto_overflow.h" +#include "wtpi_crypto_and_key_management_interface_layer1.h" + +#define UUID_LENGTH 16 + +// If changing the WrappedData struct family, make sure to update +// ENCRYPT_AND_SIGN_EXTRA in wtpi_config_macros.h if needed. + +// This struct represents a wrapped blob that we do not yet know how to +// interpret. It contains only the fields that we expect every versioned blob to +// have. +typedef struct WrappedData { + uint8_t signature[SHA256_DIGEST_LENGTH]; + uint8_t magic[UUID_LENGTH]; + uint8_t version[sizeof(uint32_t)]; + uint8_t data[]; +} WrappedData; + +// The randomly-generated UUID that identifies a blob as a WrappedData struct, +// in network byte order. +static const uint8_t kMagicUuid[UUID_LENGTH] = { + 0xb5, 0x76, 0x3b, 0xad, 0x84, 0x05, 0x40, 0xfd, + 0xa0, 0x88, 0x3b, 0x6c, 0x69, 0x97, 0xfc, 0x74}; + +// 1 in network byte order +static const uint8_t kVersionOne[sizeof(uint32_t)] = {0x00, 0x00, 0x00, 0x01}; + +// This is the layout of the |data| field of a WrappedData structure when its +// |version| field is 1. +typedef struct WrappedData_V1 { + uint8_t iv[KEY_IV_SIZE]; + uint8_t enc_data[]; +} WrappedData_V1; + +OEMCryptoResult WTPI_GetEncryptAndSignSize(UNUSED uint32_t context, + size_t in_size, + size_t* wrapped_size) { + if (OPK_AddOverflowUX(in_size, sizeof(WrappedData) + sizeof(WrappedData_V1), + wrapped_size)) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_EncryptAndSign(uint32_t context, + const uint8_t* data, + size_t data_size, + uint8_t* out, + size_t* out_size) { + if (!out_size) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + size_t needed_size; + OEMCryptoResult result = + WTPI_GetEncryptAndSignSize(context, data_size, &needed_size); + if (result != OEMCrypto_SUCCESS) + return result; + if (*out_size < needed_size) { + *out_size = needed_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + // Allow querying size without these buffers. + if (!data || !out) + return OEMCrypto_ERROR_INVALID_CONTEXT; + *out_size = needed_size; + + WrappedData* const wrapped_header = (WrappedData*)out; + memcpy(wrapped_header->magic, kMagicUuid, sizeof(wrapped_header->magic)); + memcpy(wrapped_header->version, kVersionOne, + sizeof(wrapped_header->version)); + + WrappedData_V1* const wrapped_data = (WrappedData_V1*)wrapped_header->data; + /* Pick a random IV for generating keys. */ + result = WTPI_C1_RandomBytes(wrapped_data->iv, sizeof(wrapped_data->iv)); + if (result != OEMCrypto_SUCCESS) + return result; + + // Encrypt the buffer. + WTPI_K1_SymmetricKey_Handle encryption_key = NULL; + result = WTPI_K1_DeriveDeviceKeyIntoHandle(context, ENCRYPTION_KEY, + &encryption_key, KEY_SIZE_128); + if (result != OEMCrypto_SUCCESS) + return result; + result = WTPI_C1_AESCBCEncrypt(encryption_key, data, data_size, + wrapped_data->iv, wrapped_data->enc_data); + WTPI_K1_FreeKeyHandle(encryption_key); + 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_K1_SymmetricKey_Handle signing_key = NULL; + result = WTPI_K1_DeriveDeviceKeyIntoHandle(context, MAC_KEY_CLIENT, + &signing_key, KEY_SIZE_256); + if (result != OEMCrypto_SUCCESS) + return result; + const size_t offset = sizeof(wrapped_header->signature); + result = + WTPI_C1_HMAC_SHA256(signing_key, out + offset, needed_size - offset, + wrapped_header->signature); + WTPI_K1_FreeKeyHandle(signing_key); + return result; +} + +static OEMCryptoResult VerifyAndDecrypt_V1(uint32_t context, + const uint8_t* data, + size_t data_size, + uint8_t* out, + size_t* out_size) { + // We explicitly defer checking |out| until later, to allow querying the + // size without an out buffer. + if (data == NULL || + data_size < sizeof(WrappedData) + sizeof(WrappedData_V1) || + out_size == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + const WrappedData* const wrapped_header = (const WrappedData*)data; + + // Verify the signature first, before interpreting. + WTPI_K1_SymmetricKey_Handle signing_key = NULL; + OEMCryptoResult result = WTPI_K1_DeriveDeviceKeyIntoHandle( + context, MAC_KEY_SERVER, &signing_key, KEY_SIZE_256); + if (result != OEMCrypto_SUCCESS) + return result; + const size_t offset = sizeof(wrapped_header->signature); + result = WTPI_C1_HMAC_SHA256_Verify(signing_key, data + offset, + data_size - offset, + wrapped_header->signature); + WTPI_K1_FreeKeyHandle(signing_key); + if (result != OEMCrypto_SUCCESS) + return result; + + // If someday we support multiple versions, we'll need to decode + // wrapped_header->version and switch our behavior depending on the version + // we find. But for now, since only version 1 is valid, we can just verify + // that wrapped_header->version is 1. + if (memcmp(wrapped_header->magic, kMagicUuid, + sizeof(wrapped_header->magic)) != 0 || + memcmp(wrapped_header->version, kVersionOne, + sizeof(wrapped_header->version)) != 0) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + const size_t needed_size = + data_size - (sizeof(WrappedData) + sizeof(WrappedData_V1)); + if (*out_size < needed_size) { + *out_size = needed_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + // We defer this check to allow querying the size without an out buffer. + if (out == NULL) + return OEMCrypto_ERROR_INVALID_CONTEXT; + *out_size = needed_size; + + const WrappedData_V1* const wrapped_data = + (const WrappedData_V1*)wrapped_header->data; + + // Decrypt the buffer. + WTPI_K1_SymmetricKey_Handle encryption_key = NULL; + result = WTPI_K1_DeriveDeviceKeyIntoHandle(context, ENCRYPTION_KEY, + &encryption_key, KEY_SIZE_128); + if (result != OEMCrypto_SUCCESS) + return result; + KeySize key_size; + result = WTPI_K1_GetKeySize(encryption_key, &key_size); + if (result != OEMCrypto_SUCCESS) + return result; + result = WTPI_C1_AESCBCDecrypt(encryption_key, (size_t)key_size, + wrapped_data->enc_data, needed_size, + wrapped_data->iv, out); + WTPI_K1_FreeKeyHandle(encryption_key); + return result; +} + +// The input |data| buffer can be either a serialized WrappedData blob, or a +// wrapped RSA key in old format used on Pixel 6 devices that are already in the +// fields. The implementation of this function needs to detect and handle the +// old format wrapped key to provide backwards-compatibility. +OEMCryptoResult WTPI_VerifyAndDecrypt(uint32_t context, + const uint8_t* data, + size_t data_size, + uint8_t* out, + size_t* out_size) { + if (data == NULL || data_size == 0 || out == NULL || out_size == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + // Handle the wrapped RSA key in old format + bool is_v0_drm_key_candidate = false; + if (data_size < sizeof(WrappedData)) { + is_v0_drm_key_candidate = true; + } else { + const WrappedData* wrapped_header = (WrappedData*)data; + if (memcmp(wrapped_header->magic, kMagicUuid, UUID_LENGTH) != 0) { + is_v0_drm_key_candidate = true; + } + } + if (is_v0_drm_key_candidate) { + if (context != DEVICE_KEY_WRAP_DRM_CERT) { + TLOGE("Either the format of the wrapped data is unrecognized, or" + "the context value is invalid. context = %u", + context); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + TLOGD("DRM key wrapped in legacy format detected."); + return VerifyAndDecryptDRMKeyData_V0(data, data_size, out); + } + // Handle other versioned WrappedData blob + // This function is broken out separately in order to ease maintenance for + // those who also need to maintain backwards-compatibility with other + // versioned wrapping formats. + return VerifyAndDecrypt_V1(context, data, data_size, out, out_size); +} + +// The input |wrapped| buffer can be either an encrypted usage table header +// blob, or an encrypted usage entry blob. This function shall only be called on +// a Pixel 6 device in the field which has usage table and entry data wrapped in +// an old format. +OEMCryptoResult WTPI_VerifyAndDecryptUsageData_Legacy(const uint8_t* wrapped, + size_t wrapped_size, + const uint8_t* signature, + const uint8_t* iv, + uint8_t* out) { + return VerifyAndDecryptUsageData_V0(wrapped, wrapped_size, signature, iv, + out); +} \ No newline at end of file diff --git a/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_initialize_terminate.c b/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_initialize_terminate.c new file mode 100644 index 0000000..1c31565 --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_initialize_terminate.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define TLOG_TAG "INIT_TERMINATE" +#include +#include + +#include + +#include +#include + +#include "tee_context.h" + +static void close_storage_sessions(tee_context* ctx) { + if (ctx->provisioning_session != STORAGE_INVALID_SESSION) { + storage_close_session(ctx->provisioning_session); + ctx->provisioning_session = STORAGE_INVALID_SESSION; + } + if (ctx->user_data_session != STORAGE_INVALID_SESSION) { + storage_close_session(ctx->user_data_session); + ctx->user_data_session = STORAGE_INVALID_SESSION; + } +} + +OEMCryptoResult WTPI_Initialize(void) { + TLOGD("WTPI_Initialize\n"); + + tee_context* ctx = get_tee_context(); + + assert(ctx->provisioning_session == STORAGE_INVALID_SESSION); + assert(ctx->user_data_session == STORAGE_INVALID_SESSION); + + storage_session_t newsession; + int rc = storage_open_session(&newsession, STORAGE_CLIENT_TDP_PORT); + if (rc < 0) { + TLOGE("WTPI_Initialize: couldn't open provisioning storage session\n"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + ctx->provisioning_session = newsession; + + rc = storage_open_session(&newsession, STORAGE_CLIENT_TD_PORT); + if (rc < 0) { + TLOGE("WTPI_Initialize: couldn't open user data storage session\n"); + close_storage_sessions(ctx); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + ctx->user_data_session = newsession; + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_Terminate(void) { + TLOGD("WTPI_Terminate\n"); + close_storage_sessions(get_tee_context()); + return OEMCrypto_SUCCESS; +} diff --git a/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_persistent_storage.c b/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_persistent_storage.c new file mode 100644 index 0000000..baf7f7a --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_persistent_storage.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define TLOG_TAG "generation" +#include +#include + +#include + +#include + +#include "tee_context.h" + +static const char* kPersistPath = "widevine_persistent_data"; + +OEMCryptoResult WTPI_PrepareStoredPersistentData(void) { + TLOGD("WTPI_PrepareStoredPersistentData\n"); + tee_context* ctx = get_tee_context(); + assert(ctx->user_data_session != STORAGE_INVALID_SESSION); + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_LoadPersistentData(uint8_t* data, size_t* data_length) { + TLOGD("WTPI_LoadPersistentData\n"); + + if (!data || !data_length) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + tee_context* ctx = get_tee_context(); + assert(ctx->user_data_session != STORAGE_INVALID_SESSION); + + file_handle_t handle; + int rc = storage_open_file(ctx->user_data_session, &handle, kPersistPath, 0, + 0); + if (rc < 0) { + return OEMCrypto_ERROR_OPEN_FAILURE; + } + + storage_off_t datasize; + rc = storage_get_file_size(handle, &datasize); + if (rc < 0) { + TLOGE("couldn't get file size: %d\n", rc); + storage_close_file(handle); + return OEMCrypto_ERROR_OPEN_FAILURE; + } + + if (datasize > *data_length) { + TLOGE("persisted data too big"); + storage_close_file(handle); + *data_length = datasize; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + rc = storage_read(handle, 0, data, datasize); + storage_close_file(handle); + if (rc < 0) { + TLOGE("error reading persisted data: %d\n", rc); + return OEMCrypto_ERROR_OPEN_FAILURE; + } + *data_length = datasize; + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_StorePersistentData(const uint8_t* data, + size_t data_length) { + TLOGD("WTPI_SaveGenerationNumber\n"); + + tee_context* ctx = get_tee_context(); + assert(ctx->user_data_session != STORAGE_INVALID_SESSION); + + file_handle_t handle; + int rc = storage_open_file( + ctx->user_data_session, &handle, kPersistPath, + STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, 0); + if (rc < 0) { + TLOGE("failed to create persist file: %d\n", rc); + return OEMCrypto_ERROR_OPEN_FAILURE; + } + + rc = storage_write(handle, 0, data, data_length, STORAGE_OP_COMPLETE); + storage_close_file(handle); + if (rc < 0) { + TLOGE("failed to write persist data: %d\n", rc); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} diff --git a/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_root_of_trust_layer2.c b/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_root_of_trust_layer2.c new file mode 100644 index 0000000..b8bad5b --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_root_of_trust_layer2.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define TLOG_TAG "ROTLAYER2" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "tee_context.h" + +static const char* kKeyboxPath = "widevine_keybox"; + +OEMCryptoResult WTPI_UnwrapRootOfTrust(const uint8_t* input, + size_t input_length, + uint8_t* output, + size_t* output_length) { + TLOGD("WTPI_UnwrapRootOfTrust\n"); + + if (input == NULL || output == NULL || output_length == NULL) { + TLOGE("WTPI_UnwrapRootOfTrust: invalid parameters\n"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + /* Try to unwrap the keybox. */ + int rc = keybox_unwrap(input, input_length, output, *output_length, + output_length); + if (rc == KEYBOX_STATUS_SUCCESS) { + return OEMCrypto_SUCCESS; + } + + /* If unwrapping failed, assume the keybox is cleartext. */ + TLOGW("WTPI_UnwrapRootOfTrust: decryption failed (%d), assuming cleartext\n", + rc); + + /* The output buffer needs to be at least the size of the input buffer. */ + size_t min_output_length = input_length; + if (min_output_length > *output_length) { + *output_length = min_output_length; + TLOGE("WTPI_UnwrapRootOfTrust: output buffer too short, should be at least %zu bytes\n", + min_output_length); + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + *output_length = min_output_length; + memcpy(output, input, *output_length); + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_SaveRootOfTrust(const uint8_t* keybox, size_t length) { + TLOGD("WTPI_SaveRootOfTrust\n"); + tee_context* ctx = get_tee_context(); + assert(ctx->provisioning_session != STORAGE_INVALID_SESSION); + + if (!system_state_provisioning_allowed()) { + TLOGE("provisioning not allowed\n"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + if (keybox == NULL) { + TLOGE("WTPI_SaveRootOfTrust: no keybox provided\n"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + file_handle_t handle; + int rc = storage_open_file( + ctx->provisioning_session, &handle, kKeyboxPath, + STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, 0); + if (rc < 0) { + TLOGE("WTPI_SaveRootOfTrust: failed to create keybox: %d\n", rc); + return OEMCrypto_ERROR_OPEN_FAILURE; + } + rc = storage_write(handle, 0, keybox, length, STORAGE_OP_COMPLETE); + storage_close_file(handle); + if (rc < 0) { + TLOGE("WTPI_SaveRootOfTrust: failed to write keybox: %d\n", rc); + return OEMCrypto_ERROR_CLOSE_FAILURE; + } + + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_LoadRootOfTrust(uint8_t* keybox, size_t* length) { + TLOGD("WTPI_LoadRootOfTrust\n"); + tee_context* ctx = get_tee_context(); + assert(ctx->provisioning_session != STORAGE_INVALID_SESSION); + + if (keybox == NULL || length == NULL) { + TLOGE("WTPI_LoadRootOfTrust: invalid parameters\n"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + file_handle_t handle; + int rc = storage_open_file(ctx->provisioning_session, &handle, kKeyboxPath, + 0, 0); + if (rc < 0) { + TLOGE("WTPI_LoadRootOfTrust: error opening keybox: %d\n", rc); + return OEMCrypto_ERROR_OPEN_FAILURE; + } + + storage_off_t keysize; + rc = storage_get_file_size(handle, &keysize); + if (rc < 0) { + TLOGE("WTPI_LoadRootOfTrust: couldn't get file size: %d\n", rc); + storage_close_file(handle); + return OEMCrypto_ERROR_OPEN_FAILURE; + } + + if (*length < keysize) { + TLOGE("WTPI_LoadRootOfTrust: output buffer too small, should be at least %zu bytes\n", + (size_t)keysize); + storage_close_file(handle); + *length = keysize; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + rc = storage_read(handle, 0, keybox, keysize); + storage_close_file(handle); + if (rc < 0) { + TLOGE("WTPI_LoadRootOfTrust: error reading keybox: %d\n", rc); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + assert((size_t)rc == keysize); + + *length = keysize; + return OEMCrypto_SUCCESS; +} diff --git a/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_secure_buffer_access.c b/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_secure_buffer_access.c new file mode 100644 index 0000000..0dae23b --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/interface_impls/wtpi_secure_buffer_access.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wtpi_secure_buffer_access_interface.h" + +OEMCryptoResult WTPI_GetSecureBufferAddress(void* secure, + size_t offset, + uint8_t** out_addr) { + if (secure == NULL || out_addr == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + *out_addr = ((uint8_t*)secure) + offset; + return OEMCrypto_SUCCESS; +} diff --git a/oemcrypto/opk/ports/trusty/ta/liboemcrypto/.gitignore b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/.gitignore new file mode 100644 index 0000000..4c72101 --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/.gitignore @@ -0,0 +1,2 @@ +# The output directory for the NDK build. +/out/ diff --git a/oemcrypto/opk/ports/trusty/ta/liboemcrypto/README.md b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/README.md new file mode 100644 index 0000000..cb4b54f --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/README.md @@ -0,0 +1,9 @@ +# liboemcrypto.so for Trusty + +This directory contains a simple liboemcrypto.so that serializes commands into +the TrustZone using Trusty IPC. For prototyping reasons, the code is set up to +be built with the NDK. This allows you to build it without having a full Android +checkout. + +Note that libtrusty/ has been copied from the Android build and should be +de-duplicated once this code has been integrated back into the Android build. diff --git a/oemcrypto/opk/ports/trusty/ta/liboemcrypto/build.sh b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/build.sh new file mode 100755 index 0000000..882c383 --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/build.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e +set -u + +# Build liboemcrypto.so + +if [ ! -v ANDROID_NDK_ROOT ]; +then + ANDROID_NDK_ROOT=$(realpath $(dirname "$0")/../../../../../prebuilts/ndk/r21) +fi + +$ANDROID_NDK_ROOT/ndk-build -C $(dirname "${BASH_SOURCE[0]}") NDK_PROJECT_PATH=out APP_BUILD_SCRIPT=Android.mk NDK_APPLICATION_MK=Application.mk NDK_DEBUG=1 -j`nproc` diff --git a/oemcrypto/opk/ports/trusty/ta/liboemcrypto/clean.sh b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/clean.sh new file mode 100755 index 0000000..d193c2e --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/clean.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +set -e +set -u + +# Remove liboemcrypto.so from the device. + +adb root +adb remount + +adb shell rm -f /system/lib/liboemcrypto.so +adb shell rm -f /system/lib64/liboemcrypto.so + +adb shell rm -f /vendor/lib/liboemcrypto.so +adb shell rm -f /vendor/lib64/liboemcrypto.so diff --git a/oemcrypto/opk/ports/trusty/ta/liboemcrypto/include/linux/dma-heap.h b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/include/linux/dma-heap.h new file mode 100644 index 0000000..1f20169 --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/include/linux/dma-heap.h @@ -0,0 +1,34 @@ +/**************************************************************************** + **************************************************************************** + *** + *** This header was automatically generated from a Linux kernel header + *** of the same name, to make information necessary for userspace to + *** call into the kernel available to libc. It contains only constants, + *** structures, and macros generated from the original header, and thus, + *** contains no copyrightable information. + *** + *** To edit the content of this header, modify the corresponding + *** source file (e.g. under external/kernel-headers/original/) then + *** run bionic/libc/kernel/tools/update_all.py + *** + *** Any manual change here will be lost the next time this script will + *** be run. You've been warned! + *** + **************************************************************************** + ****************************************************************************/ +#ifndef _UAPI_LINUX_DMABUF_POOL_H +#define _UAPI_LINUX_DMABUF_POOL_H +#include +#include +#define DMA_HEAP_VALID_FD_FLAGS (O_CLOEXEC | O_ACCMODE) +#define DMA_HEAP_VALID_HEAP_FLAGS (0) +struct dma_heap_allocation_data { + __u64 len; + __u32 fd; + __u32 fd_flags; + __u64 heap_flags; +}; +#define DMA_HEAP_IOC_MAGIC 'H' +#define DMA_HEAP_IOCTL_ALLOC \ + _IOWR(DMA_HEAP_IOC_MAGIC, 0x0, struct dma_heap_allocation_data) +#endif diff --git a/oemcrypto/opk/ports/trusty/ta/liboemcrypto/include/trusty_log.h b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/include/trusty_log.h new file mode 100644 index 0000000..516937c --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/include/trusty_log.h @@ -0,0 +1,16 @@ +#pragma once + +/* + * NDK polyfill for trusty_log.h + * This makes it easier to share code between Trusty and Android. + */ + +#include + +#define _LOG(level, fmt, ...) \ + __android_log_print(level, TLOG_TAG, fmt, ##__VA_ARGS__) + +#define TLOGD(fmt, ...) _LOG(ANDROID_LOG_DEBUG, fmt, ##__VA_ARGS__) +#define TLOGI(fmt, ...) _LOG(ANDROID_LOG_INFO, fmt, ##__VA_ARGS__) +#define TLOGW(fmt, ...) _LOG(ANDROID_LOG_WARN, fmt, ##__VA_ARGS__) +#define TLOGE(fmt, ...) _LOG(ANDROID_LOG_ERROR, fmt, ##__VA_ARGS__) diff --git a/oemcrypto/opk/ports/trusty/ta/liboemcrypto/libtrusty/include/trusty/ipc.h b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/libtrusty/include/trusty/ipc.h new file mode 100644 index 0000000..04e84c6 --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/libtrusty/include/trusty/ipc.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UAPI_LINUX_TRUSTY_IPC_H_ +#define _UAPI_LINUX_TRUSTY_IPC_H_ + +#include +#include +#include + +/** + * enum transfer_kind - How to send an fd to Trusty + * @TRUSTY_SHARE: Memory will be accessible by Linux and Trusty. On ARM it + * will be mapped as nonsecure. Suitable for shared memory. + * The paired fd must be a "dma_buf". + * @TRUSTY_LEND: Memory will be accessible only to Trusty. On ARM it will + * be transitioned to "Secure" memory if Trusty is in + * TrustZone. This transfer kind is suitable for donating + * video buffers or other similar resources. The paired fd + * may need to come from a platform-specific allocator for + * memory that may be transitioned to "Secure". + * @TRUSTY_SEND_SECURE: Send memory that is already "Secure". Memory will be + * accessible only to Trusty. The paired fd may need to + * come from a platform-specific allocator that returns + * "Secure" buffers. + * + * Describes how the user would like the resource in question to be sent to + * Trusty. Options may be valid only for certain kinds of fds. + */ +enum transfer_kind { + TRUSTY_SHARE = 0, + TRUSTY_LEND = 1, + TRUSTY_SEND_SECURE = 2, +}; + +/** + * struct trusty_shm - Describes a transfer of memory to Trusty + * @fd: The fd to transfer + * @transfer: How to transfer it - see &enum transfer_kind + */ +struct trusty_shm { + __s32 fd; + __u32 transfer; +}; + +/** + * struct tipc_send_msg_req - Request struct for @TIPC_IOC_SEND_MSG + * @iov: Pointer to an array of &struct iovec describing data to be sent + * @shm: Pointer to an array of &struct trusty_shm describing any file + * descriptors to be transferred. + * @iov_cnt: Number of elements in the @iov array + * @shm_cnt: Number of elements in the @shm array + */ +struct tipc_send_msg_req { + __u64 iov; + __u64 shm; + __u64 iov_cnt; + __u64 shm_cnt; +}; + +#define TIPC_IOC_MAGIC 'r' +#define TIPC_IOC_CONNECT _IOW(TIPC_IOC_MAGIC, 0x80, char*) +#define TIPC_IOC_SEND_MSG _IOW(TIPC_IOC_MAGIC, 0x81, struct tipc_send_msg_req) + +#if defined(CONFIG_COMPAT) +#define TIPC_IOC_CONNECT_COMPAT _IOW(TIPC_IOC_MAGIC, 0x80, compat_uptr_t) +#endif + +#endif diff --git a/oemcrypto/opk/ports/trusty/ta/liboemcrypto/libtrusty/include/trusty/tipc.h b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/libtrusty/include/trusty/tipc.h new file mode 100644 index 0000000..d2a3bc8 --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/libtrusty/include/trusty/tipc.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIB_TIPC_H +#define _LIB_TIPC_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +int tipc_connect(const char* dev_name, const char* srv_name); +ssize_t tipc_send(int fd, + const struct iovec* iov, + int iovcnt, + struct trusty_shm* shm, + int shmcnt); +int tipc_close(int fd); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/oemcrypto/opk/ports/trusty/ta/liboemcrypto/libtrusty/trusty.c b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/libtrusty/trusty.c new file mode 100644 index 0000000..f675dfe --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/libtrusty/trusty.c @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * The copy of libtrusty was slightly modified to compile with the NDK. + * This involved changing the logging API. + */ + +#define TLOG_TAG "libtrusty" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +int tipc_connect(const char* dev_name, const char* srv_name) { + int fd; + int rc; + + fd = TEMP_FAILURE_RETRY(open(dev_name, O_RDWR)); + if (fd < 0) { + rc = -errno; + TLOGE("%s: cannot open tipc device \"%s\": %s\n", __func__, dev_name, + strerror(errno)); + return rc < 0 ? rc : -1; + } + + rc = TEMP_FAILURE_RETRY(ioctl(fd, TIPC_IOC_CONNECT, srv_name)); + if (rc < 0) { + rc = -errno; + TLOGE("%s: can't connect to tipc service \"%s\" (err=%d)\n", __func__, + srv_name, errno); + close(fd); + return rc < 0 ? rc : -1; + } + + TLOGD("%s: connected to \"%s\" fd %d\n", __func__, srv_name, fd); + return fd; +} + +ssize_t tipc_send(int fd, + const struct iovec* iov, + int iovcnt, + struct trusty_shm* shms, + int shmcnt) { + struct tipc_send_msg_req req; + req.iov = (__u64)iov; + req.iov_cnt = (__u64)iovcnt; + req.shm = (__u64)shms; + req.shm_cnt = (__u64)shmcnt; + + int rc = TEMP_FAILURE_RETRY(ioctl(fd, TIPC_IOC_SEND_MSG, &req)); + if (rc < 0) { + TLOGE("%s: failed to send message (err=%d)\n", __func__, rc); + } + + return rc; +} + +void tipc_close(int fd) { + close(fd); +} diff --git a/oemcrypto/opk/ports/trusty/ta/liboemcrypto/push.sh b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/push.sh new file mode 100755 index 0000000..b1afc18 --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/push.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -e +set -u + +OUT_DIR=$(dirname "${BASH_SOURCE[0]}")/out + +# Push liboemcrypto.so onto the device. + +# Prepare to write to /vendor +adb root +adb remount + +# Write liboemcrypto +adb push $OUT_DIR/libs/arm64-v8a/liboemcrypto.so /vendor/lib64/liboemcrypto.so + +# Make sure it actually gets written to disk so it won't be lost if we crash. +adb shell sync diff --git a/oemcrypto/opk/ports/trusty/ta/liboemcrypto/secure_buffer.c b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/secure_buffer.c new file mode 100644 index 0000000..b3a4546 --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/secure_buffer.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define TLOG_TAG "liboemcrypto" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "secure_buffer.h" + +/* + * The CDM passes secure buffers as a void*, but the underlying type is a + * native_handle_t. Duplicate the structure definition since we don't have + * access to libcutils. + * TODO: can we do this in a more principled manner? + */ +typedef struct native_handle { + int version; /* sizeof(native_handle_t) */ + int numFds; /* number of file-descriptors at &data[0] */ + int numInts; /* number of ints at &data[numFds] */ + int data[]; /* numFds + numInts ints */ +} native_handle_t; + +void TOS_SecureBuffer_Pack(ODK_Message* message, + const OEMCrypto_DestBufferDesc* obj) { + assert(obj != NULL); + assert(obj->type == OEMCrypto_BufferType_Secure); + + /* Avoid global side effects if serialization has already failed. */ + if (ODK_Message_GetStatus(message) != MESSAGE_STATUS_OK) { + return; + } + + /* + * If the base is NULL, we calculating the message size rather than + * actually serializing the message. Skip registering the handle. + */ + int index = 0; + if (ODK_Message_GetBase(message) != NULL) { + /* Decode the handle. */ + native_handle_t* h = + (native_handle_t*)(obj->buffer.secure.secure_buffer); + int fd = h->data[0]; + size_t fd_len = h->data[h->numFds + 0]; + if (fd_len > WV_MAX_REASONABLE_BUFFER_SIZE) { + TLOGE("Secure output buffer is unexpectedly large.\n"); + ODK_Message_SetStatus(message, MESSAGE_STATUS_OVERFLOW_ERROR); + return; + } + if (fd_len < obj->buffer.secure.secure_buffer_length) { + TLOGE("API believes the secure output buffer is bigger than the system does.\n"); + ODK_Message_SetStatus(message, MESSAGE_STATUS_OVERFLOW_ERROR); + return; + } + index = register_handle(fd); + } + OPK_Pack_int(message, &index); + OPK_Pack_size_t(message, &obj->buffer.secure.secure_buffer_length); + OPK_Pack_size_t(message, &obj->buffer.secure.offset); +} + +/* Not called, but required by linker. */ +void TOS_SecureBuffer_Unpack(ODK_Message* message, + OEMCrypto_DestBufferDesc* obj) { + (void)(message); + (void)(obj); + assert(0); +} + +/* Not called, but required by linker. */ +bool TOS_SecureBuffer_CheckSize(void* handle, size_t size) { + (void)handle; + (void)size; + assert(0); + return false; +} diff --git a/oemcrypto/opk/ports/trusty/ta/liboemcrypto/secure_buffer.h b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/secure_buffer.h new file mode 100644 index 0000000..9b32256 --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/secure_buffer.h @@ -0,0 +1,15 @@ +/* + * Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine Master + * License Agreement. + */ + +#pragma once + +#include + +__BEGIN_CDECLS + +int register_handle(int fd); + +__END_CDECLS diff --git a/oemcrypto/opk/ports/trusty/ta/liboemcrypto/transport.c b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/transport.c new file mode 100644 index 0000000..18b4e47 --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/liboemcrypto/transport.c @@ -0,0 +1,429 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define TLOG_TAG "liboemcrypto" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "secure_buffer.h" + +static uint8_t message_buffer[OPK_TRANSPORT_MESSAGE_SIZE]; + +bool TOS_Transport_Initialize(void) { + return true; +} + +void TOS_Transport_Terminate(void) {} + +ODK_Message TOS_Transport_GetRequest(void) { + return ODK_Message_Create(message_buffer, sizeof(message_buffer)); +} + +void TOS_Transport_ReleaseMessage(ODK_Message* message) { + assert(message); + (void)message; +} + +static struct wv_ipc_header create_header(uint16_t tag) { + return (struct wv_ipc_header){.tag = tag}; +} + +static int send_tagged(int chan, + struct wv_ipc_header header, + void* data, + size_t num_bytes, + struct trusty_shm* handles, + size_t num_handles) { + if (num_bytes > WV_MAX_PAYLOAD_SIZE) { + TLOGE("Message is too big (%zu)\n", num_bytes); + return -1; + } + + struct iovec msg[] = { + { + .iov_base = &header, + .iov_len = WV_HEADER_SIZE, + }, + { + .iov_base = data, + .iov_len = num_bytes, + }, + }; + ssize_t rc = tipc_send(chan, msg, countof(msg), handles, num_handles); + if (rc < 0) { + TLOGE("Client send error on %d: %s (%zd)\n", chan, strerror(errno), rc); + return rc; + } + if ((size_t)rc != WV_HEADER_SIZE + num_bytes) { + TLOGE("Did not send complete message (%zd)\n", rc); + return -1; + } + return 0; +} + +static int receive_tagged(int chan, + struct wv_ipc_header* header, + uint8_t* data, + size_t* num_bytes) { + struct iovec msg[] = { + { + .iov_base = header, + .iov_len = WV_HEADER_SIZE, + }, + { + .iov_base = data, + .iov_len = *num_bytes, + }, + }; + ssize_t rc = readv(chan, msg, countof(msg)); + if (rc < 0) { + TLOGE("Client receive error on %d: %s (%zd)\n", chan, strerror(errno), + rc); + return rc; + } + if ((size_t)rc < WV_HEADER_SIZE) { + TLOGE("Message too small to contain header\n"); + return -1; + } + *num_bytes = (size_t)rc - WV_HEADER_SIZE; + return 0; +} + +struct shared_buffer { + int fd; + void* addr; + size_t size; +}; + +static int allocate_shared_buffer(int dma_heap_fd, + size_t size, + struct shared_buffer* buf) { + /* Default for early out. */ + buf->fd = -1; + buf->addr = NULL; + buf->size = 0; + + size_t aligned = 0; + if (align_overflow(size, getauxval(AT_PAGESZ), &aligned)) { + TLOGE("Rounding up buffer size oveflowed\n"); + return -1; + } + + if (aligned > WV_MAX_REASONABLE_BUFFER_SIZE) { + TLOGE("Requested an unreasonablly large buffer\n"); + return -1; + } + + struct dma_heap_allocation_data allocation_request = { + .len = aligned, + .fd_flags = O_RDWR | O_CLOEXEC, + }; + + /* Create the dma buffer. */ + int rc = ioctl(dma_heap_fd, DMA_HEAP_IOCTL_ALLOC, &allocation_request); + if (rc < 0) { + TLOGE("Buffer allocation request failed (%d)\n", rc); + return -1; + } + + int fd = allocation_request.fd; + if (fd < 0) { + TLOGE("Allocation request returned bad fd (%d)\n", fd); + return -1; + } + + void* addr = mmap(NULL, aligned, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) { + TLOGE("Failed to map buffer\n"); + close(fd); + return -1; + } + + buf->fd = fd; + buf->addr = addr; + buf->size = aligned; + + return 0; +} + +static void close_shared_buffer(struct shared_buffer* buf) { + if (buf->fd >= 0) { + close(buf->fd); + buf->fd = -1; + } + if (buf->addr != NULL) { + munmap(buf->addr, buf->size); + buf->addr = NULL; + buf->size = 0; + } +} + +struct transport_context { + bool ok; + int chan; + struct shared_buffer message; + struct shared_buffer shared; +}; + +static int allocate_buffers(struct transport_context* ctx) { + const char* device_name = "/dev/dma_heap/system"; + int dma_heap_fd = open(device_name, O_RDONLY | O_CLOEXEC); + if (dma_heap_fd < 0) { + TLOGE("Cannot open %s\n", device_name); + return -1; + } + if (allocate_shared_buffer(dma_heap_fd, WV_MESSAGE_BUFFER_SIZE, + &ctx->message) != 0) { + goto on_message_alloc_error; + } + if (allocate_shared_buffer(dma_heap_fd, WV_SHARED_BUFFER_SIZE, + &ctx->shared) != 0) { + goto on_shared_alloc_error; + } + close(dma_heap_fd); + TEE_SharedMemory_Bind(ctx->shared.addr, ctx->shared.size); + return 0; + +on_shared_alloc_error: + close_shared_buffer(&ctx->message); +on_message_alloc_error: + close(dma_heap_fd); + return -1; +} + +static void stop_trusty_transport(struct transport_context* ctx) { + if (ctx->ok) { + TOS_SharedMemory_Release(); + close_shared_buffer(&ctx->shared); + close_shared_buffer(&ctx->message); + if (ctx->chan >= 0) { + tipc_close(ctx->chan); + ctx->chan = -1; + } + ctx->ok = false; + } +} + +static int start_trusty_transport(struct transport_context* ctx) { + assert(!ctx->ok); + ctx->chan = -1; + + const char* device = "/dev/trusty-ipc-dev0"; + const char* port = WV_PORT_NAME; + int chan = tipc_connect(device, port); + if (chan < 0) { + TLOGE("Could not connect to %s through %s\n", port, device); + return -1; + } + + OPK_Initialize(); + + if (allocate_buffers(ctx) != 0) { + TOS_SharedMemory_Release(); + close(chan); + return -1; + } + + /* + * At this point the context is complete, but we still need to bind the + * buffers to the other side. + */ + ctx->chan = chan; + ctx->ok = true; + + /* Bind the shared buffers. */ + struct trusty_shm handles[] = { + { + .fd = ctx->message.fd, + .transfer = TRUSTY_SHARE, + }, + { + .fd = ctx->shared.fd, + .transfer = TRUSTY_SHARE, + }, + }; + struct bind_message message = { + .protocol_version = WV_PROTOCOL_VERSION, + .message_buffer_size = ctx->message.size, + .shared_buffer_size = ctx->shared.size, + }; + int rc = send_tagged(chan, create_header(WV_TAG_BIND), &message, + sizeof(message), handles, countof(handles)); + if (rc != 0) { + goto on_bind_error; + } + + /* + * Get ACK. + * If we don't wait for an ACK and send too many messages, the TA's + * receive buffer will fill up and messages will be silently discarded. + * This is a quirk of how IPC messages are transfered from the HLOS to + * the TA. For now, we work around it. + */ + struct wv_ipc_header header; + size_t num_bytes = 0; + rc = receive_tagged(chan, &header, NULL, &num_bytes); + if (rc != 0) { + goto on_bind_error; + } + if (header.tag != WV_TAG_ACK) { + TLOGE("Client received non-ACK message %d\n", header.tag); + goto on_bind_error; + } + return 0; + +on_bind_error: + stop_trusty_transport(ctx); + return -1; +} + +/* Must be global due to the API we're working with. */ +struct transport_context context; + +/* Create the connection if it doesn't already exist. */ +static int get_connection() { + struct transport_context* ctx = &context; + if (!ctx->ok) { + start_trusty_transport(ctx); + } + return ctx->chan; +} + +/* + * Close the connection if the shared library is unloaded. + */ +__attribute__((destructor)) static void stop_hook() { + struct transport_context* ctx = &context; + stop_trusty_transport(ctx); +} + +static struct trusty_shm handles[WV_MAX_NUM_HANDLES]; +static size_t num_handles; + +int register_handle(int fd) { + if (num_handles >= countof(handles)) { + return -1; + } + int index = num_handles; + handles[index] = (struct trusty_shm){ + .fd = dup(fd), + .transfer = TRUSTY_SEND_SECURE, + }; + num_handles += 1; + return index; +} + +/* + * Note: this hook will not be called if serialization fails, which means we + * cannot always clean up out global state. We will send too many handles on the + * next call. + * TODO: sure we always clean up the global state. + */ +void post_send_hook() { + for (size_t i = 0; i < num_handles; i++) { + close(handles[i].fd); + handles[i].fd = -1; + } + num_handles = 0; +} + +OPK_TransportStatus TOS_Transport_SendMessage(ODK_Message* message, + ODK_Message* response) { + int chan = get_connection(); + if (chan < 0) { + goto on_error; + } + + size_t sz = ODK_Message_GetSize(message); + + /* Validate the message size. */ + if (sz > context.message.size) { + TLOGE("Message too big. (%zu)\n", sz); + goto on_error; + } + + /* Copy the message into shared memory. */ + memcpy(context.message.addr, ODK_Message_GetBase(message), sz); + + /* Send IPC message. */ + struct widevine_message m = { + .message_size = (uint32_t)sz, + }; + int rc = send_tagged(chan, create_header(WV_TAG_WIDEVINE), &m, sizeof(m), + handles, num_handles); + if (rc != 0) { + goto on_error; + } + + post_send_hook(); + + /* Receive the response data */ + struct wv_ipc_header header; + uint8_t ipc_buffer[WV_IPC_BUFFER_SIZE]; + size_t num_bytes = sizeof(ipc_buffer); + rc = receive_tagged(chan, &header, ipc_buffer, &num_bytes); + if (rc != 0) { + goto on_error; + } + + if (validate_message_metadata(header.tag, num_bytes, 0) != 0) { + goto on_error; + } + + if (header.tag != WV_TAG_WIDEVINE) { + TLOGE("Client received non-WIDEVINE response %d\n", header.tag); + goto on_error; + } + + struct widevine_message* msg = (struct widevine_message*)ipc_buffer; + + /* Validate the size. */ + sz = msg->message_size; + if (sz > context.message.size) { + TLOGE("Message too large (%u)\n", msg->message_size); + return OPK_TRANSPORT_STATUS_IO_ERROR; + } + + /* Fill in the response. */ + *response = TOS_Transport_GetRequest(); + ODK_Message_SetSize(response, sz); + memcpy(ODK_Message_GetBase(response), context.message.addr, sz); + return OPK_TRANSPORT_STATUS_OK; + +on_error: + post_send_hook(); + return OPK_TRANSPORT_STATUS_IO_ERROR; +} diff --git a/oemcrypto/opk/ports/trusty/ta/manifest.json b/oemcrypto/opk/ports/trusty/ta/manifest.json new file mode 100644 index 0000000..c34831a --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/manifest.json @@ -0,0 +1,9 @@ +{ + "uuid": "08d3ed40-bde2-448c-a91d-75f1989c57ef", + "app_name": "widevine", + "min_heap": 1048576, + "min_stack": 16384, + "mgmt_flags": { + "non_critical_app": true + } +} diff --git a/oemcrypto/opk/ports/trusty/ta/secure_buffer.c b/oemcrypto/opk/ports/trusty/ta/secure_buffer.c new file mode 100644 index 0000000..af059cc --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/secure_buffer.c @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "secure_buffer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#define TLOG_TAG "widevine" + +/* + * Keep track of the state of secure buffers that were sent to us as part of + * this transaction. + */ +struct secure_buffer_info { + int handle; + size_t size; + void* addr; +}; + +static struct secure_buffer_info secure_buffers[WV_MAX_NUM_HANDLES]; +static size_t num_secure_buffers; + +/* + * This buffer was received as part of a transaction and should be mapped and + * cleaned up as needed. + */ +int register_secure_buffer(int handle) { + assert(handle != INVALID_IPC_HANDLE); + /* + * This should only triggered if the IPC system handle limit gets out of + * sync with the secure buffer array size. + */ + if (num_secure_buffers >= countof(secure_buffers)) { + TLOGE("Tried to register too many secure buffers.\n"); + return -1; + } + secure_buffers[num_secure_buffers] = (struct secure_buffer_info){ + .handle = handle, .size = 0, .addr = NULL}; + num_secure_buffers += 1; + return 0; +} + +int secure_buffer_address(size_t index, void** out_addr) { + if (index >= num_secure_buffers) { + TLOGE("Invalid secure buffer handle.\n"); + return -1; + } + void* addr = secure_buffers[index].addr; + size_t size = secure_buffers[index].size; + if (addr == NULL && size > 0) { + /* Lazy mmap */ + int handle = secure_buffers[index].handle; + assert(handle != INVALID_IPC_HANDLE); + /* Validate this handle is safe to use as a secure output buffer. */ + if (system_specific_validate_secure_output_buffer(handle) != 0) { + return -1; + } + addr = mmap(0, size, PROT_READ | PROT_WRITE, 0, handle, 0); + if (addr == MAP_FAILED) { + TLOGE("Failed to map handle=%d size=%zu\n", handle, size); + return -1; + } + /* Cache */ + secure_buffers[index].addr = addr; + + /* Handle no longer needed */ + close(handle); + secure_buffers[index].handle = INVALID_IPC_HANDLE; + } + *out_addr = addr; + return 0; +} + +void cleanup_secure_buffers(void) { + for (size_t i = 0; i < num_secure_buffers; i++) { + /* Clean up mapping */ + void* addr = secure_buffers[i].addr; + size_t size = secure_buffers[i].size; + if (addr != NULL) { + /* Flush */ + finish_dma(addr, size, DMA_FLAG_FROM_DEVICE); + /* Unmap */ + if (munmap(addr, size)) { + TLOGE(" %zu munmap failed %p/%zu\n", i, addr, size); + } + } + + /* Clean up handle */ + int handle = secure_buffers[i].handle; + if (handle != INVALID_IPC_HANDLE) { + close(handle); + } + + /* Overly cautious clear */ + secure_buffers[num_secure_buffers] = (struct secure_buffer_info){ + .handle = INVALID_IPC_HANDLE, .size = 0, .addr = NULL}; + } + num_secure_buffers = 0; +} + +void TOS_SecureBuffer_Unpack(ODK_Message* message, + OEMCrypto_DestBufferDesc* obj) { + assert(obj->type == OEMCrypto_BufferType_Secure); + + /* + * This function has side effects, so we should not continue trying to + * unpack in an error state. + */ + if (ODK_Message_GetStatus(message) != MESSAGE_STATUS_OK) { + return; + } + + /* Deserialize the handle index. */ + int raw_index = 0; + OPK_Unpack_int(message, &raw_index); + if (ODK_Message_GetStatus(message) != MESSAGE_STATUS_OK) { + return; + } + + /* Bounds check the index. */ + if (raw_index < 0 || (size_t)raw_index >= num_secure_buffers) { + TLOGE("Index out of range (%d)\n", raw_index); + ODK_Message_SetStatus(message, MESSAGE_STATUS_INVALID_ENUM_VALUE); + return; + } + + size_t index = (size_t)raw_index; + + /* Deserialize the other fields. */ + size_t handle_length = 0; + OPK_Unpack_size_t(message, &handle_length); + size_t offset = 0; + OPK_Unpack_size_t(message, &offset); + + /* Again, avoid side effects. */ + if (ODK_Message_GetStatus(message) != MESSAGE_STATUS_OK) { + return; + } + + /* Reject unreasonablely large buffers before we do any math on the size. */ + if (handle_length > WV_MAX_REASONABLE_BUFFER_SIZE) { + TLOGE("Secure output buffer is unexpectedly large.\n"); + ODK_Message_SetStatus(message, MESSAGE_STATUS_OVERFLOW_ERROR); + return; + } + + /* Validate invariant */ + if (offset >= handle_length) { + TLOGE("offset is outside secure buffer\n"); + ODK_Message_SetStatus(message, MESSAGE_STATUS_PARSE_ERROR); + return; + } + + /* + * Size is not known when the handle is initially received via IPC. + * Note that the Widevine API may give strange non-page-sized buffer sizes + * that are smaller than the actual buffer size. We work from these sizes + * so that we don't map and unmap too much memory, and don't need to + * enforce an invariant between the "real" and the "specified" sizes. + */ + size_t size = 0; + if (align_overflow(handle_length, 4096, &size)) { + TLOGE("Rounding up buffer size overflowed (%zu)\n", handle_length); + ODK_Message_SetStatus(message, MESSAGE_STATUS_PARSE_ERROR); + return; + } + + /* Validate */ + if (secure_buffers[index].size != 0) { + TLOGE("Attempted to redefine secure buffer size\n"); + ODK_Message_SetStatus(message, MESSAGE_STATUS_PARSE_ERROR); + return; + } + + /* Commit */ + secure_buffers[index].size = size; + + /* + * Eagerly map the buffer and store the pointer as the handle. + * In the future we may have an opaque handle and lazy map. + */ + void* addr; + if (secure_buffer_address(index, &addr) != 0) { + ODK_Message_SetStatus(message, MESSAGE_STATUS_PARSE_ERROR); + return; + } + obj->buffer.secure.secure_buffer = addr; + obj->buffer.secure.secure_buffer_length = handle_length; + obj->buffer.secure.offset = offset; +} + +/* Not called, but required by linker. */ +void TOS_SecureBuffer_Pack(ODK_Message* message, + const OEMCrypto_DestBufferDesc* obj) { + (void)(message); + (void)(obj); + assert(0); +} + +bool TOS_SecureBuffer_CheckSize(void* handle, size_t size) { + for (size_t i = 0; i < num_secure_buffers; i++) { + if (secure_buffers[i].addr == handle) { + if (secure_buffers[i].size >= size) { + return true; + } else { + TLOGE("buffer size too small\n"); + return false; + } + } + } + TLOGE("unknown handle\n"); + return true; +} diff --git a/oemcrypto/opk/ports/trusty/ta/secure_buffer.h b/oemcrypto/opk/ports/trusty/ta/secure_buffer.h new file mode 100644 index 0000000..6e9181a --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/secure_buffer.h @@ -0,0 +1,16 @@ +/* + * Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine Master + * License Agreement. + */ + +#pragma once + +#include + +__BEGIN_CDECLS + +int register_secure_buffer(int handle) WARN_UNUSED_RESULT; +void cleanup_secure_buffers(void); + +__END_CDECLS diff --git a/oemcrypto/opk/ports/trusty/ta/shared/include/macros.h b/oemcrypto/opk/ports/trusty/ta/shared/include/macros.h new file mode 100644 index 0000000..0cba811 --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/shared/include/macros.h @@ -0,0 +1,35 @@ +/* + * Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine Master + * License Agreement. + */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +#define __BEGIN_CDECLS extern "C" { +#define __END_CDECLS } +#else +#define __BEGIN_CDECLS +#define __END_CDECLS +#endif + +#define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) + +#define countof(a) (sizeof(a) / sizeof((a)[0])) + +static inline WARN_UNUSED_RESULT bool align_overflow(size_t size, + size_t alignment, + size_t* aligned) { + if (size % alignment == 0) { + *aligned = size; + return false; + } + size_t temp = 0; + bool overflow = __builtin_add_overflow(size / alignment, 1, &temp); + overflow |= __builtin_mul_overflow(temp, alignment, aligned); + return overflow; +} diff --git a/oemcrypto/opk/ports/trusty/ta/shared/include/trusty_shared_memory.h b/oemcrypto/opk/ports/trusty/ta/shared/include/trusty_shared_memory.h new file mode 100644 index 0000000..9949108 --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/shared/include/trusty_shared_memory.h @@ -0,0 +1,19 @@ +/* + * Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine Master + * License Agreement. + */ + +#pragma once + +#include + +__BEGIN_CDECLS + +/* + * Set the shared memory buffer used by Widevine serialization code to transfer + * bulk data. + */ +void TEE_SharedMemory_Bind(uint8_t* buffer, size_t size); + +__END_CDECLS diff --git a/oemcrypto/opk/ports/trusty/ta/shared/include/widevine_ipc_protocol.h b/oemcrypto/opk/ports/trusty/ta/shared/include/widevine_ipc_protocol.h new file mode 100644 index 0000000..d4232ce --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/shared/include/widevine_ipc_protocol.h @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include + +__BEGIN_CDECLS + +#define WV_PORT_NAME "com.android.trusty.widevine" +#define WV_PROTOCOL_VERSION (0) + +/* + * Messages between non-secure and secure currently need to fit into a single + * page of memory. However, an additional header is prepended to the message. + * This means that the maximum size of a Trusty IPC message between the + * non-secure and secure worlds is slightly less than 4k. Currently the header + * is 16 bytes. To be cautious, double that size. + */ +#define __TRUSTY_NS_IPC_LIMIT (4096 - 32) +#define __MIN(A, B) ((A <= B) ? A : B) + +/* The size of the IPC buffers allocated by the Trusty app. */ +#define WV_IPC_BUFFER_SIZE (32) +static_assert(WV_IPC_BUFFER_SIZE <= 4096, "IPC buffer size cannot exceed 4k"); + +/* + * The number of bytes send via IPC may be limited for reasons other that buffer + * size. + */ +#define WV_MAX_IPC_MESSAGE_SIZE __MIN(WV_IPC_BUFFER_SIZE, __TRUSTY_NS_IPC_LIMIT) +static_assert(WV_MAX_IPC_MESSAGE_SIZE <= WV_IPC_BUFFER_SIZE, + "IPC message size limit exceeds buffer size"); +static_assert(WV_MAX_IPC_MESSAGE_SIZE <= __TRUSTY_NS_IPC_LIMIT, + "IPC message size limit exceeds Trusty's non-secure IPC limit"); + +/* + * If we see a buffer larger than this, assume something is wrong. + * Strictly speaking this should not be needed for either correctness or safety, + * but it provides another layer of defence to avoid overflows. + */ +#define WV_MAX_REASONABLE_BUFFER_SIZE (32 * 1024 * 1024) + +/* + * Size of shared memory region containing Widevine messages that must be copied + * into Trusty's address space for safety reasons. + */ +#define WV_MESSAGE_BUFFER_SIZE (32 * 1024) +static_assert(WV_MESSAGE_BUFFER_SIZE <= WV_MAX_REASONABLE_BUFFER_SIZE, + "Message buffer way too big"); + +/* + * Bulk data from a Widevine message that is kept in shared memory rather than + * copying it into Trusty's address space. + * TODO: fix the shared memory API so that this buffer size can be specified by + * liboemcrypto and the app makes no assumptions. + */ +#define WV_SHARED_BUFFER_SIZE (16 * 1024 * 1024) +static_assert(WV_SHARED_BUFFER_SIZE <= WV_MAX_REASONABLE_BUFFER_SIZE, + "Shared buffer way too big"); + +/* The maximum number of handles that can be sent with a single message. */ +#define WV_MAX_NUM_HANDLES (2) + +struct wv_ipc_header { + uint16_t tag; +}; + +#define WV_HEADER_SIZE (sizeof(struct wv_ipc_header)) +static_assert(WV_HEADER_SIZE <= WV_MAX_IPC_MESSAGE_SIZE, + "IPC header is too big"); + +#define WV_MAX_PAYLOAD_SIZE (WV_MAX_IPC_MESSAGE_SIZE - WV_HEADER_SIZE) + +enum wv_tag : uint16_t { + WV_TAG_ACK = 0u, + WV_TAG_BIND = 1u, + WV_TAG_WIDEVINE = 2u, +}; + +/** + * struct ack_message - a generic acknowlegement. + * + * Every message sent between the client and server is acknowledged somehow. + * This helps avoid losing messages sent from non-secure to secure when the + * channel's message buffers are all in use. In this case, messages may be + * dropped without notifying Android. Android does not inherently know if the + * buffers are full before sending a message. + * ACK is a generic response for situations where no other data is transmited. + * ACK effectively signals that there is an empty IPC buffer available. + */ +struct ack_message {}; +static_assert(sizeof(struct ack_message) <= WV_MAX_PAYLOAD_SIZE, + "ack_message size bigger than max payload size"); + +/** + * struct bind_message - initialize the connection and bind shared buffers + * @protocol_version: the version of the protocol that the client speaks. + * @message_buffer_size: size of the shared buffer for message transfer. + * @shared_buffer_size: size of the shared buffer for bulk data transfer. + * + * Sent from Android to Trusty. + * Must be sent before sending Widevine messages. + * Acknowledged with an ACK. + * + * handle 0 is the message buffer. + * handle 1 is the shared buffer. + */ +struct bind_message { + uint32_t protocol_version; + uint32_t message_buffer_size; + uint32_t shared_buffer_size; +}; +static_assert(sizeof(struct bind_message) <= WV_MAX_PAYLOAD_SIZE, + "bind_message size bigger than max payload size"); + +/** + * struct widevine_message - a message corresponding to the OEMCrypto API + * @message_size: The size of the message that has been written into the + * message buffer. + * + * Android sends an initial WIDEVINE message and Trusty responds with a + * WIDEVINE message. + * + * handles are secure output buffers for copy or decryption operations. + */ +struct widevine_message { + uint32_t message_size; +}; +static_assert(sizeof(struct widevine_message) <= WV_MAX_PAYLOAD_SIZE, + "widevine_message size bigger than max payload size"); + +/** + * tag_name() - human readable name for a message type + */ +const char* tag_name(uint16_t tag); + +/** + * validate_message_metadata() - check if incoming message may be valid. + * @tag: incoming message type. + * @num_bytes: incoming message size. + * @num_handles: number of handles sent with the message. + * + * Note this function rejects obviously out of range values and does not + * provide hard guarentees if validity depends on the contents of the message. + * For example, the number of handles for a WIDEVINE message may depend on how + * many secure buffers are being passed into the API. + */ +int validate_message_metadata(uint16_t tag, + size_t num_bytes, + size_t num_handles) WARN_UNUSED_RESULT; + +__END_CDECLS diff --git a/oemcrypto/opk/ports/trusty/ta/shared/shared_memory.c b/oemcrypto/opk/ports/trusty/ta/shared/shared_memory.c new file mode 100644 index 0000000..dc42934 --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/shared/shared_memory.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static uint8_t* pool; +static size_t pool_size; + +void TEE_SharedMemory_Bind(uint8_t* buffer, size_t size) { + pool = buffer; + pool_size = size; +} + +/* + * Initialize shared memory pool + */ +bool TOS_SharedMemory_Initialize(void) { + return pool != NULL && pool_size != 0; +} + +/* + * Allocate a region of shared memory of |size| bytes. Regions are allocated + * based on |region_id| that is used to correlate remote and local mappings. + * The region must remain mapped until released by TOS_SharedMemory_Release. + * + * Parameters: + * size - the size of the memory region to allocate + * + * Returns: + * A handle to the shared memory, or NULL if allocation fails + */ +bool TOS_SharedMemory_Allocate(size_t size) { + return pool != NULL && size <= pool_size; +} + +/* + * Release the shared memory block that was previously allocated + * by TOS_SharedMemory_Allocate. + */ +void TOS_SharedMemory_Release(void) { + pool = NULL; + pool_size = 0; +} + +/* + * Return the address that a shared memory handle is mapped to. + * + * Returns: + * The address of the shared memory region refered by |handle| + */ +uint8_t* TOS_SharedMemory_GetAddress() { + return pool; +} + +/* + * Return the size of a shared memory region + * + * Returns: + * the size of the shared memory region + */ +size_t TOS_SharedMemory_GetSize() { + return pool_size; +} + +/* + * Copy data into and/or out of the memory block + * + * This function is called after all shared buffers have been + * allocated, to sync REE non-shared memory with shared memory. + * + * Two vectors of bytes ranges to copy are provided. Each element in + * the |in_vec| vector identifies a range of bytes to be copied from + * the non-shared address to an offset in the shared memory + * block. Each element in the |out_vec| vector identies a range of + * bytes to be copied from an offset in the shared memory block to the + * non-shared memory address. + * + * In the TEE all operations on shared memory directly address + * the shared memory blocks so copy-in/copy-out are never required. + * This function will never be called with nonzero in_count or out_count + * in the TEE. + * + * Parameters: + * in_vec - a vector of copy operations describing the ranges + * of bytes to be copied in + * in_vec_count - the number of elements in the in_vec array + * out_vec - a vector of copy operations describing the + * ranges of bytes to be copied out + * out_vec_count - the number of elements in the out_vec array + */ +void TOS_SharedMemory_Finalize(OPK_SharedMemory_CopyVector* in_vec, + size_t in_count, + OPK_SharedMemory_CopyVector* out_vec, + size_t out_count) { + for (size_t i = 0; i < in_count; i++) { + if (in_vec[i].address) { + memcpy(pool + in_vec[i].offset, in_vec[i].address, + in_vec[i].length); + } + } + for (size_t i = 0; i < out_count; i++) { + if (out_vec[i].address) { + memcpy(out_vec[i].address, pool + out_vec[i].offset, + out_vec[i].length); + } + } +} + +/* + * An OPK TEE implementation must specify the amount of shared memory + * it can make available for passing data in shared memory blocks. + * The shared memory block must be large enough to accomodate the + * minimum sample size required by the OEMCrypto Resource Rating Tier. + * The block size should be 2 * Minimum_Sample_Size, plus 32 KiB for + * various parameters that are annotated as OEMCrypto_SharedMemory + * in OEMCryptoCENC.h. + */ +size_t TOS_SharedMemory_AvailableSize(void) { + return pool_size; +} diff --git a/oemcrypto/opk/ports/trusty/ta/shared/widevine_ipc_protocol.c b/oemcrypto/opk/ports/trusty/ta/shared/widevine_ipc_protocol.c new file mode 100644 index 0000000..c5661f1 --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/shared/widevine_ipc_protocol.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define TLOG_TAG "widevine_ipc_protocol" + +#include + +#include + +const char* tag_name(uint16_t tag) { + switch (tag) { + case WV_TAG_ACK: + return "ACK"; + case WV_TAG_BIND: + return "BIND"; + case WV_TAG_WIDEVINE: + return "WIDEVINE"; + default: + return "UNKNOWN"; + } +} + +struct message_info { + int unknown; + size_t exact_bytes; + size_t min_handles; + size_t max_handles; +}; + +static struct message_info get_message_info(uint16_t tag) { + switch (tag) { + case WV_TAG_ACK: + return (struct message_info){ + .unknown = 0, + .exact_bytes = sizeof(struct ack_message), + .min_handles = 0, + .max_handles = 0, + }; + case WV_TAG_BIND: + return (struct message_info){ + .unknown = 0, + .exact_bytes = sizeof(struct bind_message), + .min_handles = 2, + .max_handles = 2, + }; + case WV_TAG_WIDEVINE: + return (struct message_info){ + .unknown = 0, + .exact_bytes = sizeof(struct widevine_message), + .min_handles = 0, + .max_handles = WV_MAX_NUM_HANDLES, + }; + default: + return (struct message_info){ + .unknown = 1, + .exact_bytes = 0, + .min_handles = 0, + .max_handles = 0, + }; + } +} + +int validate_message_metadata(uint16_t tag, + size_t num_bytes, + size_t num_handles) { + struct message_info info = get_message_info(tag); + if (info.unknown != 0) { + TLOGE("Unknown tag (%d)\n", tag); + return -1; + } + if (num_bytes != info.exact_bytes) { + TLOGE("Incorrect number of bytes (%zu) for %s message.\n", num_bytes, + tag_name(tag)); + return -1; + } + if (num_handles < info.min_handles || num_handles > info.max_handles) { + TLOGE("Incorrect number of handles (%zu) for %s message.\n", + num_handles, tag_name(tag)); + return -1; + } + return 0; +} diff --git a/oemcrypto/opk/ports/trusty/ta/system_specific.c b/oemcrypto/opk/ports/trusty/ta/system_specific.c new file mode 100644 index 0000000..76bfd86 --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/system_specific.c @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#define TLOG_TAG "widevine" + +// Any system-specific functionality to validate the ipc buffer +int system_specific_validate_ipc_buffer(int handle) { return 0; } + +// Any system-specific functionality to validate the secure buffer +int system_specific_validate_secure_output_buffer(int handle) { return 0; } diff --git a/oemcrypto/opk/ports/trusty/ta/tee_context.c b/oemcrypto/opk/ports/trusty/ta/tee_context.c new file mode 100644 index 0000000..066b3ca --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/tee_context.c @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/tee_context.h" + +static tee_context ctx = {STORAGE_INVALID_SESSION, STORAGE_INVALID_SESSION}; + +tee_context* get_tee_context() { + return &ctx; +} diff --git a/oemcrypto/opk/ports/trusty/ta/widevine_app.c b/oemcrypto/opk/ports/trusty/ta/widevine_app.c new file mode 100644 index 0000000..201b148 --- /dev/null +++ b/oemcrypto/opk/ports/trusty/ta/widevine_app.c @@ -0,0 +1,496 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define TLOG_TAG "widevine" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "secure_buffer.h" + +/* Only live for the duration of a request, so it can be global. */ +uint8_t message_buffer[WV_MESSAGE_BUFFER_SIZE]; +ODK_Message request; + +/* + * Close all valid handles in an array. + * It must be safe to call this function multiple times on the same array. + */ +static void close_handles(handle_t* handles, size_t num_handles) { + assert(handles != NULL || num_handles == 0); + for (size_t i = 0; i < num_handles; i++) { + if (handles[i] >= 0) { + close(handles[i]); + handles[i] = -1; + } + } +} + +static struct wv_ipc_header create_header(uint16_t tag) { + return (struct wv_ipc_header){.tag = tag}; +} + +static int send_tagged(handle_t chan, + struct wv_ipc_header header, + void* data, + size_t num_bytes) { + TLOGD("Server send %d %d - %zu bytes\n", chan, header.tag, num_bytes); + + if (num_bytes > WV_MAX_PAYLOAD_SIZE) { + TLOGE("Message is too big (%zu)\n", num_bytes); + return -1; + } + + struct iovec iov[2] = { + { + .iov_base = &header, + .iov_len = WV_HEADER_SIZE, + }, + { + .iov_base = data, + .iov_len = num_bytes, + }, + }; + + ipc_msg_t msg = { + .num_iov = countof(iov), + .iov = iov, + .num_handles = 0, + .handles = NULL, + }; + + ssize_t rc = send_msg(chan, &msg); + if (rc < 0) { + TLOGE("Error sending message (%zd)\n", rc); + return -1; + } + if ((size_t)rc != WV_HEADER_SIZE + num_bytes) { + TLOGE("Did not send complete message (%zd)\n", rc); + return -1; + } + return 0; +} + +static int receive_tagged(handle_t chan, + struct wv_ipc_header* header, + uint8_t* data, + size_t* num_bytes, + handle_t* handles, + size_t* num_handles) { + struct iovec iov[2] = { + { + .iov_base = header, + .iov_len = WV_HEADER_SIZE, + }, + { + .iov_base = data, + .iov_len = *num_bytes, + }, + }; + ipc_msg_t msg = { + .num_iov = countof(iov), + .iov = iov, + .num_handles = *num_handles, + .handles = handles, + }; + + /* Check the message info. */ + ipc_msg_info_t info; + ssize_t rc = get_msg(chan, &info); + if (rc < 0) { + TLOGE("get_msg failed %d\n", chan); + goto on_early_error; + } + + /* Check we have enough space to read the message. */ + if (info.len > WV_HEADER_SIZE + *num_bytes) { + TLOGE("Buffer too small %d %zu\n", chan, info.len); + rc = -1; + goto on_post_get_error; + } + if (info.num_handles > *num_handles) { + TLOGE("Too many handles %d %u\n", chan, info.num_handles); + rc = -1; + goto on_post_get_error; + } + + /* Copy the message data. */ + rc = read_msg(chan, info.id, 0, &msg); + if (rc < 0) { + TLOGE("read_msg failed %d\n", chan); + goto on_post_get_error; + } + if ((size_t)rc < WV_HEADER_SIZE) { + TLOGE("Message too small to contain header\n"); + goto on_post_read_error; + } + + *num_bytes = (size_t)rc - WV_HEADER_SIZE; + *num_handles = info.num_handles; + put_msg(chan, info.id); + TLOGD("Server receive %d %d - %zd bytes\n", chan, header->tag, rc); + return 0; + +on_post_read_error: + close_handles(handles, info.num_handles); +on_post_get_error: + put_msg(chan, info.id); +on_early_error: + return -1; +} + +struct widevine_ctx { + void* shared_message_addr; + size_t shared_message_size; + + void* shared_buffer_addr; + size_t shared_buffer_size; +}; + +static bool is_bound(struct widevine_ctx* ctx) { + return ctx->shared_message_addr != NULL && ctx->shared_buffer_addr != NULL; +} + +static void close_shared_memory(struct widevine_ctx* ctx) { + TEE_SharedMemory_Bind(NULL, 0); + if (ctx->shared_buffer_addr != NULL) { + munmap(ctx->shared_buffer_addr, ctx->shared_buffer_size); + ctx->shared_buffer_addr = NULL; + ctx->shared_buffer_size = 0; + } + if (ctx->shared_message_addr != NULL) { + munmap(ctx->shared_message_addr, ctx->shared_message_size); + ctx->shared_message_addr = NULL; + ctx->shared_message_size = 0; + } +} + +static void bind_shared_memory(struct widevine_ctx* ctx, + void* shared_message_addr, + size_t shared_message_size, + void* shared_buffer_addr, + size_t shared_buffer_size) { + close_shared_memory(ctx); + + ctx->shared_message_addr = shared_message_addr; + ctx->shared_message_size = shared_message_size; + + ctx->shared_buffer_addr = shared_buffer_addr; + ctx->shared_buffer_size = shared_buffer_size; + + TEE_SharedMemory_Bind(shared_buffer_addr, shared_buffer_size); +} + +static int on_widevine_connect(const struct tipc_port* port, + handle_t chan, + const struct uuid* peer, + void** ctx_p) { + struct widevine_ctx* ctx = calloc(1, sizeof(*ctx)); + if (ctx == NULL) { + TLOGE("Failed to allocate connection context.\n"); + return ERR_NO_MEMORY; + } + + *ctx_p = ctx; + return NO_ERROR; +} + +static void on_widevine_channel_cleanup(void* _ctx) { + struct widevine_ctx* ctx = (struct widevine_ctx*)_ctx; + close_shared_memory(ctx); + free(ctx); +} + +static int validate_buffer_sizes(struct bind_message* msg) { + size_t page_size = getauxval(AT_PAGESZ); + + size_t shared_message_size = msg->message_buffer_size; + if (shared_message_size % page_size != 0) { + TLOGE("Message buffer is not page aligned. (%zu)\n", + shared_message_size); + return -1; + } + /* + * The message is copied into a preallocated buffer. Check that the shared + * memory region is the same size as the preallocated buffer. If the memory + * region is larger than the preallocated buffer, the client may see dynamic + * failures if it tries to pass large messages. Similarly, if the memory + * region is smaller the server may fail to copy out the message. Make sure + * the buffers have exactly the same size. + */ + if (shared_message_size != ODK_Message_GetCapacity(&request)) { + TLOGE("Message buffer is wrong size. (%zu/%zu)\n", shared_message_size, + ODK_Message_GetCapacity(&request)); + return -1; + } + + size_t shared_buffer_size = msg->shared_buffer_size; + if (shared_buffer_size % page_size != 0) { + TLOGE("Shared buffer is not page aligned. (%zu)\n", shared_buffer_size); + return -1; + } + if (shared_buffer_size > WV_MAX_REASONABLE_BUFFER_SIZE) { + TLOGE("Shared buffer is unexpectedly large. (%zu)\n", + shared_buffer_size); + return -1; + } + + return 0; +} + +static int dispatch_bind_message(handle_t chan, + struct widevine_ctx* ctx, + void* bytes, + size_t num_bytes, + handle_t* handles, + size_t num_handles) { + struct bind_message* msg = (struct bind_message*)bytes; + + if (msg->protocol_version != WV_PROTOCOL_VERSION) { + TLOGE("Received protocol version %u, expected %u.\n", + msg->protocol_version, WV_PROTOCOL_VERSION); + return -1; + } + + if (validate_buffer_sizes(msg) != 0) { + return -1; + } + + /* Validate that the buffers are safe to use for IPC. */ + if (system_specific_validate_ipc_buffer(handles[0]) != 0) { + return -1; + } + if (system_specific_validate_ipc_buffer(handles[1]) != 0) { + return -1; + } + + /* Bind the shared message buffer. */ + size_t shared_message_size = msg->message_buffer_size; + void* shared_message_addr = mmap(0, shared_message_size, + PROT_READ | PROT_WRITE, 0, handles[0], 0); + if (shared_message_addr == MAP_FAILED) { + TLOGE("Could not map message buffer.\n"); + return -1; + } + + /* Bind the bulk shared memory buffer. */ + size_t shared_buffer_size = msg->shared_buffer_size; + void* shared_buffer_addr = mmap(0, shared_buffer_size, + PROT_READ | PROT_WRITE, 0, handles[1], 0); + if (shared_buffer_addr == MAP_FAILED) { + TLOGE("Could not map shared buffer.\n"); + munmap(shared_message_addr, shared_message_size); + return -1; + } + + bind_shared_memory(ctx, shared_message_addr, shared_message_size, + shared_buffer_addr, shared_buffer_size); + + /* OPK_Initialize needs to be called after the shared memory is bound, not before */ + OPK_Initialize(); + + /* ACK */ + return send_tagged(chan, create_header(WV_TAG_ACK), NULL, 0); +} + +static int dispatch_widevine_message(handle_t chan, + struct widevine_ctx* ctx, + void* bytes, + size_t num_bytes, + handle_t* handles, + size_t num_handles) { + struct widevine_message* msg = (struct widevine_message*)bytes; + + /* + * Copy in the message. + * The message must be copied in to prevent the client from attacking the + * sever by mutating memory as it is read. That can result in subtle time of + * check / time of use bugs. + */ + size_t sz = msg->message_size; + if (sz > ctx->shared_message_size || sz > WV_MESSAGE_BUFFER_SIZE) { + TLOGE("Message size exceeds shared memory size. %zu > %zu\n", sz, + ctx->shared_message_size); + return -1; + } + if (sz > ODK_Message_GetCapacity(&request)) { + TLOGE("Message size exceeds internal buffer size. %zu > %zu\n", sz, + ODK_Message_GetCapacity(&request)); + return -1; + } + ODK_Message_Reset(&request); + ODK_Message_SetSize(&request, sz); + memcpy(ODK_Message_GetBase(&request), ctx->shared_message_addr, sz); + + /* Transfer ownership of the handles to the Widevine code. */ + for (size_t i = 0; i < num_handles; i++) { + if (register_secure_buffer(handles[i]) < 0) { + cleanup_secure_buffers(); + return -1; + } + handles[i] = -1; + } + + /* Execute the request. */ + ODK_Message response; + ODK_MessageStatus message_status = OPK_DispatchMessage(&request, &response); + cleanup_secure_buffers(); + if (message_status != MESSAGE_STATUS_OK) { + TLOGE("Error dispatching request message, status=%d\n", message_status); + /* TODO what should we be doing here? */ + } + + /* Copy out the response. */ + /* TODO synthesize an OOM message if needed. */ + size_t response_size = 0; + response_size = ODK_Message_GetSize(&response); + if (response_size > ctx->shared_message_size) { + TLOGE("Response too large.\n"); + response_size = 0; + } + memcpy(ctx->shared_message_addr, ODK_Message_GetBase(&response), + response_size); + TOS_Transport_ReleaseMessage(&response); + + /* Notify the client of the response. */ + struct widevine_message response_msg = { + .message_size = (uint32_t)response_size, + }; + return send_tagged(chan, create_header(WV_TAG_WIDEVINE), &response_msg, + sizeof(response_msg)); +} + +static int dispatch_message(handle_t chan, + struct widevine_ctx* ctx, + uint16_t tag, + void* bytes, + size_t num_bytes, + handle_t* handles, + size_t num_handles) { + switch (tag) { + case WV_TAG_WIDEVINE: + if (is_bound(ctx)) { + return dispatch_widevine_message(chan, ctx, bytes, num_bytes, + handles, num_handles); + } else { + TLOGE("Unexpected %s message while unbound\n", tag_name(tag)); + return -1; + } + case WV_TAG_BIND: + if (is_bound(ctx)) { + TLOGE("Unexpected %s message while bound\n", tag_name(tag)); + return -1; + } else { + return dispatch_bind_message(chan, ctx, bytes, num_bytes, handles, + num_handles); + } + default: + TLOGE("Unexpected %s message\n", tag_name(tag)); + return -1; + } +} + +static int on_widevine_message(const struct tipc_port* port, + handle_t chan, + void* _ctx) { + struct widevine_ctx* ctx = (struct widevine_ctx*)_ctx; + + /* Receive the IPC data. */ + struct wv_ipc_header header; + uint8_t ipc_buffer[WV_IPC_BUFFER_SIZE]; + handle_t handles[WV_MAX_NUM_HANDLES]; + size_t num_bytes = sizeof(ipc_buffer); + size_t num_handles = countof(handles); + int rc = receive_tagged(chan, &header, ipc_buffer, &num_bytes, handles, + &num_handles); + if (rc != 0) { + return -1; + } + + if (validate_message_metadata(header.tag, num_bytes, num_handles) != 0) { + close_handles(handles, num_handles); + return -1; + } + + rc = dispatch_message(chan, ctx, header.tag, ipc_buffer, num_bytes, handles, + num_handles); + /* + * Make sure the handles never leak. If you need to take ownership, + * replace the handle in the array with -1. + */ + close_handles(handles, num_handles); + return rc; +} + +int main(void) { + int rc; + struct tipc_hset* hset; + + /* Service configuration */ + static struct tipc_port_acl acl = { + .flags = IPC_PORT_ALLOW_NS_CONNECT, + }; + static struct tipc_port port = { + .name = WV_PORT_NAME, + .msg_max_size = WV_IPC_BUFFER_SIZE, + .msg_queue_len = 1, + .acl = &acl, + }; + static struct tipc_srv_ops ops = { + .on_connect = on_widevine_connect, + .on_message = on_widevine_message, + .on_channel_cleanup = on_widevine_channel_cleanup, + }; + + request = ODK_Message_Create(message_buffer, sizeof(message_buffer)); + + /* Create event loop */ + hset = tipc_hset_create(); + if (IS_ERR(hset)) { + TLOGE("failed (%d) to create handle set\n", PTR_ERR(hset)); + return PTR_ERR(hset); + } + + /* Initialize event loop */ + rc = tipc_add_service(hset, &port, 1, 1, &ops); + if (rc != NO_ERROR) { + TLOGE("failed (%d) to initialize widevine service\n", rc); + return rc; + } + + /* Run event loop */ + return tipc_run_event_loop(hset); +} diff --git a/oemcrypto/opk/serialization/common/GEN_common_serializer.c b/oemcrypto/opk/serialization/common/GEN_common_serializer.c new file mode 100644 index 0000000..7956e1b --- /dev/null +++ b/oemcrypto/opk/serialization/common/GEN_common_serializer.c @@ -0,0 +1,705 @@ +/* + * Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine License + * Agreement. + */ + +/* + * This code is auto-generated, do not edit + */ +#include "GEN_common_serializer.h" + +#include + +#include "GEN_common_serializer.h" +#include "bump_allocator.h" +#include "common_special_cases.h" +#include "log_macros.h" +#include "marshaller_base.h" +#include "odk_overflow.h" +#include "opk_serialization_base.h" +#include "shared_buffer_allocator.h" +#include "tos_transport_interface.h" + +bool SuccessResult(OEMCryptoResult result) { + switch (result) { + case OEMCrypto_SUCCESS: + case OEMCrypto_WARNING_GENERATION_SKEW: + case OEMCrypto_LOCAL_DISPLAY_ONLY: + return true; + default: + return false; + } +} +bool Is_Valid_OEMCryptoResult(uint32_t value) { + switch (value) { + case 0: /* OEMCrypto_SUCCESS */ + case 1: /* OEMCrypto_ERROR_INIT_FAILED */ + case 2: /* OEMCrypto_ERROR_TERMINATE_FAILED */ + case 3: /* OEMCrypto_ERROR_OPEN_FAILURE */ + case 4: /* OEMCrypto_ERROR_CLOSE_FAILURE */ + case 5: /* OEMCrypto_ERROR_ENTER_SECURE_PLAYBACK_FAILED */ + case 6: /* OEMCrypto_ERROR_EXIT_SECURE_PLAYBACK_FAILED */ + case 7: /* OEMCrypto_ERROR_SHORT_BUFFER */ + case 8: /* OEMCrypto_ERROR_NO_DEVICE_KEY */ + case 9: /* OEMCrypto_ERROR_NO_ASSET_KEY */ + case 10: /* OEMCrypto_ERROR_KEYBOX_INVALID */ + case 11: /* OEMCrypto_ERROR_NO_KEYDATA */ + case 12: /* OEMCrypto_ERROR_NO_CW */ + case 13: /* OEMCrypto_ERROR_DECRYPT_FAILED */ + case 14: /* OEMCrypto_ERROR_WRITE_KEYBOX */ + case 15: /* OEMCrypto_ERROR_WRAP_KEYBOX */ + case 16: /* OEMCrypto_ERROR_BAD_MAGIC */ + case 17: /* OEMCrypto_ERROR_BAD_CRC */ + case 18: /* OEMCrypto_ERROR_NO_DEVICEID */ + case 19: /* OEMCrypto_ERROR_RNG_FAILED */ + case 20: /* OEMCrypto_ERROR_RNG_NOT_SUPPORTED */ + case 21: /* OEMCrypto_ERROR_SETUP */ + case 22: /* OEMCrypto_ERROR_OPEN_SESSION_FAILED */ + case 23: /* OEMCrypto_ERROR_CLOSE_SESSION_FAILED */ + case 24: /* OEMCrypto_ERROR_INVALID_SESSION */ + case 25: /* OEMCrypto_ERROR_NOT_IMPLEMENTED */ + case 26: /* OEMCrypto_ERROR_NO_CONTENT_KEY */ + case 27: /* OEMCrypto_ERROR_CONTROL_INVALID */ + case 28: /* OEMCrypto_ERROR_UNKNOWN_FAILURE */ + case 29: /* OEMCrypto_ERROR_INVALID_CONTEXT */ + case 30: /* OEMCrypto_ERROR_SIGNATURE_FAILURE */ + case 31: /* OEMCrypto_ERROR_TOO_MANY_SESSIONS */ + case 32: /* OEMCrypto_ERROR_INVALID_NONCE */ + case 33: /* OEMCrypto_ERROR_TOO_MANY_KEYS */ + case 34: /* OEMCrypto_ERROR_DEVICE_NOT_RSA_PROVISIONED */ + case 35: /* OEMCrypto_ERROR_INVALID_RSA_KEY */ + case 36: /* OEMCrypto_ERROR_KEY_EXPIRED */ + case 37: /* OEMCrypto_ERROR_INSUFFICIENT_RESOURCES */ + case 38: /* OEMCrypto_ERROR_INSUFFICIENT_HDCP */ + case 39: /* OEMCrypto_ERROR_BUFFER_TOO_LARGE */ + case 40: /* OEMCrypto_WARNING_GENERATION_SKEW */ + case 41: /* OEMCrypto_ERROR_GENERATION_SKEW */ + case 42: /* OEMCrypto_LOCAL_DISPLAY_ONLY */ + case 43: /* OEMCrypto_ERROR_ANALOG_OUTPUT */ + case 44: /* OEMCrypto_ERROR_WRONG_PST */ + case 45: /* OEMCrypto_ERROR_WRONG_KEYS */ + case 46: /* OEMCrypto_ERROR_MISSING_MASTER */ + case 47: /* OEMCrypto_ERROR_LICENSE_INACTIVE */ + case 48: /* OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE */ + case 49: /* OEMCrypto_ERROR_ENTRY_IN_USE */ + case 50: /* OEMCrypto_ERROR_USAGE_TABLE_UNRECOVERABLE */ + case 51: /* OEMCrypto_KEY_NOT_LOADED */ + case 52: /* OEMCrypto_KEY_NOT_ENTITLED */ + case 53: /* OEMCrypto_ERROR_BAD_HASH */ + case 54: /* OEMCrypto_ERROR_OUTPUT_TOO_LARGE */ + case 55: /* OEMCrypto_ERROR_SESSION_LOST_STATE */ + case 56: /* OEMCrypto_ERROR_SYSTEM_INVALIDATED */ + case 57: /* OEMCrypto_ERROR_LICENSE_RELOAD */ + case 58: /* OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES */ + case 59: /* OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION */ + case 60: /* OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION */ + case 61: /* OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING */ + case 62: /* OEMCrypto_ERROR_UNSUPPORTED_CIPHER */ + case 63: /* OEMCrypto_ERROR_DVR_FORBIDDEN */ + case 64: /* OEMCrypto_ERROR_INSUFFICIENT_PRIVILEGE */ + case 65: /* OEMCrypto_ERROR_INVALID_KEY */ + case 1000: /* ODK_ERROR_BASE */ + case 1001: /* ODK_SET_TIMER */ + case 1002: /* ODK_DISABLE_TIMER */ + case 1003: /* ODK_TIMER_EXPIRED */ + case 1004: /* ODK_UNSUPPORTED_API */ + case 1005: /* ODK_STALE_RENEWAL */ + case 2000: /* OPK_ERROR_BASE */ + case 2001: /* OPK_ERROR_INCOMPATIBLE_VERSION */ + case 2002: /* OPK_ERROR_NO_PERSISTENT_DATA */ + return true; + default: + return false; + } +} + +bool Is_Valid_OEMCrypto_Usage_Entry_Status(uint32_t value) { + switch (value) { + case 0: /* kUnused */ + case 1: /* kActive */ + case 2: /* kInactive */ + case 3: /* kInactiveUsed */ + case 4: /* kInactiveUnused */ + return true; + default: + return false; + } +} + +bool Is_Valid_OEMCrypto_LicenseType(uint32_t value) { + switch (value) { + case 0: /* OEMCrypto_ContentLicense */ + case 1: /* OEMCrypto_EntitlementLicense */ + return true; + default: + return false; + } +} + +bool Is_Valid_OEMCrypto_PrivateKeyType(uint32_t value) { + switch (value) { + case 0: /* OEMCrypto_RSA_Private_Key */ + case 1: /* OEMCrypto_ECC_Private_Key */ + return true; + default: + return false; + } +} + +bool Is_Valid_OEMCryptoBufferType(uint32_t value) { + switch (value) { + case 0: /* OEMCrypto_BufferType_Clear */ + case 1: /* OEMCrypto_BufferType_Secure */ + case 2: /* OEMCrypto_BufferType_Direct */ + return true; + default: + return false; + } +} + +bool Is_Valid_OEMCryptoCipherMode(uint32_t value) { + switch (value) { + case 0: /* OEMCrypto_CipherMode_CENC */ + case 1: /* OEMCrypto_CipherMode_CBCS */ + case 2: /* OEMCrypto_CipherMode_CTR */ + case 3: /* OEMCrypto_CipherMode_CBC */ + case 4: /* OEMCrypto_CipherMode_CSA2 */ + case 5: /* OEMCrypto_CipherMode_CSA3 */ + case 6: /* OEMCrypto_CipherMode_OFB */ + case 7: /* OEMCrypto_CipherMode_SCTE */ + case 8: /* OEMCrypto_CipherMode_ECB */ + return true; + default: + return false; + } +} + +bool Is_Valid_OEMCrypto_Algorithm(uint32_t value) { + switch (value) { + case 0: /* OEMCrypto_AES_CBC_128_NO_PADDING */ + case 1: /* OEMCrypto_HMAC_SHA256 */ + return true; + default: + return false; + } +} + +bool Is_Valid_OEMCrypto_Clock_Security_Level(uint32_t value) { + switch (value) { + case 0: /* kInsecureClock */ + case 1: /* kMonotonicClock */ + case 2: /* kSecureClock */ + case 3: /* kHardwareSecureClock */ + return true; + default: + return false; + } +} + +bool Is_Valid_OEMCrypto_HDCP_Capability(uint32_t value) { + switch (value) { + case 0: /* HDCP_NONE */ + case 1: /* HDCP_V1 */ + case 2: /* HDCP_V2 */ + case 3: /* HDCP_V2_1 */ + case 4: /* HDCP_V2_2 */ + case 5: /* HDCP_V2_3 */ + case 6: /* HDCP_V1_0 */ + case 7: /* HDCP_V1_1 */ + case 8: /* HDCP_V1_2 */ + case 9: /* HDCP_V1_3 */ + case 10: /* HDCP_V1_4 */ + case 255: /* HDCP_NO_DIGITAL_OUTPUT */ + return true; + default: + return false; + } +} + +bool Is_Valid_OEMCrypto_DTCP2_Capability(uint32_t value) { + switch (value) { + case 0: /* OEMCrypto_NO_DTCP2 */ + case 1: /* OEMCrypto_DTCP2_V1 */ + return true; + default: + return false; + } +} + +bool Is_Valid_OEMCrypto_ProvisioningMethod(uint32_t value) { + switch (value) { + case 0: /* OEMCrypto_ProvisioningError */ + case 1: /* OEMCrypto_DrmCertificate */ + case 2: /* OEMCrypto_Keybox */ + case 3: /* OEMCrypto_OEMCertificate */ + case 4: /* OEMCrypto_BootCertificateChain */ + return true; + default: + return false; + } +} + +bool Is_Valid_OEMCrypto_WatermarkingSupport(uint32_t value) { + switch (value) { + case 0: /* OEMCrypto_WatermarkingError */ + case 1: /* OEMCrypto_WatermarkingNotSupported */ + case 2: /* OEMCrypto_WatermarkingConfigurable */ + case 3: /* OEMCrypto_WatermarkingAlwaysOn */ + return true; + default: + return false; + } +} + +bool Is_Valid_OEMCrypto_IdleState(uint32_t value) { + switch (value) { + case 0: /* OEMCrypto_NoCryptoActivity */ + case 1: /* OEMCrypto_CpuSuspend */ + return true; + default: + return false; + } +} + +bool Is_Valid_OEMCrypto_Security_Level(uint32_t value) { + switch (value) { + case 0: /* OEMCrypto_Level_Unknown */ + case 1: /* OEMCrypto_Level1 */ + case 2: /* OEMCrypto_Level2 */ + case 3: /* OEMCrypto_Level3 */ + return true; + default: + return false; + } +} + +void OPK_Pack_OEMCrypto_Substring(ODK_Message* msg, + OEMCrypto_Substring const* obj) { + OPK_Pack_size_t(msg, (const size_t*)&obj->offset); + OPK_Pack_size_t(msg, (const size_t*)&obj->length); +} + +void OPK_Pack_OEMCrypto_DTCP2_CMI_Descriptor_0( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_0 const* obj) { + OPK_Pack_uint8_t(msg, (const uint8_t*)&obj->id); + OPK_Pack_uint8_t(msg, (const uint8_t*)&obj->extension); + OPK_Pack_uint16_t(msg, (const uint16_t*)&obj->length); + OPK_Pack_uint8_t(msg, (const uint8_t*)&obj->data); +} + +void OPK_Pack_OEMCrypto_DTCP2_CMI_Descriptor_1( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_1 const* obj) { + OPK_Pack_uint8_t(msg, (const uint8_t*)&obj->id); + OPK_Pack_uint8_t(msg, (const uint8_t*)&obj->extension); + OPK_Pack_uint16_t(msg, (const uint16_t*)&obj->length); + OPK_PackArray(msg, (const uint8_t*)&obj->data[0], 3); +} + +void OPK_Pack_OEMCrypto_DTCP2_CMI_Descriptor_2( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_2 const* obj) { + OPK_Pack_uint8_t(msg, (const uint8_t*)&obj->id); + OPK_Pack_uint8_t(msg, (const uint8_t*)&obj->extension); + OPK_Pack_uint16_t(msg, (const uint16_t*)&obj->length); + OPK_PackArray(msg, (const uint8_t*)&obj->data[0], 3); +} + +void OPK_Pack_OEMCrypto_DTCP2_CMI_Packet( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Packet const* obj) { + OPK_Pack_uint8_t(msg, (const uint8_t*)&obj->dtcp2_required); + OPK_Pack_OEMCrypto_DTCP2_CMI_Descriptor_0( + msg, (const OEMCrypto_DTCP2_CMI_Descriptor_0*)&obj->cmi_descriptor_0); + OPK_Pack_OEMCrypto_DTCP2_CMI_Descriptor_1( + msg, (const OEMCrypto_DTCP2_CMI_Descriptor_1*)&obj->cmi_descriptor_1); + OPK_Pack_OEMCrypto_DTCP2_CMI_Descriptor_2( + msg, (const OEMCrypto_DTCP2_CMI_Descriptor_2*)&obj->cmi_descriptor_2); +} + +void OPK_Pack_OEMCrypto_KeyObject(ODK_Message* msg, + OEMCrypto_KeyObject const* obj) { + OPK_Pack_OEMCrypto_Substring(msg, (const OEMCrypto_Substring*)&obj->key_id); + OPK_Pack_OEMCrypto_Substring(msg, + (const OEMCrypto_Substring*)&obj->key_data_iv); + OPK_Pack_OEMCrypto_Substring(msg, (const OEMCrypto_Substring*)&obj->key_data); + OPK_Pack_OEMCrypto_Substring( + msg, (const OEMCrypto_Substring*)&obj->key_control_iv); + OPK_Pack_OEMCrypto_Substring(msg, + (const OEMCrypto_Substring*)&obj->key_control); +} + +void OPK_Pack_OEMCrypto_InputOutputPair(ODK_Message* msg, + OEMCrypto_InputOutputPair const* obj) { + OPK_Pack_size_t(msg, (const size_t*)&obj->input_data_length); + OPK_PackSharedBuffer(msg, obj->input_data, + OPK_ToLengthType(obj->input_data_length), /* map */ true, + /* copy_in */ true, /* is_output */ false); + OPK_Pack_OEMCrypto_DestBufferDesc( + msg, (const OEMCrypto_DestBufferDesc*)&obj->output_descriptor); +} + +void OPK_Pack_OEMCrypto_SubSampleDescription( + ODK_Message* msg, OEMCrypto_SubSampleDescription const* obj) { + OPK_Pack_size_t(msg, (const size_t*)&obj->num_bytes_clear); + OPK_Pack_size_t(msg, (const size_t*)&obj->num_bytes_encrypted); + OPK_Pack_uint8_t(msg, (const uint8_t*)&obj->subsample_flags); + OPK_Pack_size_t(msg, (const size_t*)&obj->block_offset); +} + +void OPK_Pack_OEMCrypto_SampleDescription( + ODK_Message* msg, OEMCrypto_SampleDescription const* obj) { + OPK_Pack_size_t(msg, (const size_t*)&obj->subsamples_length); + OPK_Pack_OEMCrypto_InputOutputPair( + msg, (const OEMCrypto_InputOutputPair*)&obj->buffers); + OPK_PackArray(msg, (const uint8_t*)&obj->iv[0], 16); + /* pack object array with packer function + * OPK_Pack_OEMCrypto_SubSampleDescription */ + ODK_Message* const odk_message = msg; + const void* const objs = (const void*)obj->subsamples; + const LengthType count = OPK_ToLengthType(obj->subsamples_length); + const size_t size = sizeof(OEMCrypto_SubSampleDescription); + const bool is_null = objs == NULL || OPK_LengthIsNull(count); + if (!OPK_PackBoolValue(odk_message, is_null)) { + for (size_t i = 0; i < OPK_ToSizeT(count); i++) { + OPK_Pack_OEMCrypto_SubSampleDescription( + odk_message, + (const OEMCrypto_SubSampleDescription*)(objs + i * size)); + } + } +} + +void OPK_Pack_OEMCrypto_CENCEncryptPatternDesc( + ODK_Message* msg, OEMCrypto_CENCEncryptPatternDesc const* obj) { + OPK_Pack_size_t(msg, (const size_t*)&obj->encrypt); + OPK_Pack_size_t(msg, (const size_t*)&obj->skip); +} + +void OPK_Pack_OEMCrypto_EntitledContentKeyObject( + ODK_Message* msg, OEMCrypto_EntitledContentKeyObject const* obj) { + OPK_Pack_OEMCrypto_Substring( + msg, (const OEMCrypto_Substring*)&obj->entitlement_key_id); + OPK_Pack_OEMCrypto_Substring( + msg, (const OEMCrypto_Substring*)&obj->content_key_id); + OPK_Pack_OEMCrypto_Substring( + msg, (const OEMCrypto_Substring*)&obj->content_key_data_iv); + OPK_Pack_OEMCrypto_Substring( + msg, (const OEMCrypto_Substring*)&obj->content_key_data); + OPK_Pack_OEMCrypto_Substring(msg, + (const OEMCrypto_Substring*)&obj->content_iv); + OPK_Pack_uint32_t(msg, (const OEMCryptoCipherMode*)&obj->cipher_mode); +} + +void OPK_Pack_OEMCrypto_KeyRefreshObject( + ODK_Message* msg, OEMCrypto_KeyRefreshObject const* obj) { + OPK_Pack_OEMCrypto_Substring(msg, (const OEMCrypto_Substring*)&obj->key_id); + OPK_Pack_OEMCrypto_Substring( + msg, (const OEMCrypto_Substring*)&obj->key_control_iv); + OPK_Pack_OEMCrypto_Substring(msg, + (const OEMCrypto_Substring*)&obj->key_control); +} + +void OPK_Pack_OEMCrypto_CENCEncryptPatternDesc_V15( + ODK_Message* msg, OEMCrypto_CENCEncryptPatternDesc_V15 const* obj) { + OPK_Pack_size_t(msg, (const size_t*)&obj->encrypt); + OPK_Pack_size_t(msg, (const size_t*)&obj->skip); + OPK_Pack_size_t(msg, (const size_t*)&obj->offset); +} + +void OPK_Pack_OEMCrypto_EntitledContentKeyObject_V16( + ODK_Message* msg, OEMCrypto_EntitledContentKeyObject_V16 const* obj) { + OPK_Pack_OEMCrypto_Substring( + msg, (const OEMCrypto_Substring*)&obj->entitlement_key_id); + OPK_Pack_OEMCrypto_Substring( + msg, (const OEMCrypto_Substring*)&obj->content_key_id); + OPK_Pack_OEMCrypto_Substring( + msg, (const OEMCrypto_Substring*)&obj->content_key_data_iv); + OPK_Pack_OEMCrypto_Substring( + msg, (const OEMCrypto_Substring*)&obj->content_key_data); +} + +void OPK_Unpack_OEMCrypto_Substring(ODK_Message* msg, + OEMCrypto_Substring* obj) { + OPK_Unpack_size_t(msg, &obj->offset); + OPK_Unpack_size_t(msg, &obj->length); +} + +void OPK_Unpack_OEMCrypto_DTCP2_CMI_Descriptor_0( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_0* obj) { + OPK_Unpack_uint8_t(msg, &obj->id); + OPK_Unpack_uint8_t(msg, &obj->extension); + OPK_Unpack_uint16_t(msg, &obj->length); + OPK_Unpack_uint8_t(msg, &obj->data); +} + +void OPK_Unpack_OEMCrypto_DTCP2_CMI_Descriptor_1( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_1* obj) { + OPK_Unpack_uint8_t(msg, &obj->id); + OPK_Unpack_uint8_t(msg, &obj->extension); + OPK_Unpack_uint16_t(msg, &obj->length); + OPK_UnpackArray(msg, &obj->data[0], sizeof(obj->data)); +} + +void OPK_Unpack_OEMCrypto_DTCP2_CMI_Descriptor_2( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_2* obj) { + OPK_Unpack_uint8_t(msg, &obj->id); + OPK_Unpack_uint8_t(msg, &obj->extension); + OPK_Unpack_uint16_t(msg, &obj->length); + OPK_UnpackArray(msg, &obj->data[0], sizeof(obj->data)); +} + +void OPK_Unpack_OEMCrypto_DTCP2_CMI_Packet(ODK_Message* msg, + OEMCrypto_DTCP2_CMI_Packet* obj) { + OPK_Unpack_uint8_t(msg, &obj->dtcp2_required); + OPK_Unpack_OEMCrypto_DTCP2_CMI_Descriptor_0(msg, &obj->cmi_descriptor_0); + OPK_Unpack_OEMCrypto_DTCP2_CMI_Descriptor_1(msg, &obj->cmi_descriptor_1); + OPK_Unpack_OEMCrypto_DTCP2_CMI_Descriptor_2(msg, &obj->cmi_descriptor_2); +} + +void OPK_Unpack_OEMCrypto_KeyObject(ODK_Message* msg, + OEMCrypto_KeyObject* obj) { + OPK_Unpack_OEMCrypto_Substring(msg, &obj->key_id); + OPK_Unpack_OEMCrypto_Substring(msg, &obj->key_data_iv); + OPK_Unpack_OEMCrypto_Substring(msg, &obj->key_data); + OPK_Unpack_OEMCrypto_Substring(msg, &obj->key_control_iv); + OPK_Unpack_OEMCrypto_Substring(msg, &obj->key_control); +} + +void OPK_Unpack_OEMCrypto_InputOutputPair(ODK_Message* msg, + OEMCrypto_InputOutputPair* obj) { + OPK_Unpack_size_t(msg, &obj->input_data_length); + OEMCrypto_SharedMemory* input_data; + OPK_UnpackSharedBuffer(msg, &input_data, + OPK_ToLengthType(obj->input_data_length), + /* map */ true, /* is_output */ false); + OPK_Unpack_OEMCrypto_DestBufferDesc(msg, &obj->output_descriptor); + memcpy(&obj->input_data, &input_data, sizeof(input_data)); +} + +void OPK_Unpack_OEMCrypto_SubSampleDescription( + ODK_Message* msg, OEMCrypto_SubSampleDescription* obj) { + OPK_Unpack_size_t(msg, &obj->num_bytes_clear); + OPK_Unpack_size_t(msg, &obj->num_bytes_encrypted); + OPK_Unpack_uint8_t(msg, &obj->subsample_flags); + OPK_Unpack_size_t(msg, &obj->block_offset); +} + +void OPK_Unpack_OEMCrypto_SampleDescription(ODK_Message* msg, + OEMCrypto_SampleDescription* obj) { + OPK_Unpack_size_t(msg, &obj->subsamples_length); + OPK_Unpack_OEMCrypto_InputOutputPair(msg, &obj->buffers); + OPK_UnpackArray(msg, &obj->iv[0], sizeof(obj->iv)); + OEMCrypto_SubSampleDescription* subsamples; + + /* unpack object array with unpacker function + * OPK_Unpack_OEMCrypto_SubSampleDescription */ + ODK_Message* odk_message = msg; + void** address = (void**)&subsamples; + LengthType count = OPK_ToLengthType(obj->subsamples_length); + size_t size = sizeof(OEMCrypto_SubSampleDescription); + if (address) { + *address = NULL; + } + if (!OPK_UnpackIsNull(odk_message)) { + if (address && !OPK_LengthIsNull(count)) { + size_t bytes_to_unpack = 0; + if (odk_mul_overflow_ux(OPK_ToSizeT(count), size, &bytes_to_unpack)) { + ODK_MESSAGE_SETSTATUS(odk_message, MESSAGE_STATUS_PARSE_ERROR); + } else { + *address = OPK_BumpAllocate(bytes_to_unpack); + if (!*address) { + ODK_MESSAGE_SETSTATUS(odk_message, MESSAGE_STATUS_OUT_OF_MEMORY); + } else { + for (size_t i = 0; i < OPK_ToSizeT(count); i++) { + OPK_Unpack_OEMCrypto_SubSampleDescription( + odk_message, + (OEMCrypto_SubSampleDescription*)((*address) + size * i)); + } + } + } + } + } + + memcpy(&obj->subsamples, &subsamples, sizeof(subsamples)); +} + +void OPK_Unpack_OEMCrypto_CENCEncryptPatternDesc( + ODK_Message* msg, OEMCrypto_CENCEncryptPatternDesc* obj) { + OPK_Unpack_size_t(msg, &obj->encrypt); + OPK_Unpack_size_t(msg, &obj->skip); +} + +void OPK_Unpack_OEMCrypto_EntitledContentKeyObject( + ODK_Message* msg, OEMCrypto_EntitledContentKeyObject* obj) { + OPK_Unpack_OEMCrypto_Substring(msg, &obj->entitlement_key_id); + OPK_Unpack_OEMCrypto_Substring(msg, &obj->content_key_id); + OPK_Unpack_OEMCrypto_Substring(msg, &obj->content_key_data_iv); + OPK_Unpack_OEMCrypto_Substring(msg, &obj->content_key_data); + OPK_Unpack_OEMCrypto_Substring(msg, &obj->content_iv); + OPK_Unpack_uint32_t(msg, &obj->cipher_mode); +} + +void OPK_Unpack_OEMCrypto_KeyRefreshObject(ODK_Message* msg, + OEMCrypto_KeyRefreshObject* obj) { + OPK_Unpack_OEMCrypto_Substring(msg, &obj->key_id); + OPK_Unpack_OEMCrypto_Substring(msg, &obj->key_control_iv); + OPK_Unpack_OEMCrypto_Substring(msg, &obj->key_control); +} + +void OPK_Unpack_OEMCrypto_CENCEncryptPatternDesc_V15( + ODK_Message* msg, OEMCrypto_CENCEncryptPatternDesc_V15* obj) { + OPK_Unpack_size_t(msg, &obj->encrypt); + OPK_Unpack_size_t(msg, &obj->skip); + OPK_Unpack_size_t(msg, &obj->offset); +} + +void OPK_Unpack_OEMCrypto_EntitledContentKeyObject_V16( + ODK_Message* msg, OEMCrypto_EntitledContentKeyObject_V16* obj) { + OPK_Unpack_OEMCrypto_Substring(msg, &obj->entitlement_key_id); + OPK_Unpack_OEMCrypto_Substring(msg, &obj->content_key_id); + OPK_Unpack_OEMCrypto_Substring(msg, &obj->content_key_data_iv); + OPK_Unpack_OEMCrypto_Substring(msg, &obj->content_key_data); +} + +void OPK_PackNullable_uint32_t(ODK_Message* msg, const uint32_t* value) { + OPK_PackBoolValue(msg, value == NULL); + if (value) { + OPK_Pack_uint32_t(msg, value); + } +} +void OPK_UnpackNullable_uint32_t(ODK_Message* msg, uint32_t** value) { + if (OPK_UnpackIsNull(msg)) { + *value = NULL; + } else { + OPK_Unpack_uint32_t(msg, *value); + } +} +void OPK_UnpackAlloc_uint32_t(ODK_Message* msg, uint32_t** value) { + *value = (uint32_t*)OPK_UnpackAlloc(msg, sizeof(uint32_t)); + if (*value) { + OPK_Unpack_uint32_t(msg, *value); + } +} +void OPK_PackNullable_size_t(ODK_Message* msg, const size_t* value) { + OPK_PackBoolValue(msg, value == NULL); + if (value) { + OPK_Pack_size_t(msg, value); + } +} +void OPK_UnpackNullable_size_t(ODK_Message* msg, size_t** value) { + if (OPK_UnpackIsNull(msg)) { + *value = NULL; + } else { + OPK_Unpack_size_t(msg, *value); + } +} +void OPK_UnpackAlloc_size_t(ODK_Message* msg, size_t** value) { + *value = (size_t*)OPK_UnpackAlloc(msg, sizeof(size_t)); + if (*value) { + OPK_Unpack_size_t(msg, *value); + } +} +void OPK_PackNullable_OEMCrypto_KeyRefreshObject( + ODK_Message* msg, const OEMCrypto_KeyRefreshObject* value) { + OPK_PackBoolValue(msg, value == NULL); + if (value) { + OPK_Pack_OEMCrypto_KeyRefreshObject(msg, value); + } +} +void OPK_UnpackNullable_OEMCrypto_KeyRefreshObject( + ODK_Message* msg, OEMCrypto_KeyRefreshObject** value) { + if (OPK_UnpackIsNull(msg)) { + *value = NULL; + } else { + OPK_Unpack_OEMCrypto_KeyRefreshObject(msg, *value); + } +} +void OPK_PackNullable_OEMCrypto_CENCEncryptPatternDesc( + ODK_Message* msg, const OEMCrypto_CENCEncryptPatternDesc* value) { + OPK_PackBoolValue(msg, value == NULL); + if (value) { + OPK_Pack_OEMCrypto_CENCEncryptPatternDesc(msg, value); + } +} +void OPK_UnpackNullable_OEMCrypto_CENCEncryptPatternDesc( + ODK_Message* msg, OEMCrypto_CENCEncryptPatternDesc** value) { + if (OPK_UnpackIsNull(msg)) { + *value = NULL; + } else { + OPK_Unpack_OEMCrypto_CENCEncryptPatternDesc(msg, *value); + } +} +void OPK_PackNullable_OEMCrypto_DestBufferDesc( + ODK_Message* msg, const OEMCrypto_DestBufferDesc* value) { + OPK_PackBoolValue(msg, value == NULL); + if (value) { + OPK_Pack_OEMCrypto_DestBufferDesc(msg, value); + } +} +void OPK_UnpackNullable_OEMCrypto_DestBufferDesc( + ODK_Message* msg, OEMCrypto_DestBufferDesc** value) { + if (OPK_UnpackIsNull(msg)) { + *value = NULL; + } else { + OPK_Unpack_OEMCrypto_DestBufferDesc(msg, *value); + } +} +void OPK_PackNullable_uint16_t(ODK_Message* msg, const uint16_t* value) { + OPK_PackBoolValue(msg, value == NULL); + if (value) { + OPK_Pack_uint16_t(msg, value); + } +} +void OPK_UnpackNullable_uint16_t(ODK_Message* msg, uint16_t** value) { + if (OPK_UnpackIsNull(msg)) { + *value = NULL; + } else { + OPK_Unpack_uint16_t(msg, *value); + } +} +void OPK_UnpackAlloc_uint16_t(ODK_Message* msg, uint16_t** value) { + *value = (uint16_t*)OPK_UnpackAlloc(msg, sizeof(uint16_t)); + if (*value) { + OPK_Unpack_uint16_t(msg, *value); + } +} +void OPK_UnpackAlloc_OEMCrypto_DestBufferDesc( + ODK_Message* msg, OEMCrypto_DestBufferDesc** value) { + *value = (OEMCrypto_DestBufferDesc*)OPK_UnpackAlloc( + msg, sizeof(OEMCrypto_DestBufferDesc)); + if (*value) { + OPK_Unpack_OEMCrypto_DestBufferDesc(msg, *value); + } +} +void OPK_PackNullable_int(ODK_Message* msg, const int* value) { + OPK_PackBoolValue(msg, value == NULL); + if (value) { + OPK_Pack_int(msg, value); + } +} +void OPK_UnpackNullable_int(ODK_Message* msg, int** value) { + if (OPK_UnpackIsNull(msg)) { + *value = NULL; + } else { + OPK_Unpack_int(msg, *value); + } +} +void OPK_UnpackAlloc_int(ODK_Message* msg, int** value) { + *value = (int*)OPK_UnpackAlloc(msg, sizeof(int)); + if (*value) { + OPK_Unpack_int(msg, *value); + } +} +void OPK_PackNullable_OEMCrypto_EntitledContentKeyObject( + ODK_Message* msg, const OEMCrypto_EntitledContentKeyObject* value) { + OPK_PackBoolValue(msg, value == NULL); + if (value) { + OPK_Pack_OEMCrypto_EntitledContentKeyObject(msg, value); + } +} +void OPK_UnpackNullable_OEMCrypto_EntitledContentKeyObject( + ODK_Message* msg, OEMCrypto_EntitledContentKeyObject** value) { + if (OPK_UnpackIsNull(msg)) { + *value = NULL; + } else { + OPK_Unpack_OEMCrypto_EntitledContentKeyObject(msg, *value); + } +} diff --git a/oemcrypto/opk/serialization/common/GEN_common_serializer.h b/oemcrypto/opk/serialization/common/GEN_common_serializer.h new file mode 100644 index 0000000..6146680 --- /dev/null +++ b/oemcrypto/opk/serialization/common/GEN_common_serializer.h @@ -0,0 +1,122 @@ +/* + * Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine License + * Agreement. + */ + +/* + * This code is auto-generated, do not edit + */ +#ifndef OPK_COMMON_SERIALIZER_H_ +#define OPK_COMMON_SERIALIZER_H_ + +#include "log_macros.h" +#include "opk_serialization_base.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool SuccessResult(OEMCryptoResult result); +bool Is_Valid_OEMCryptoResult(uint32_t value); +bool Is_Valid_OEMCrypto_Usage_Entry_Status(uint32_t value); +bool Is_Valid_OEMCrypto_LicenseType(uint32_t value); +bool Is_Valid_OEMCrypto_PrivateKeyType(uint32_t value); +bool Is_Valid_OEMCryptoBufferType(uint32_t value); +bool Is_Valid_OEMCryptoCipherMode(uint32_t value); +bool Is_Valid_OEMCrypto_Algorithm(uint32_t value); +bool Is_Valid_OEMCrypto_Clock_Security_Level(uint32_t value); +bool Is_Valid_OEMCrypto_HDCP_Capability(uint32_t value); +bool Is_Valid_OEMCrypto_DTCP2_Capability(uint32_t value); +bool Is_Valid_OEMCrypto_ProvisioningMethod(uint32_t value); +bool Is_Valid_OEMCrypto_WatermarkingSupport(uint32_t value); +bool Is_Valid_OEMCrypto_IdleState(uint32_t value); +bool Is_Valid_OEMCrypto_Security_Level(uint32_t value); +void OPK_Pack_OEMCrypto_Substring(ODK_Message* msg, + OEMCrypto_Substring const* obj); +void OPK_Pack_OEMCrypto_DTCP2_CMI_Descriptor_0( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_0 const* obj); +void OPK_Pack_OEMCrypto_DTCP2_CMI_Descriptor_1( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_1 const* obj); +void OPK_Pack_OEMCrypto_DTCP2_CMI_Descriptor_2( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_2 const* obj); +void OPK_Pack_OEMCrypto_DTCP2_CMI_Packet(ODK_Message* msg, + OEMCrypto_DTCP2_CMI_Packet const* obj); +void OPK_Pack_OEMCrypto_KeyObject(ODK_Message* msg, + OEMCrypto_KeyObject const* obj); +void OPK_Pack_OEMCrypto_InputOutputPair(ODK_Message* msg, + OEMCrypto_InputOutputPair const* obj); +void OPK_Pack_OEMCrypto_SubSampleDescription( + ODK_Message* msg, OEMCrypto_SubSampleDescription const* obj); +void OPK_Pack_OEMCrypto_SampleDescription( + ODK_Message* msg, OEMCrypto_SampleDescription const* obj); +void OPK_Pack_OEMCrypto_CENCEncryptPatternDesc( + ODK_Message* msg, OEMCrypto_CENCEncryptPatternDesc const* obj); +void OPK_Pack_OEMCrypto_EntitledContentKeyObject( + ODK_Message* msg, OEMCrypto_EntitledContentKeyObject const* obj); +void OPK_Pack_OEMCrypto_KeyRefreshObject(ODK_Message* msg, + OEMCrypto_KeyRefreshObject const* obj); +void OPK_Pack_OEMCrypto_CENCEncryptPatternDesc_V15( + ODK_Message* msg, OEMCrypto_CENCEncryptPatternDesc_V15 const* obj); +void OPK_Pack_OEMCrypto_EntitledContentKeyObject_V16( + ODK_Message* msg, OEMCrypto_EntitledContentKeyObject_V16 const* obj); +void OPK_Unpack_OEMCrypto_Substring(ODK_Message* msg, OEMCrypto_Substring* obj); +void OPK_Unpack_OEMCrypto_DTCP2_CMI_Descriptor_0( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_0* obj); +void OPK_Unpack_OEMCrypto_DTCP2_CMI_Descriptor_1( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_1* obj); +void OPK_Unpack_OEMCrypto_DTCP2_CMI_Descriptor_2( + ODK_Message* msg, OEMCrypto_DTCP2_CMI_Descriptor_2* obj); +void OPK_Unpack_OEMCrypto_DTCP2_CMI_Packet(ODK_Message* msg, + OEMCrypto_DTCP2_CMI_Packet* obj); +void OPK_Unpack_OEMCrypto_KeyObject(ODK_Message* msg, OEMCrypto_KeyObject* obj); +void OPK_Unpack_OEMCrypto_InputOutputPair(ODK_Message* msg, + OEMCrypto_InputOutputPair* obj); +void OPK_Unpack_OEMCrypto_SubSampleDescription( + ODK_Message* msg, OEMCrypto_SubSampleDescription* obj); +void OPK_Unpack_OEMCrypto_SampleDescription(ODK_Message* msg, + OEMCrypto_SampleDescription* obj); +void OPK_Unpack_OEMCrypto_CENCEncryptPatternDesc( + ODK_Message* msg, OEMCrypto_CENCEncryptPatternDesc* obj); +void OPK_Unpack_OEMCrypto_EntitledContentKeyObject( + ODK_Message* msg, OEMCrypto_EntitledContentKeyObject* obj); +void OPK_Unpack_OEMCrypto_KeyRefreshObject(ODK_Message* msg, + OEMCrypto_KeyRefreshObject* obj); +void OPK_Unpack_OEMCrypto_CENCEncryptPatternDesc_V15( + ODK_Message* msg, OEMCrypto_CENCEncryptPatternDesc_V15* obj); +void OPK_Unpack_OEMCrypto_EntitledContentKeyObject_V16( + ODK_Message* msg, OEMCrypto_EntitledContentKeyObject_V16* obj); +void OPK_PackNullable_uint32_t(ODK_Message* msg, const uint32_t* value); +void OPK_UnpackNullable_uint32_t(ODK_Message* msg, uint32_t** value); +void OPK_UnpackAlloc_uint32_t(ODK_Message* msg, uint32_t** value); +void OPK_PackNullable_size_t(ODK_Message* msg, const size_t* value); +void OPK_UnpackNullable_size_t(ODK_Message* msg, size_t** value); +void OPK_UnpackAlloc_size_t(ODK_Message* msg, size_t** value); +void OPK_PackNullable_OEMCrypto_KeyRefreshObject( + ODK_Message* msg, const OEMCrypto_KeyRefreshObject* value); +void OPK_UnpackNullable_OEMCrypto_KeyRefreshObject( + ODK_Message* msg, OEMCrypto_KeyRefreshObject** value); +void OPK_PackNullable_OEMCrypto_CENCEncryptPatternDesc( + ODK_Message* msg, const OEMCrypto_CENCEncryptPatternDesc* value); +void OPK_UnpackNullable_OEMCrypto_CENCEncryptPatternDesc( + ODK_Message* msg, OEMCrypto_CENCEncryptPatternDesc** value); +void OPK_PackNullable_OEMCrypto_DestBufferDesc( + ODK_Message* msg, const OEMCrypto_DestBufferDesc* value); +void OPK_UnpackNullable_OEMCrypto_DestBufferDesc( + ODK_Message* msg, OEMCrypto_DestBufferDesc** value); +void OPK_PackNullable_uint16_t(ODK_Message* msg, const uint16_t* value); +void OPK_UnpackNullable_uint16_t(ODK_Message* msg, uint16_t** value); +void OPK_UnpackAlloc_uint16_t(ODK_Message* msg, uint16_t** value); +void OPK_UnpackAlloc_OEMCrypto_DestBufferDesc(ODK_Message* msg, + OEMCrypto_DestBufferDesc** value); +void OPK_PackNullable_int(ODK_Message* msg, const int* value); +void OPK_UnpackNullable_int(ODK_Message* msg, int** value); +void OPK_UnpackAlloc_int(ODK_Message* msg, int** value); +void OPK_PackNullable_OEMCrypto_EntitledContentKeyObject( + ODK_Message* msg, const OEMCrypto_EntitledContentKeyObject* value); +void OPK_UnpackNullable_OEMCrypto_EntitledContentKeyObject( + ODK_Message* msg, OEMCrypto_EntitledContentKeyObject** value); +#ifdef __cplusplus +} // extern "C" +#endif +#endif /* OPK_COMMON_SERIALIZER_H_ */ diff --git a/oemcrypto/opk/serialization/common/bump_allocator.c b/oemcrypto/opk/serialization/common/bump_allocator.c new file mode 100644 index 0000000..1ffeec5 --- /dev/null +++ b/oemcrypto/opk/serialization/common/bump_allocator.c @@ -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. + */ + +#include "bump_allocator.h" + +#include +#include +#include + +#include "log_macros.h" +#include "odk_overflow.h" +#include "oemcrypto_compiler_attributes.h" + +#define BUFFER_SIZE (1024 * 1024) +static uint8_t buffer[BUFFER_SIZE]; +static size_t buffer_offset = 0; + +/* Align allocations to 16 bytes */ +#define ALIGN 16 + +/* + * Return void* to avoid cast alignment warnings. This is safe because all + * allocations are aligned to 16 byte boundaries + */ +UBSAN_IGNORE_UNSIGNED_OVERFLOW void* OPK_BumpAllocate(size_t nbytes) { + size_t alignment_pad = (ALIGN - buffer_offset) & (ALIGN - 1); + size_t new_offset = 0; + if (odk_add_overflow_ux(buffer_offset, nbytes + alignment_pad, &new_offset) || + (new_offset > BUFFER_SIZE)) { + LOGE("failed: %zu / %d bytes free, requested %zu", + BUFFER_SIZE - buffer_offset, BUFFER_SIZE, nbytes); + return NULL; + } + LOGV("allocated %zu bytes at offset %zu, %zu free", nbytes, buffer_offset, + BUFFER_SIZE - buffer_offset); + uint8_t* result = buffer + buffer_offset; + buffer_offset = new_offset; + return result; +} + +void OPK_BumpAllocator_Reset(void) { + LOGV("bump allocator capacity = %d", BUFFER_SIZE); + memset(buffer, 0, BUFFER_SIZE); + buffer_offset = 0; +} diff --git a/oemcrypto/opk/serialization/common/common_special_cases.c b/oemcrypto/opk/serialization/common/common_special_cases.c new file mode 100644 index 0000000..d0ea673 --- /dev/null +++ b/oemcrypto/opk/serialization/common/common_special_cases.c @@ -0,0 +1,96 @@ +/* + * 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 "common_special_cases.h" + +#include +#include +#include +#include + +#include "GEN_common_serializer.h" +#include "OEMCryptoCENC.h" +#include "log_macros.h" +#include "odk_overflow.h" +#include "opk_serialization_base.h" +#include "os_type.h" +#include "shared_buffer_allocator.h" +#include "tos_secure_buffer_interface.h" +#include "tos_shared_memory_interface.h" + +/* + * Special cases due to union & shared memory + */ + +/* + * Pack the destination buffer parameter to OEMCrypto_DecryptCENC and + * OEMCrypto_CopyBuffer + */ +void OPK_Pack_OEMCrypto_DestBufferDesc(ODK_Message* message, + const OEMCrypto_DestBufferDesc* obj) { + if (obj == NULL) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_NULL_POINTER_ERROR); + return; + } + OPK_Pack_uint32_t(message, (const uint32_t*)&obj->type); + switch (obj->type) { + case OEMCrypto_BufferType_Clear: { + OPK_Pack_size_t(message, &obj->buffer.clear.clear_buffer_length); + OPK_PackSharedBuffer( + message, obj->buffer.clear.clear_buffer, + OPK_ToLengthType(obj->buffer.clear.clear_buffer_length), + /* map */ true, /* copy in */ false, + /* is_output */ true); + break; + } + case OEMCrypto_BufferType_Secure: + if (OPK_Get_OSType() == OPK_RICH_OS) { + /* let the secure buffer OS interface pack the buffer info as needed */ + TOS_SecureBuffer_Pack(message, obj); + } + break; + case OEMCrypto_BufferType_Direct: + OPK_Pack_bool(message, &obj->buffer.direct.is_video); + break; + } +} + +/* + * Unpack the destination buffer parameter to OEMCrypto_DecryptCENC and + * OEMCrypto_CopyBuffer + */ +void OPK_Unpack_OEMCrypto_DestBufferDesc(ODK_Message* message, + OEMCrypto_DestBufferDesc* obj) { + if (obj == NULL) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_NULL_POINTER_ERROR); + return; + } + OPK_Unpack_uint32_t(message, (uint32_t*)&obj->type); + switch (obj->type) { + case OEMCrypto_BufferType_Clear: + OPK_Unpack_size_t(message, &obj->buffer.clear.clear_buffer_length); + OPK_UnpackSharedBuffer( + message, &obj->buffer.clear.clear_buffer, + OPK_ToLengthType(obj->buffer.clear.clear_buffer_length), + /* map */ true, /* is output */ true); + break; + case OEMCrypto_BufferType_Secure: + if (OPK_Get_OSType() == OPK_TRUSTED_OS) { + /* let the secure buffer OS interface unpack the buffer info */ + TOS_SecureBuffer_Unpack(message, obj); + /* double-check the size */ + if (!TOS_SecureBuffer_CheckSize( + obj->buffer.secure.secure_buffer, + obj->buffer.secure.secure_buffer_length)) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_PARSE_ERROR); + } + } + break; + case OEMCrypto_BufferType_Direct: + OPK_Unpack_bool(message, &obj->buffer.direct.is_video); + break; + } +} diff --git a/oemcrypto/opk/serialization/common/common_special_cases.h b/oemcrypto/opk/serialization/common/common_special_cases.h new file mode 100644 index 0000000..cb6d352 --- /dev/null +++ b/oemcrypto/opk/serialization/common/common_special_cases.h @@ -0,0 +1,32 @@ +/* + * 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 OPK_COMMON_SPECIAL_CASES_H_ +#define OPK_COMMON_SPECIAL_CASES_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "OEMCryptoCENC.h" +#include "odk_message.h" + +/* + * Special cases due to union & shared memory + */ +void OPK_Pack_OEMCrypto_DestBufferDesc(ODK_Message* msg, + const OEMCrypto_DestBufferDesc* obj); +void OPK_Unpack_OEMCrypto_DestBufferDesc(ODK_Message* msg, + OEMCrypto_DestBufferDesc* obj); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPK_COMMON_SPECIAL_CASES_H_ diff --git a/oemcrypto/opk/serialization/common/include/bump_allocator.h b/oemcrypto/opk/serialization/common/include/bump_allocator.h new file mode 100644 index 0000000..42b1098 --- /dev/null +++ b/oemcrypto/opk/serialization/common/include/bump_allocator.h @@ -0,0 +1,32 @@ +/* + * 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 OPK_BUMP_ALLOCATOR_H_ +#define OPK_BUMP_ALLOCATOR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/* + * Simple bump allocator. Allocate memory chunks from a fixed region + * at consecutively increasing offsets. The memory is all released + * when OPK_BumpAllocator_Reset is called. Returns NULL if the region + * is full. + * + * Allocations are guaranteed to be aligned to 16 byte boundaries. + */ +void* OPK_BumpAllocate(size_t size); +void OPK_BumpAllocator_Reset(void); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPK_BUMP_ALLOCATOR_H_ diff --git a/oemcrypto/opk/serialization/common/include/length_types.h b/oemcrypto/opk/serialization/common/include/length_types.h new file mode 100644 index 0000000..3fb685b --- /dev/null +++ b/oemcrypto/opk/serialization/common/include/length_types.h @@ -0,0 +1,49 @@ +/* + * 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 OPK_LENGTH_TYPES_H_ +#define OPK_LENGTH_TYPES_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * In the OEMCrypto API, the lengths of variable length buffers can be of + * various types: base types of size_t or uint32_t and different levels of + * indirection when used as [in] or [out] parameters. The LengthType struct + * enables the pack/unpack primitives to operate on lengths consistently + * regardless of data type and level of indirection. + */ + +typedef struct { + size_t length; + bool is_null; +} LengthType; + +LengthType OPK_ToLengthType(size_t length); +LengthType OPK_FromSizeTPtr(const size_t* length); +LengthType OPK_FromU32Ptr(const uint32_t* length); +LengthType OPK_FromSizeTPtrPtr(size_t* const* length); +LengthType OPK_FromUint32PtrPtr(uint32_t* const* length); + +size_t OPK_ToSizeT(LengthType length_type); +bool OPK_LengthIsNull(LengthType length_type); + +size_t OPK_SafeDerefSizeTPtr(const size_t* ptr); +size_t OPK_SafeDerefU32Ptr(const uint32_t* ptr); +size_t OPK_SafeDerefSizeTPtrPtr(size_t* const* ptr); +size_t OPK_SafeDerefU32PtrPtr(uint32_t* const* ptr); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPK_LENGTH_TYPES_H_ diff --git a/oemcrypto/opk/serialization/common/include/log_macros.h b/oemcrypto/opk/serialization/common/include/log_macros.h new file mode 100644 index 0000000..5b5fdf2 --- /dev/null +++ b/oemcrypto/opk/serialization/common/include/log_macros.h @@ -0,0 +1,77 @@ +/* + * 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 WIDEVINE_OPK_INCLUDE_LOG_MACROS_H_ +#define WIDEVINE_OPK_INCLUDE_LOG_MACROS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "odk_message.h" +#include "tos_logging_interface.h" +#include "tos_transport_interface.h" + +/* + * Log statements are only compiled into the code when logging is enabled. + * Logging is disabled on non-debug builds where NDEBUG is defined. + * + * To enable logging #define ENABLE_LOGGING to 1. By default logging + * output goes to the internally defined varargs function "OPK_Log", you + * can override this with your own varargs function by calling + * OPK_SetLogFunction((*func)(OPK_LogLevel level, const char* fmt, ...)); + */ +#ifndef ENABLE_LOGGING +# define ENABLE_LOGGING 0 +#endif + +/* + * For non-debug builds ensure that logging is turned off + */ +#ifdef NDEBUG +# undef ENABLE_LOGGING +# define ENABLE_LOGGING 0 +#endif + +#if ENABLE_LOGGING + +# define LOG(level, ...) \ + do { \ + TOS_Log(__FILE__, __func__, __LINE__, level, __VA_ARGS__); \ + } while (0) +# define ODK_MESSAGE_SETSTATUS(msg, status) \ + do { \ + ODK_Message_SetStatus(msg, status); \ + if (status != MESSAGE_STATUS_OK) { \ + LOGW("%s", OPK_MessageStatus_Str(status)); \ + } \ + } while (0) + +const char* OPK_MessageStatus_Str(ODK_MessageStatus status); +const char* OPK_TransportStatus_Str(OPK_TransportStatus status); + +#else /* !ENABLE_LOGGING */ + +# define LOG(level, ...) +# define ODK_MESSAGE_SETSTATUS(msg, status) ODK_Message_SetStatus(msg, status); + +#endif /* ENABLE_LOGGING */ + +#define LOGF(...) LOG(OPK_LogLevel_Fatal, __VA_ARGS__) +#define LOGE(...) LOG(OPK_LogLevel_Error, __VA_ARGS__) +#define LOGW(...) LOG(OPK_LogLevel_Warning, __VA_ARGS__) +#define LOGI(...) LOG(OPK_LogLevel_Info, __VA_ARGS__) +#define LOGD(...) LOG(OPK_LogLevel_Debug, __VA_ARGS__) +#define LOGV(...) LOG(OPK_LogLevel_Verbose, __VA_ARGS__) + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* WIDEVINE_OPK_INCLUDE_LOG_MACROS_H_ */ diff --git a/oemcrypto/opk/serialization/common/include/marshaller_base.h b/oemcrypto/opk/serialization/common/include/marshaller_base.h new file mode 100644 index 0000000..dbe9440 --- /dev/null +++ b/oemcrypto/opk/serialization/common/include/marshaller_base.h @@ -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. + */ + +#ifndef OPK_MARSHALLER_BASE_H_ +#define OPK_MARSHALLER_BASE_H_ + +#include +#include +#include +#include + +#include "OEMCryptoCENC.h" +#include "opk_serialization_base.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _Message Message; + +/* + * When packing a pointer to size_t, and the pointer is null, pass + * this special value of size_t instead + */ +#define SZ_NULL (size_t) ~0 + +void OPK_Init_bool(bool* value); +void OPK_Init_int(int* value); +void OPK_Init_size_t(size_t* value); +void OPK_Init_c_str(char** value); +void OPK_Init_uint8_t(uint8_t* value); +void OPK_Init_uint16_t(uint16_t* value); +void OPK_Init_uint32_t(uint32_t* value); +void OPK_Init_uint64_t(uint64_t* value); +void OPK_InitMemory(uint8_t* addr, size_t length); +void OPK_InitPointer(uint8_t** addr); + +/* + * Allocate memory for a variable from the bump allocator, used for + * DeclarePackVar, DeclareUnpackVar, for some pointer types + */ +void* OPK_VarAlloc(size_t size); + +/* + * Special cases due to union & shared memory + */ +void OPK_Init_OEMCrypto_DestBufferDesc(OEMCrypto_DestBufferDesc* desc); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPK_MARSHALLER_BASE_H_ diff --git a/oemcrypto/opk/serialization/common/include/message_debug.h b/oemcrypto/opk/serialization/common/include/message_debug.h new file mode 100644 index 0000000..51f1e36 --- /dev/null +++ b/oemcrypto/opk/serialization/common/include/message_debug.h @@ -0,0 +1,77 @@ +/* + * 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 WIDEVINE_OPK_INCLUDE_MESSAGE_DEBUG_H_ +#define WIDEVINE_OPK_INCLUDE_MESSAGE_DEBUG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "odk_message.h" +#include "tos_transport_interface.h" + +/* + * setenv GENERATE_MESSAGE_DEBUG=yes inserts MDBG messages to the code + * setenv ENABLE_MESSAGE_DEBUG=yes compiles the code with MDBG enabled + */ + +/* + * You can redefine MSG_PRINTF if you want to use a custom varargs message + * function + */ +#ifndef MSG_PRINTF +# define MSG_PRINTF printf +#endif + +#define BLACK "\x1B[30m" +#define RED "\x1B[31m" +#define GREEN "\x1B[32m" +#define YELLOW "\x1B[33m" +#define BLUE "\x1B[34m" +#define MAGENTA "\x1B[35m" +#define CYAN "\x1B[36m" +#define BOLD "\x1B[30;1m" +#define COLOR_OFF "\x1B[0m" +#define BOLD_OFF "\x1B[0m" + +/* + * A message ID number is included in each log message and incremented each + * time a message is sent/received. Used to help correlate ree & tee messages + * in separate log files. + */ +extern size_t opk_message_debug_id_; + +char* OPK_MessageDebug_Time(void); +const char* OPK_TransportStatus_Str(OPK_TransportStatus status); +const char* OPK_Pointer_Str(const void* ptr, const char* ptr_name, + uint64_t value); +const char* OPK_Bytes_Str(const uint8_t* address, size_t length); +const char* OPK_Memory_Str(const uint8_t* address, size_t length); + +#ifdef ENABLE_MESSAGE_DEBUG +# define MDBG(fmt, ...) \ + do { \ + MSG_PRINTF(BLACK "%s " MAGENTA "#%05zu" COLOR_OFF " %s: " fmt COLOR_OFF \ + "\n", \ + OPK_MessageDebug_Time(), opk_message_debug_id_, __func__, \ + ##__VA_ARGS__); \ + } while (0) + +# define MDBG_NEXT_MESSAGE opk_message_debug_id_++ +#else +# define MDBG(level, ...) +# define MDBG_NEXT_MESSAGE +#endif /* ENABLE_MESSAGE_DEBUG */ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* WIDEVINE_OPK_INCLUDE_MESSAGE_DEBUG_H_ */ diff --git a/oemcrypto/opk/serialization/common/include/opk_serialization_base.h b/oemcrypto/opk/serialization/common/include/opk_serialization_base.h new file mode 100644 index 0000000..09dd4ca --- /dev/null +++ b/oemcrypto/opk/serialization/common/include/opk_serialization_base.h @@ -0,0 +1,262 @@ +/* + * 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 OPK_SERIALIZATION_BASE_H_ +#define OPK_SERIALIZATION_BASE_H_ + +#include +#include + +#include "OEMCryptoCENC.h" +#include "length_types.h" +#include "odk_message.h" +#include "odk_message_priv.h" +#include "shared_buffer_allocator.h" +#include "tos_shared_memory_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef char* c_str; + +/* + * Errors in updating message base, size, capacity and read offset are likely + * candidates for introducing vulnerabilities that could be exploited with + * buffer overflow attacks during message parsing or construction, so it's + * important to have a methodology to reduce the chances of these types of + * errors being introduced during development or maintenance. This methodology + * is based on: + * + * 1. Define a minimal set of message manipulation functions that can directly + * modify the internal message fields. Message validity checking needs to be + * done in these functions and only in these functions. Currently there are + * just three: PackBytes, UnpackBytes, and OPK_UnpackInPlace + * + * 2. Define a small number of primitives that build on the message manipulation + * functions in 1, that generated and hand-written code can use to read/write + * messages. These primitives are all defined in this file so they are + * localized and easy to review. + * + * 3. Allow any code that uses the primitives in 2. to set the message status to + * an error code when an errors occurs. Once the status has been set to an + * error code, the message validity check in the message manipulation + * functions will fail and subsequent calls to the primitives will have no + * effect until the error is checked and cleared. After packing/unpacking + * the whole message, the caller should check the message status to confirm + * successful completion of the overall operation. + * + * 4. No code that uses these primitives needs to check message validity or + * message status during packing/unpacking because it is all handled by the + * functions in 1. This reduces the chances of generated or hand-written + * code neglecting to check for error conditions and conditionally altering + * control flow by returning from each function call early on errors. It also + * simplifies the pack/unpack code considerably which makes the code smaller + * and easier to review. + */ + +/* + * Convenience function used by the serializer to pack a bool without having to + * allocate a local variable. + */ +void OPK_Pack_bool(ODK_Message* message, const bool* value); + +/* + * Pack primitives for the integer and string types + */ +void OPK_Pack_int(ODK_Message* message, const int* value); +void OPK_Pack_size_t(ODK_Message* message, const size_t* value); +void OPK_Pack_uint8_t(ODK_Message* message, const uint8_t* value); +void OPK_Pack_uint16_t(ODK_Message* message, const uint16_t* value); +void OPK_Pack_uint32_t(ODK_Message* message, const uint32_t* value); +void OPK_Pack_uint64_t(ODK_Message* message, const uint64_t* value); +bool OPK_PackBoolValue(ODK_Message* message, bool value); + +/* + * Pack a boolean indicating if the pointer value is NULL + */ +bool OPK_PackIsNull(ODK_Message* message, const void* value); + +/* + * Pack a boolean indicating if the address value is NULL. It's effectively the + * same as PackIsNull but provided for symmetry with UnpackAlloc. + * + * Parameters: + * message - the message to pack into + * address - the pointer being packed + */ +void OPK_PackAlloc(ODK_Message* message, const void* address); + +/* + * Pack a range of memory given by address and length. First pack a bool + * indicating if the address is NULL. If the address is non-null and length is + * non-null then pack the length and |length| bytes from memory beginning at + * |address|. + * + * Parameters: + * message - the message to pack into + * address - the pointer to the memory being packed + * length - the number of bytes in the range + */ +void OPK_PackMemory(ODK_Message* message, const uint8_t* address, + LengthType length); + +/* + * Pack an array of bytes given by address and length. Similar to + * OPK_PackMemory, except the array length here is a fixed constant passed + * as size_t, whereas in OPK_PackMemory the length is from a variable. + * + * Parameters: + * message - the message to pack into + * address - the pointer to the array being packed + * length - the number of bytes in the array + */ +void OPK_PackArray(ODK_Message* message, const uint8_t* address, size_t length); + +/* + * Pack end-of-messsage to mark the end of the message + */ +void OPK_PackEOM(ODK_Message* message); + +/* + * Pack a description of a shared buffer into a mesesage. A shared buffer is a + * chunk of shared memory that is allocated for passing an OEMCrypto parameter + * between the REE and TEE. If the buffer has not yet been allocated, |map| must + * be set to true. If |map| is false, the shared buffer must have been allocated + * previously. When a shared buffer is allocated the caller must indicate if + * there is data in non-shared memory that will be used to initialize the + * contents of the shared buffer, and whether the buffer is associated with an + * input or output parameter. + * + * Parameters: + * message - The message to pack into + * address - address of the memory buffer. If address is null, no shared + * buffer is being packed, which is indicated in the message by a packed + * bool value so the receiver will also know not to allocate the buffer + * length - the length of the buffer + * map - if true, the buffer needs to be allocated + * copy_in - set to true if data is to be copied from |address| into the + * buffer + * is_output - set to true if the buffer is associated with an output + * parameter type + */ +void OPK_PackSharedBuffer(ODK_Message* message, const uint8_t* address, + const LengthType length, bool map, bool copy_in, + bool is_output); + +/* + * Unpack primitives for the integer and string types + */ +void OPK_Unpack_bool(ODK_Message* message, bool* value); +void OPK_Unpack_int(ODK_Message* message, int* value); +void OPK_Unpack_size_t(ODK_Message* message, size_t* value); +void OPK_Unpack_uint8_t(ODK_Message* message, uint8_t* value); +void OPK_Unpack_uint16_t(ODK_Message* message, uint16_t* value); +void OPK_Unpack_uint32_t(ODK_Message* message, uint32_t* value); +void OPK_Unpack_uint64_t(ODK_Message* message, uint64_t* value); +bool OPK_UnpackBoolValue(ODK_Message* message); + +/* + * Return true if a pointer was packed as NULL + */ +bool OPK_UnpackIsNull(ODK_Message* message); + +/* + * Unpack a pointer to a range of memory of the size indicated by |size|. If + * the pointer was packed as NULL, return NULL. Otherwise return |size| bytes + * allocated from the bump allocator. + * + * Parameters: + * message - the message to unpack from + * size - the number of bytes to allocate + */ +void* OPK_UnpackAlloc(ODK_Message* message, size_t size); + +/* + * Unpack a pointer to an array of basic types, each of |size| bytes. If the + * pointer was packed as NULL, return NULL. Otherwise return |count| * |size| + * bytes allocated from the bump allocator. + * + * Parameters: + * message - the message to unpack from + * count - the number of elements to allocate + * size - the number of bytes in each element + */ +void* OPK_UnpackAllocBuffer(ODK_Message* message, LengthType count, + size_t size); +/* + * Unpack |length| bytes from the message, copying them to the memory referenced + * by |address|. The caller is responsible for ensuring that the buffer is + * large enough to handle the number of bytes requested. First unpack a bool + * that indicates if the pointer was passed as NULL and if not unpack the + * requested number of bytes into the address. + * + * Parameters: + * message - the message to unpack from + * address - where the bytes will be copied to + * length - the number of bytes to copy out + */ +void OPK_UnpackArray(ODK_Message* message, uint8_t* address, size_t length); + +/* + * Unpack a variable length array of bytes from the message in place, meaning + * that the array of bytes will be referenced where they are in the message, not + * copied out. The length of the variable length array is encoded in the + * message. First unpack a bool that indicates if the address was passed in as + * NULL. If so set *|address| to NULL and return. Otherwise set *|address| to + * the base + read offset in the message, unpack the length of the array from + * the message and increment the message read offset by that amount. Return the + * number of bytes read in *length. If |length| passed in is non-zero, check + * if the length matches the number of bytes unpacked. If not, set *address to + * NULL. + * + * Parameters: + * message - the message to unpack from + * address - where the bytes may be accessed from. If |length| is non-zero on + * input and the number of bytes unpacked is not equal to |length|, + * *|address| will be set to NULL + * length - 0 if the number of bytes to unpack is unknown, otherwise set + * length to the number of bytes expected. + */ +void OPK_UnpackInPlace(ODK_Message* message, uint8_t** address, + LengthType length); + +/* + * Unpack a description of a shared buffer from a mesesage. A shared buffer is a + * chunk of shared memory that is allocated for passing an OEMCrypto parameter + * between the REE and TEE. First unpack a bool that indicates if the output + * buffer pointer is null, if so there is no shared buffer to unpack. If |map| + * is true, allocate the shared buffer. The caller must specify whether the + * buffer is associated with an input or output parameter. + * + * Parameters: + * message - The message to unpack from + * address - if |map| is true, *address will get set to the address + * of the shared buffer. + * length - the length of the segment + * map - if true, the buffer needs to be mapped + * is_output - set to true if the buffer is associated with an output + * parameter type + * + * Returns: + * void + */ +void OPK_UnpackSharedBuffer(ODK_Message* message, uint8_t** address, + LengthType length, bool map, bool is_output); + +/* + * Unpack end-of-messsage to indicate the whole message was recieved. + * + * Parameters: + * message - The message to unpack from + */ +void OPK_UnpackEOM(ODK_Message* message); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPK_SERIALIZATION_BASE_H_ diff --git a/oemcrypto/opk/serialization/common/include/os_type.h b/oemcrypto/opk/serialization/common/include/os_type.h new file mode 100644 index 0000000..aa6b745 --- /dev/null +++ b/oemcrypto/opk/serialization/common/include/os_type.h @@ -0,0 +1,25 @@ +/* + * 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 OPK_OS_TYPE_H_ +#define OPK_OS_TYPE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Provide a function to test if code is running in the TEE or the REE + */ +typedef enum { OPK_RICH_OS, OPK_TRUSTED_OS } OPK_OSType; + +OPK_OSType OPK_Get_OSType(void); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPK_OS_TYPE_H_ diff --git a/oemcrypto/opk/serialization/common/include/shared_buffer_allocator.h b/oemcrypto/opk/serialization/common/include/shared_buffer_allocator.h new file mode 100644 index 0000000..24c6202 --- /dev/null +++ b/oemcrypto/opk/serialization/common/include/shared_buffer_allocator.h @@ -0,0 +1,43 @@ +/* + * 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 OPK_SHARED_BUFFER_ALLOCATOR_H_ +#define OPK_SHARED_BUFFER_ALLOCATOR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +typedef enum { + SHARED_BUFFER_TYPE_INVALID, + SHARED_BUFFER_TYPE_INPUT, + SHARED_BUFFER_TYPE_OUTPUT +} SharedBufferType; + +/* + * Allocation and iteration for shared memory buffers + */ +bool OPK_SharedBuffer_Initialize(void); +void OPK_SharedBuffer_Terminate(void); +uint8_t* OPK_SharedBuffer_Allocate(uint8_t* address, size_t size, bool copy_in, + bool copy_out, bool is_output); +uint8_t* OPK_SharedBuffer_NextBuffer(void); +uint8_t* OPK_SharedBuffer_NextOutputBuffer(void); +uint8_t* OPK_SharedBuffer_GetAddress(void); +size_t OPK_SharedBuffer_GetCurrentBufferSize(void); +void OPK_SharedBuffer_FinalizePacking(void); +void OPK_SharedBuffer_FinalizeUnpacking(void); +void OPK_SharedBuffer_Reset(void); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPK_SHARED_BUFFER_ALLOCATOR_H_ diff --git a/oemcrypto/opk/serialization/common/include/version.h b/oemcrypto/opk/serialization/common/include/version.h new file mode 100644 index 0000000..36a5f7a --- /dev/null +++ b/oemcrypto/opk/serialization/common/include/version.h @@ -0,0 +1,33 @@ +/* + * 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 OPK_COMMON_VERSION_H_ +#define OPK_COMMON_VERSION_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The serialization version is used to check compatibility between the + * protocols that the REE and TEE were compiled with. Prior to sending the first + * OEMCrypto request, the REE will construct a version request message and send + * it to the TEE. The TEE will respond with its {major, minor} version. The REE + * will compare its {major, minor} version to the TEE version. If the REE and + * TEE have the same major version, they must be compatible and communication + * will be allowed to continue. If the major versions differ, the connection + * will be terminated with an error. A difference in only the minor version + * indicates that there may be some differences in the protocols that are + * backwards compatible. + */ +#define OPK_SERIALIZATION_VERSION_MAJOR 3 +#define OPK_SERIALIZATION_VERSION_MINOR 0 + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPK_COMMON_VERSION_H diff --git a/oemcrypto/opk/serialization/common/length_types.c b/oemcrypto/opk/serialization/common/length_types.c new file mode 100644 index 0000000..ca10db2 --- /dev/null +++ b/oemcrypto/opk/serialization/common/length_types.c @@ -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 "length_types.h" + +LengthType OPK_ToLengthType(size_t length) { + LengthType lt = {.length = length, .is_null = false}; + return lt; +} + +LengthType OPK_FromSizeTPtr(const size_t* length) { + LengthType lt = {0}; + if (!length) { + lt.length = 0; + lt.is_null = true; + } else { + lt.length = *length; + lt.is_null = false; + } + return lt; +} + +LengthType OPK_FromU32Ptr(const uint32_t* length) { + LengthType lt = {0}; + if (!length) { + lt.length = 0; + lt.is_null = true; + } else { + lt.length = *length; + lt.is_null = false; + } + return lt; +} + +LengthType OPK_FromSizeTPtrPtr(size_t* const* length) { + LengthType lt = {0}; + if (!length || !*length) { + lt.length = 0; + lt.is_null = true; + } else { + lt.length = **length; + lt.is_null = false; + } + return lt; +} + +LengthType OPK_FromU32PtrPtr(uint32_t* const* length) { + LengthType lt = {0}; + if (!length || !*length) { + lt.length = 0; + lt.is_null = true; + } else { + lt.length = **length; + lt.is_null = false; + } + return lt; +} + +size_t OPK_ToSizeT(LengthType length_type) { return length_type.length; } + +bool OPK_LengthIsNull(LengthType length_type) { return length_type.is_null; } + +size_t OPK_SafeDerefSizeTPtr(const size_t* ptr) { return ptr ? *ptr : 0; } + +size_t OPK_SafeDerefU32Ptr(const uint32_t* ptr) { return ptr ? *ptr : 0; } + +size_t OPK_SafeDerefSizeTPtrPtr(size_t* const* ptr) { + return ptr && *ptr ? **ptr : 0; +} + +size_t OPK_SafeDerefU32PtrPtr(uint32_t* const* ptr) { + return ptr && *ptr ? **ptr : 0; +} diff --git a/oemcrypto/opk/serialization/common/log_macros.c b/oemcrypto/opk/serialization/common/log_macros.c new file mode 100644 index 0000000..10e4e52 --- /dev/null +++ b/oemcrypto/opk/serialization/common/log_macros.c @@ -0,0 +1,71 @@ +/* + * 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 "log_macros.h" + +#include +#include +#include + +#if ENABLE_LOGGING + +/* + * Format an ODK_MessageStatus enum as a string + */ +const char* OPK_MessageStatus_Str(ODK_MessageStatus status) { + switch (status) { + case MESSAGE_STATUS_OK: + return "OK"; + case MESSAGE_STATUS_UNKNOWN_ERROR: + return "UNKNOWN_ERROR"; + case MESSAGE_STATUS_OVERFLOW_ERROR: + return "OVERFLOW_ERROR"; + case MESSAGE_STATUS_UNDERFLOW_ERROR: + return "UNDERFLOW_ERROR"; + case MESSAGE_STATUS_PARSE_ERROR: + return "PARSE_ERROR"; + case MESSAGE_STATUS_NULL_POINTER_ERROR: + return "NULL_POINTER_ERROR"; + case MESSAGE_STATUS_API_VALUE_ERROR: + return "API_VALUE_ERROR"; + case MESSAGE_STATUS_END_OF_MESSAGE_ERROR: + return "END_OF_MESSAGE_ERROR"; + case MESSAGE_STATUS_INVALID_ENUM_VALUE: + return "INVALID_ENUM_VALUE"; + case MESSAGE_STATUS_INVALID_TAG_ERROR: + return "INVALID_TAG_ERROR"; + case MESSAGE_STATUS_NOT_INITIALIZED: + return "NOT_INITIALIZED"; + case MESSAGE_STATUS_OUT_OF_MEMORY: + return "OUT_OF_MEMORY"; + case MESSAGE_STATUS_MAP_SHARED_MEMORY_FAILED: + return "MAP_SHARED_MEMORY_FAILED"; + case MESSAGE_STATUS_SECURE_BUFFER_ERROR: + return "SECURE_BUFFER_ERROR"; + default: + break; + } + return "missing case: fix OPK_MessageStatus_Str in log_macros.c"; +} + +/* + * Format an OPK_TransportStatus enum as a string + */ +const char* OPK_TransportStatus_Str(OPK_TransportStatus status) { + switch (status) { + case OPK_TRANSPORT_STATUS_OK: + return "OK"; + case OPK_TRANSPORT_STATUS_IO_ERROR: + return "IO_ERROR"; + case OPK_TRANSPORT_STATUS_ALLOC_FAILED: + return "ALLOC_FAILED"; + default: + break; + } + return "missing case: fix OPK_TransportStatus_Str in log_macros.c"; +} + +#endif /* ENABLE_LOGGING */ diff --git a/oemcrypto/opk/serialization/common/marshaller_base.c b/oemcrypto/opk/serialization/common/marshaller_base.c new file mode 100644 index 0000000..45109aa --- /dev/null +++ b/oemcrypto/opk/serialization/common/marshaller_base.c @@ -0,0 +1,94 @@ +/* + * 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 "marshaller_base.h" +#include "bump_allocator.h" + +/* + * Initialize variables to non-zero values to trigger failures if they are not + * properly assigned to before use. + */ +static void InitBytes(uint8_t* ptr, size_t count) { + if (ptr && count) { + memset(ptr, 0xfe, count); + } +} + +void OPK_Init_bool(bool* value) { + if (value) { + *value = false; + } +} + +void OPK_Init_size_t(size_t* value) { + if (value) { + *value = ~0; + } +} + +void OPK_Init_int(int* value) { + if (value) { + *value = ~0; + } +} + +void OPK_Init_c_str(char** value) { + if (value) { + *value = NULL; + } +} + +void OPK_Init_uint8_t(uint8_t* value) { + if (value) { + *value = ~0; + } +} + +void OPK_Init_uint16_t(uint16_t* value) { + if (value) { + *value = ~0; + } +} + +void OPK_Init_uint32_t(uint32_t* value) { + if (value) { + *value = ~0; + } +} + +void OPK_Init_uint64_t(uint64_t* value) { + if (value) { + *value = ~0; + } +} + +void OPK_InitMemory(uint8_t* address, size_t length) { + if (address && length) { + InitBytes(address, length); + } +} + +void OPK_InitPointer(uint8_t** ptr) { + if (ptr) { + *ptr = NULL; + } +} + +/* + * Allocate memory for a variable from the bump allocator, used for + * DeclarePackVar, DeclareUnpackVar, for some pointer types + */ +void* OPK_VarAlloc(size_t size) { return OPK_BumpAllocate(size); } + +/* + * Special cases due to union & shared memory + */ +void OPK_Init_OEMCrypto_DestBufferDesc(OEMCrypto_DestBufferDesc* d) { + if (d) { + memset(d, 0, sizeof(OEMCrypto_DestBufferDesc)); + d->type = OEMCrypto_BufferType_Clear; + } +} diff --git a/oemcrypto/opk/serialization/common/message_debug.c b/oemcrypto/opk/serialization/common/message_debug.c new file mode 100644 index 0000000..419e16a --- /dev/null +++ b/oemcrypto/opk/serialization/common/message_debug.c @@ -0,0 +1,114 @@ +/* + * 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 "message_debug.h" + +#include +#include +#include + +#ifdef ENABLE_MESSAGE_DEBUG + +size_t opk_message_debug_id_ = 0; + +/* + * Format the current time in month-day h:m:s.ms, return as char* + */ +char* OPK_MessageDebug_Time(void) { + static char timebuf[24]; + time_t now = time(NULL); + size_t offset = + strftime(timebuf, sizeof(timebuf), "%m-%d %H:%M:%S", localtime(&now)); + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + snprintf(&timebuf[offset], sizeof(timebuf) - offset, ".%03ld", + ts.tv_nsec / 1000000); + return timebuf; +} + +/* + * Format a pointer and optionally its derefenced value if the pointer is + * non-null. The format string for the value is passed in |format|. The pointer + * is passed If |ptr| is NULL, return "", otherwise return + * ", *"|ptr_name|=" , where is *ptr formatted with |format| + * x = (null) + * x = 0x12345678, *x=42 + * + * Parameters: + * ptr - the pointer to format, as a void* + * ptr_name - the name of the pointer variable + * format - the printf-style format suitable for printing the dereferenced + * value + * value - the value of *|ptr|, or 0 if ptr is NULL + * + * Returns: + * a pointer to the formated result, in static memory + */ +const char* OPK_Pointer_Str(const void* ptr, const char* ptr_name, + uint64_t value) { + static char buf[80]; + if (!ptr) { + snprintf(buf, sizeof(buf), "%s=(null)", ptr_name); + } else { + snprintf(buf, sizeof(buf), "%s=%p, *%s=%llu", ptr_name, ptr, ptr_name, + (unsigned long long)value); + } + return buf; +} + +/* + * Format a sequence of bytes a string that can be logged. The output is + * formatted as some number of bytes in hex, where the number of bytes is the + * smaller of the number of bytes in the range and the number of formatted bytes + * that will fit in the buffer. If the memory range is too large to fit in the + * buffer, "..." will be appended to the end of the string. + * + * Parameters: + * address - the beginning address of the bytes to dump + * length - the length of the memory range + * + * Return: + * The address of the buffer the data is formatted in + */ +const char* OPK_Bytes_Str(const uint8_t* address, size_t length) { + if (!address) { + return "(null)"; + } + static char buffer[64]; + size_t offset = 0; + for (size_t i = 0; i < length; i++) { + offset += sprintf(&buffer[offset], "%02x ", address[i]); + if (offset > sizeof(buffer) - 7) { + sprintf(&buffer[offset], "..."); + break; + } + } + return buffer; +} + +/* + * Format a range of memory as a string that can be logged. The dump is + * formatted as the address followed the sequence of bytes returned by + * OPK_Bytes_Str() + * + * Parameters: + * address - the beginning address of the memory to dump + * length - the length of the memory range + * + * Return: + * The address of the buffer the data is formatted in + */ +const char* OPK_Memory_Str(const uint8_t* address, size_t length) { + if (!address) { + return "(null)"; + } + static char buffer[80]; + snprintf(buffer, sizeof(buffer), "%p: %s", (const void*)address, + OPK_Bytes_Str(address, length)); + return buffer; +} + +#endif /* ENABLE_MESSAGE_DEBUG */ diff --git a/oemcrypto/opk/serialization/common/opk_init.c b/oemcrypto/opk/serialization/common/opk_init.c new file mode 100644 index 0000000..8758b41 --- /dev/null +++ b/oemcrypto/opk/serialization/common/opk_init.c @@ -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 OPK_INIT_H_ +#define OPK_INIT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "shared_buffer_allocator.h" +#include "tos_shared_memory_interface.h" +#include "tos_transport_interface.h" + +/* + * See os_interfaces/opk_init.h for descriptions + * of the initialization functions. + */ + +bool OPK_Initialize(void) { + bool result = TOS_Transport_Initialize(); + if (!result) { + return false; + } + result = OPK_SharedBuffer_Initialize(); + if (!result) { + TOS_Transport_Terminate(); + return false; + } + return true; +} + +void OPK_Terminate(void) { + OPK_SharedBuffer_Terminate(); + TOS_Transport_Terminate(); +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPK_INIT_H_ diff --git a/oemcrypto/opk/serialization/common/opk_serialization_base.c b/oemcrypto/opk/serialization/common/opk_serialization_base.c new file mode 100644 index 0000000..537fff1 --- /dev/null +++ b/oemcrypto/opk/serialization/common/opk_serialization_base.c @@ -0,0 +1,447 @@ +/* + * 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 "opk_serialization_base.h" + +#include +#include +#include +#include + +#include "OEMCryptoCENC.h" +#include "bump_allocator.h" +#include "log_macros.h" +#include "marshaller_base.h" +#include "message_debug.h" +#include "odk_message_priv.h" +#include "odk_overflow.h" +#include "shared_buffer_allocator.h" +#include "tos_shared_memory_interface.h" + +typedef enum { + TAG_INVALID, + TAG_BOOL, + TAG_INT, + TAG_SIZE_T, + TAG_UINT8, + TAG_UINT16, + TAG_UINT32, + TAG_UINT64, + TAG_MEMORY, + TAG_SHARED_MEMORY, + TAG_EOM +} TagType; + +/* + * An ODK_Message_Impl pointer must only be obtained by calling + * GetMessageImpl(). This forces a message to pass the validity check before + * being operated on, which means that no function can modify or access the + * internals of a message without having the message be validated first. + * + * GetMessageImpl is static to this file, so only the message manipulation + * functions in this file can modifify message fields directly. No other files + * are permitted to include odk_message_priv.h, so the internal message + * structure is only known here. + */ +static ODK_Message_Impl* GetMessageImpl(ODK_Message* message) { + if (!ODK_Message_IsValid(message)) return NULL; + return (ODK_Message_Impl*)message; +} + +/* + * Pack |count| bytes from |ptr| into |message| + */ +static void PackBytes(ODK_Message* message, const uint8_t* ptr, size_t count) { + ODK_Message_Impl* message_impl = GetMessageImpl(message); + if (!message_impl) return; + if (!ptr || !count) return; + size_t new_size = 0; + if (odk_add_overflow_ux(message_impl->size, count, &new_size) || + new_size > message_impl->capacity) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_OVERFLOW_ERROR); + } else { + if (message_impl->base) { + memcpy((void*)(message_impl->base + message_impl->size), ptr, count); + } + message_impl->size = new_size; + } +} + +static void PackTag(ODK_Message* message, TagType tag) { + uint8_t byte = (uint8_t)tag; + PackBytes(message, &byte, sizeof(byte)); +} + +void OPK_Pack_bool(ODK_Message* message, const bool* value) { + PackTag(message, TAG_BOOL); + assert(value); + uint8_t b = (*value) ? 1 : 0; + PackBytes(message, &b, sizeof(b)); +} + +static void Pack64(ODK_Message* message, uint64_t u64) { + uint8_t buf[sizeof(uint64_t)]; + buf[0] = (uint8_t)u64; + buf[1] = (uint8_t)(u64 >> 8); + buf[2] = (uint8_t)(u64 >> 16); + buf[3] = (uint8_t)(u64 >> 24); + buf[4] = (uint8_t)(u64 >> 32); + buf[5] = (uint8_t)(u64 >> 40); + buf[6] = (uint8_t)(u64 >> 48); + buf[7] = (uint8_t)(u64 >> 56); + PackBytes(message, buf, sizeof(buf)); +} + +void OPK_Pack_int(ODK_Message* message, const int* value) { + PackTag(message, TAG_INT); + assert(value); + Pack64(message, *value); +} + +void OPK_Pack_size_t(ODK_Message* message, const size_t* value) { + PackTag(message, TAG_SIZE_T); + assert(value); + Pack64(message, *value); +} + +void OPK_Pack_uint8_t(ODK_Message* message, const uint8_t* value) { + PackTag(message, TAG_UINT8); + PackBytes(message, (const uint8_t*)value, sizeof(*value)); +} + +void OPK_Pack_uint16_t(ODK_Message* message, const uint16_t* value) { + PackTag(message, TAG_UINT16); + uint8_t buf[sizeof(uint16_t)]; + assert(value); + buf[0] = (uint8_t)(*value); + buf[1] = (uint8_t)(*value >> 8); + PackBytes(message, buf, sizeof(buf)); +} + +void OPK_Pack_uint32_t(ODK_Message* message, const uint32_t* value) { + PackTag(message, TAG_UINT32); + uint8_t buf[sizeof(uint32_t)]; + assert(value); + buf[0] = (uint8_t)(*value); + buf[1] = (uint8_t)(*value >> 8); + buf[2] = (uint8_t)(*value >> 16); + buf[3] = (uint8_t)(*value >> 24); + PackBytes(message, buf, sizeof(buf)); +} + +void OPK_Pack_uint64_t(ODK_Message* message, const uint64_t* value) { + PackTag(message, TAG_UINT64); + assert(value); + Pack64(message, *value); +} + +bool OPK_PackBoolValue(ODK_Message* message, bool value) { + OPK_Pack_bool(message, (const bool*)&value); + return value; +} + +bool OPK_PackIsNull(ODK_Message* message, const void* pointer) { + return OPK_PackBoolValue(message, pointer == NULL); +} + +void OPK_PackAlloc(ODK_Message* message, const void* addr) { + OPK_PackIsNull(message, addr); +} + +void OPK_PackMemory(ODK_Message* message, const uint8_t* address, + LengthType length) { + PackTag(message, TAG_MEMORY); + OPK_PackBoolValue(message, address == NULL || OPK_LengthIsNull(length)); + if (address && !OPK_LengthIsNull(length)) { + size_t count = OPK_ToSizeT(length); + OPK_Pack_size_t(message, &count); + if (OPK_ToSizeT(length) > 0) { + PackBytes(message, address, OPK_ToSizeT(length)); + } + } +} + +void OPK_PackArray(ODK_Message* message, const uint8_t* address, + size_t length) { + OPK_PackMemory(message, address, OPK_ToLengthType(length)); +} + +void OPK_PackSharedBuffer(ODK_Message* message, const uint8_t* address, + LengthType length, bool map, bool copy_in, + bool is_output) { + size_t len = OPK_ToSizeT(length); + PackTag(message, TAG_SHARED_MEMORY); + OPK_PackBoolValue(message, address == NULL || OPK_LengthIsNull(length)); + if (address && !OPK_LengthIsNull(length)) { + MDBG(BOLD YELLOW "Shared Memory " COLOR_OFF + "address=%p, length=%zu, map=%s, copy_in=%s," + " is_output=%s", + (const void*)address, len, map ? "true" : "false", + copy_in ? "true" : "false", is_output ? "true" : "false"); + uint8_t* shared_address = NULL; + if (map) { + bool copy_out = is_output; + /* + * Casting away const from |address| here because if copy_out is true, + * data may need to be copied out of shared memory to the REE memory + * referenced by |address|. This is safe because the only time it will + * happen is if the parameter being packed is an output and so will of + * course be writeable. + */ + shared_address = OPK_SharedBuffer_Allocate( + (uint8_t*)(uintptr_t)address, len, copy_in, copy_out, is_output); + if (shared_address) { + MDBG(" -> " BLUE "new map " COLOR_OFF "%p..%p [len=%zu]", + (const void*)shared_address, + (const void*)(shared_address + len - 1), len); + } else { + MDBG(RED " -> map failed: NULL" COLOR_OFF); + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_MAP_SHARED_MEMORY_FAILED); + } + } else { + if (address) { + MDBG(" -> " BLUE "existing map " COLOR_OFF "%p..%p [len=%zu]", + (const void*)address, (const void*)(address + len - 1), len); + } else { + MDBG(RED " -> existing map is NULL" COLOR_OFF); + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_MAP_SHARED_MEMORY_FAILED); + } + } + } +} + +void OPK_PackEOM(ODK_Message* message) { PackTag(message, TAG_EOM); } + +/**************************** Unpack Functions *******************************/ + +static void UnpackBytes(ODK_Message* message, uint8_t* ptr, size_t count) { + ODK_Message_Impl* message_impl = GetMessageImpl(message); + if (!message_impl) return; + size_t new_offset = 0; + if (!message_impl->base || + odk_add_overflow_ux(message_impl->read_offset, count, &new_offset) || + new_offset > message_impl->size) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_UNDERFLOW_ERROR); + } else { + if (ptr) { + memcpy((void*)ptr, + (void*)(message_impl->base + message_impl->read_offset), count); + } + message_impl->read_offset = new_offset; + } +} + +static bool CheckTag(ODK_Message* message, TagType tag) { + if (!ODK_Message_IsValid(message)) { + return false; + } + uint8_t byte = TAG_INVALID; + UnpackBytes(message, &byte, sizeof(byte)); + if (tag != (TagType)byte) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_INVALID_TAG_ERROR); + return false; + } + return true; +} + +void OPK_Unpack_bool(ODK_Message* message, bool* value) { + if (!CheckTag(message, TAG_BOOL)) return; + uint8_t b = 0; + UnpackBytes(message, &b, sizeof(b)); + assert(value); + *value = b ? true : false; +} + +static uint64_t Unpack64(ODK_Message* message) { + uint8_t buf[sizeof(uint64_t)]; + UnpackBytes(message, buf, sizeof(buf)); + uint64_t u64 = buf[0]; + u64 |= (uint64_t)buf[1] << 8; + u64 |= (uint64_t)buf[2] << 16; + u64 |= (uint64_t)buf[3] << 24; + u64 |= (uint64_t)buf[4] << 32; + u64 |= (uint64_t)buf[5] << 40; + u64 |= (uint64_t)buf[6] << 48; + u64 |= (uint64_t)buf[7] << 56; + return u64; +} + +void OPK_Unpack_int(ODK_Message* message, int* value) { + if (!CheckTag(message, TAG_INT)) return; + assert(value); + *value = (int)Unpack64(message); +} + +void OPK_Unpack_size_t(ODK_Message* message, size_t* value) { + if (!CheckTag(message, TAG_SIZE_T)) return; + assert(value); + *value = (size_t)Unpack64(message); +} + +void OPK_Unpack_uint8_t(ODK_Message* message, uint8_t* value) { + if (!CheckTag(message, TAG_UINT8)) return; + UnpackBytes(message, (uint8_t*)value, sizeof(*value)); +} + +void OPK_Unpack_uint16_t(ODK_Message* message, uint16_t* value) { + if (!CheckTag(message, TAG_UINT16)) return; + assert(value); + uint8_t buf[sizeof(uint16_t)]; + UnpackBytes(message, buf, sizeof(buf)); + *value = buf[0]; + *value |= (uint16_t)buf[1] << 8; +} + +void OPK_Unpack_uint32_t(ODK_Message* message, uint32_t* value) { + if (!CheckTag(message, TAG_UINT32)) return; + assert(value); + uint8_t buf[sizeof(uint32_t)]; + UnpackBytes(message, buf, sizeof(buf)); + *value = buf[0]; + *value |= (uint32_t)buf[1] << 8; + *value |= (uint32_t)buf[2] << 16; + *value |= (uint32_t)buf[3] << 24; +} + +void OPK_Unpack_uint64_t(ODK_Message* message, uint64_t* value) { + if (!CheckTag(message, TAG_UINT64)) return; + assert(value); + *value = Unpack64(message); +} + +bool OPK_UnpackBoolValue(ODK_Message* message) { + bool value = false; + OPK_Unpack_bool(message, &value); + return value; +} + +bool OPK_UnpackIsNull(ODK_Message* message) { + return OPK_UnpackBoolValue(message); +} + +void* OPK_UnpackAlloc(ODK_Message* message, size_t size) { + void* result = NULL; + if (ODK_Message_IsValid(message) && !OPK_UnpackIsNull(message)) { + result = OPK_BumpAllocate(size); + if (!result) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_OUT_OF_MEMORY); + } + } + return result; +} + +void* OPK_UnpackAllocBuffer(ODK_Message* message, LengthType count, + size_t size) { + void* buffer = NULL; + if (!ODK_Message_IsValid(message)) { + return NULL; + } + if (!OPK_UnpackIsNull(message) && !OPK_LengthIsNull(count)) { + size_t allocation_size = 0; + if (odk_mul_overflow_ux(OPK_ToSizeT(count), size, &allocation_size)) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_PARSE_ERROR); + } else { + buffer = OPK_BumpAllocate(allocation_size); + if (!buffer) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_OUT_OF_MEMORY); + } + } + } + return buffer; +} + +void OPK_UnpackArray(ODK_Message* message, uint8_t* address, size_t length) { + uint8_t* array = NULL; + OPK_UnpackInPlace(message, &array, OPK_ToLengthType(length)); + if (array && address) { + memcpy(address, array, length); + } +} + +void OPK_UnpackInPlace(ODK_Message* message, uint8_t** address, + LengthType expected_length) { + assert(address); + *address = NULL; + ODK_Message_Impl* message_impl = GetMessageImpl(message); + if (!message_impl) return; + if (!CheckTag(message, TAG_MEMORY)) return; + bool is_null = true; + OPK_Unpack_bool(message, &is_null); + if (is_null) { + return; + } else { + size_t length = 0; + OPK_Unpack_size_t(message, &length); + size_t bytes_expected = OPK_ToSizeT(expected_length); + if (bytes_expected && bytes_expected != length) { + message_impl->status = MESSAGE_STATUS_UNDERFLOW_ERROR; + return; + } + size_t new_offset = 0; + if (odk_add_overflow_ux(message_impl->read_offset, length, &new_offset) || + new_offset > message_impl->size) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_OVERFLOW_ERROR); + } else { + *address = message_impl->base + message_impl->read_offset; + message_impl->read_offset = new_offset; + } + } +} + +void OPK_UnpackSharedBuffer(ODK_Message* message, uint8_t** address, + LengthType length, bool map, bool is_output) { + assert(address); + if (OPK_LengthIsNull(length)) { + *address = NULL; + return; + } + size_t len = OPK_ToSizeT(length); + if (!CheckTag(message, TAG_SHARED_MEMORY)) return; + if (!OPK_UnpackIsNull(message)) { + MDBG(BOLD YELLOW "Shared Memory" COLOR_OFF + " length=%zu, map=%s, is_output=%s", + len, map ? "true" : "false", is_output ? "true" : "false"); + uint8_t* shared_address = NULL; + if (map) { + /* + * mapping a shared buffer while unpacking only happens in the TEE + * where copy_in or copy_out are never required because all accesses + * are directly to shared memory. + */ + bool copy_in = false; + bool copy_out = false; + shared_address = OPK_SharedBuffer_Allocate(*address, len, copy_in, + copy_out, is_output); + if (shared_address) { + MDBG(" -> " BLUE "new map " COLOR_OFF "%p..%p [len=%zu]", + (const void*)shared_address, + (const void*)(shared_address + len - 1), len); + } else { + MDBG(RED " -> map failed: NULL" COLOR_OFF); + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_MAP_SHARED_MEMORY_FAILED); + } + *address = shared_address; + } else { + shared_address = is_output ? OPK_SharedBuffer_NextOutputBuffer() + : OPK_SharedBuffer_NextBuffer(); + if (shared_address) { + MDBG(" -> " BLUE "existing map" COLOR_OFF " %p..%p [len=%zu]", + (const void*)shared_address, + (const void*)(shared_address + len - 1), len); + } else { + MDBG(RED " -> existing map is NULL" COLOR_OFF); + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_MAP_SHARED_MEMORY_FAILED); + } + } + } +} + +void OPK_UnpackEOM(ODK_Message* message) { + if (!CheckTag(message, TAG_EOM)) return; + if (ODK_Message_GetOffset(message) != ODK_Message_GetSize(message)) { + ODK_MESSAGE_SETSTATUS(message, MESSAGE_STATUS_END_OF_MESSAGE_ERROR); + } +} diff --git a/oemcrypto/opk/serialization/common/shared_buffer_allocator.c b/oemcrypto/opk/serialization/common/shared_buffer_allocator.c new file mode 100644 index 0000000..711cc23 --- /dev/null +++ b/oemcrypto/opk/serialization/common/shared_buffer_allocator.c @@ -0,0 +1,332 @@ +/* + * 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 + +#include "log_macros.h" +#include "message_debug.h" +#include "oemcrypto_overflow.h" +#include "shared_buffer_allocator.h" +#include "tos_shared_memory_interface.h" + +/* + * The Shared Buffer Allocator provides functions for allocating and + * iterating over shared buffers. A shared buffer is a chunk of + * shared memory that is allocated for passing an OEMCrypto parameter + * between the REE and the TEE. + * + * The Shared Memory Interface supplies a block of shared memory that + * all shared buffers are allocated from. This block of shared memory + * gets carved up into contiguous shared buffers which are defined + * by an offset from the beginning of the block and a length. + * Allocating a shared buffer means carving out the next consecutive + * buffer of the requested size from the shared memory block. + * + * Each shared buffer is referenced by an index number. Given an index + * number, the offset and length of the allocation can be looked + * up. The sequence of allocations in the REE during serialization is + * guaranteed to be the same as the sequence of allocations in the TEE + * during deserialization, so corresponding shared buffer indexes in + * the REE and the TEE appear at the same offsets and sizes in the + * shared memory block. + * + * At the beginning of each OEMCrypto API call the allocator is reset + * so there are no allocations. During request packing in the REE, a + * shared buffer is allocated for each input or output parameter of + * type OEMCrypto_SharedMemory*. During request unpacking in + * the TEE, a shared buffer is similarly allocated for each parameter. + * + * When the dispatcher in the TEE initializes arguments that will be + * passed into the OEMCrypto API function in the TA, it iterates over + * the allocated shared buffers, passing the address of each + * consecutive shared buffer into the appropriate parameter in the + * OEMCrypto function call. The OEMCrypto function then operates on + * the data in the shared buffers, reading input data and writing + * output data directly from/to the shared memory. After the API call + * completes, the response message is packed and delivered to + * the REE which can then access the result from the shared memory. + */ + +/* + * Descriptor for a shared buffer allocation + */ +typedef struct { + uint8_t* address; /* Address of the parameter in non-shared memory */ + size_t offset; /* Offset into the shared memory block of this buffer */ + size_t length; /* size of the buffer in bytes */ + bool copy_in; /* copy into buffer from |address| in finalize? */ + bool copy_out; /* copy from the buffer into |address| in finalize? */ + bool is_output; /* true if the parameter is an output parameter */ +} Allocation; + +/* + * The number of shared buffers that can be allocated + */ +#define MAX_SHARED_BUFFERS 32 +static Allocation allocations_[MAX_SHARED_BUFFERS]; + +/* + * Define indexes for shared buffers. The indexes are managed + * internally, they are not exposed directly to the caller. For each + * OEMCrypto call the index starts at 0 and increments sequentially on + * each use. + * + * |next_buffer_index_| is incremented each time a shared buffer is + * allocated with OPK_SharedBuffer_Allocate(), and when the next + * buffer is addressed using OPK_SharedBuffer_NextBuffer(). The code + * generator guarantees that buffers are allocated in the same order + * in both the REE and the TEE. + */ + +/* + * The index of the next buffer to be allocated + */ +static size_t next_buffer_index_ = 0; + +/* + * The offset into the shared memory block of the next buffer + * allocation + */ +static size_t next_buffer_offset_ = 0; + +/* + * The number of buffers allocated + */ +static size_t buffer_count_ = 0; + +/* + * Initialize shared memory state variables. + */ +bool OPK_SharedBuffer_Initialize() { + if (TOS_SharedMemory_Allocate(TOS_SharedMemory_AvailableSize())) { + OPK_SharedBuffer_Reset(); + return true; + } + return false; +} + +/* + * Release shared memory resources + */ +void OPK_SharedBuffer_Terminate() { + OPK_SharedBuffer_Reset(); + TOS_SharedMemory_Release(); +} + +/* + * Set the allocator to its initial state, where there are no buffers + * allocated. + */ +void OPK_SharedBuffer_Reset(void) { + memset(&allocations_[0], 0, sizeof(allocations_)); + next_buffer_offset_ = 0; + next_buffer_index_ = 0; + buffer_count_ = 0; +} + +/* + * Allocate a buffer from the serialization shared memory block. This + * reserves a range of bytes of shared memory to store the + * serialization parameter referenced by |address|. |copy_in| and + * |copy_out| indicate if the contents of the parameter need to be + * copied when OPK_SharedBuffer_Finalize is called. + * + * Parameters: + * address - location of the parameter in non-shared REE + * memory. This will be the source or destination of the copy + * operation if copy_in or copy_out is true. + * size - the size of the buffer to allocate + * copy_in - set to true if data must be copied from |address| into + * the buffer + * copy_out - set to true if data must be copied to |address| from + * the buffer + * is_output - set to true if the buffer is associated with an output + * parameter type + * + * Returns: + * The address of the allocation or NULL if allocation fails + */ +uint8_t* OPK_SharedBuffer_Allocate(uint8_t* address, size_t size, bool copy_in, + bool copy_out, bool is_output) { + size_t index = next_buffer_index_; + if (index >= MAX_SHARED_BUFFERS) { + LOGE("Exceeded max shared buffer allocations"); + return NULL; + } + size_t new_offset = 0; + if (OPK_AddOverflowUX(next_buffer_offset_, size, &new_offset)) { + return NULL; + } + if (new_offset > TOS_SharedMemory_GetSize()) { + LOGE("Insufficient shared memory"); + return NULL; + } + + Allocation* alloc = &allocations_[index]; + alloc->address = address; + alloc->offset = next_buffer_offset_; + alloc->length = size; + alloc->copy_in = copy_in; + alloc->copy_out = copy_out; + alloc->is_output = is_output; + + uint8_t* buffer_address = TOS_SharedMemory_GetAddress() + next_buffer_offset_; + next_buffer_offset_ = new_offset; + next_buffer_index_++; + buffer_count_++; + return buffer_address; +} + +/* + * Called after all shared buffer allocations have been completed + * during packing, this function synchronizes shared memory + * buffers with non-shared memory by copying data into the + * the buffers. It also prepares the indexes for iteration over the + * buffers that have been allocated. + */ +void OPK_SharedBuffer_FinalizePacking(void) { + /* Finalize copy ins */ + OPK_SharedMemory_CopyVector copy_vec[MAX_SHARED_BUFFERS]; + size_t vec_len = 0; + + for (size_t i = 0; i < buffer_count_; i++) { + Allocation* alloc = &allocations_[i]; + if (alloc->copy_in) { + MDBG(" -> " BLUE "copy in " COLOR_OFF "%zu bytes from %p to %p", + alloc->length, (const void*)alloc->address, + (const void*)TOS_SharedMemory_GetAddress() + alloc->offset); + MDBG(" -> %s", OPK_Memory_Str(alloc->address, alloc->length)); + OPK_SharedMemory_CopyVector* copy = ©_vec[vec_len++]; + copy->address = alloc->address; + copy->offset = alloc->offset; + copy->length = alloc->length; + } + } + TOS_SharedMemory_Finalize(copy_vec, vec_len, NULL, 0); + + /* Prepare next_buffer_index for iteration of allocated buffers */ + next_buffer_index_ = 0; +} + +/* + * Called after all shared buffer allocations have been completed + * during unpacking, this function synchronizes shared memory + * buffers with REE non-shared memory by copying data out of the + * the buffers. + */ +void OPK_SharedBuffer_FinalizeUnpacking(void) { + /* Finalize copy outs */ + OPK_SharedMemory_CopyVector copy_vec[MAX_SHARED_BUFFERS]; + size_t vec_len = 0; + + for (size_t i = 0; i < buffer_count_; i++) { + Allocation* alloc = &allocations_[i]; + if (alloc->copy_out) { + MDBG(" -> " BLUE "copy out" COLOR_OFF " %zu bytes from %p to %p", + alloc->length, + (const void*)TOS_SharedMemory_GetAddress() + alloc->offset, + (const void*)alloc->address); + MDBG(" -> %s", + OPK_Memory_Str( + (const void*)TOS_SharedMemory_GetAddress() + alloc->offset, + alloc->length)); + OPK_SharedMemory_CopyVector* copy = ©_vec[vec_len++]; + copy->address = alloc->address; + copy->offset = alloc->offset; + copy->length = alloc->length; + } + } + TOS_SharedMemory_Finalize(NULL, 0, copy_vec, vec_len); + + /* Prepare next_buffer_index for iteration of allocated buffers */ + next_buffer_index_ = 0; +} + +/* + * Advance the current index to the next shared buffer that has been + * allocated. The current buffer is at |next_buffer_index_| - 1. The + * address of the buffer referenced by the current index can be + * obtained from OPK_SharedBuffer_GetAddress(). On return, + * |next_buffer_index_| will be incremented by 1, if there is another + * allocation. + * + * Parameters: + * None + * + * Returns: + * The address of the next shared buffer, or NULL if there isn't one + */ +uint8_t* OPK_SharedBuffer_NextBuffer(void) { + if (next_buffer_index_ < buffer_count_) { + Allocation* alloc = &allocations_[next_buffer_index_++]; + return TOS_SharedMemory_GetAddress() + alloc->offset; + } + return NULL; +} + +/* + * Advance the current index to the next shared output buffer that has + * been allocated. The current buffer is at |next_buffer_index_| - 1. + * On return, |next_buffer_index_| is incremented until it references + * the next buffer of type SHARED_BUFFER_TYPE_OUTPUT, as long there + * are additional allocations. On return |next_buffer_index| will + * reference the output buffer, i.e. it will be equal to the output + * buffer's index + 1. + * + * Parameters: + * None + * + * Returns: + * The address of the next shared output buffer, or NULL if there is no + * next output buffer allocation + */ +uint8_t* OPK_SharedBuffer_NextOutputBuffer(void) { + while (next_buffer_index_ < buffer_count_) { + Allocation* alloc = &allocations_[next_buffer_index_++]; + if (alloc->is_output) { + return TOS_SharedMemory_GetAddress() + alloc->offset; + } + } + return NULL; +} + +/* + * Get the address of the currently referenced shared memory segment which + * is at next_buffer_index_ - 1. + * + * Parameters: + * None + * + * Returns: + * The address of the allocation at next_allocation_index - 1 + * NULL if there are no matching allocations. + */ +uint8_t* OPK_SharedBuffer_GetAddress(void) { + if (next_buffer_index_ == 0 || next_buffer_index_ > buffer_count_) { + return NULL; + } + Allocation* alloc = &allocations_[next_buffer_index_ - 1]; + return TOS_SharedMemory_GetAddress() + alloc->offset; +} + +/* + * Get the size of the currently referenced shared buffer allocation which + * is at next_buffer_index_ - 1. + * + * Parameters: + * None + * + * Returns: + * The size of the allocation at next_allocation_index - 1 + * 0 if there are no matching allocations. + */ +size_t OPK_SharedBuffer_GetCurrentBufferSize(void) { + if (next_buffer_index_ == 0 || next_buffer_index_ > buffer_count_) { + return 0; + } + Allocation* alloc = &allocations_[next_buffer_index_ - 1]; + return alloc->length; +} diff --git a/oemcrypto/opk/serialization/os_interfaces/opk_dispatcher.h b/oemcrypto/opk/serialization/os_interfaces/opk_dispatcher.h new file mode 100644 index 0000000..22e7e76 --- /dev/null +++ b/oemcrypto/opk/serialization/os_interfaces/opk_dispatcher.h @@ -0,0 +1,85 @@ +/* + * 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 OPK_DISPATCHER_INTERFACE_H_ +#define OPK_DISPATCHER_INTERFACE_H_ + +#include "opk_serialization_base.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The Dispatcher is the entry point in the Widevine Trusted App (TA) that + * receives messages, deserializes them into local variables as needed, and + * invokes the correct OEMCrypto API function in the TA. After the function is + * executed, the results of the function are packed into a response message that + * is returned to the HLOS REE. + * + * Note that the dispatcher is solely responsible for the TEE side of the + * transport interface. The transport_interface is not used for the TEE side + * serialization/deserialization. + * + * The trusted app must implement message receive and send functions, + * using any transport method it chooses. The transport mechanism must + * provide a message delivery protocol that implements the functions + * declared in os_interfaces/tos_transport_interface.h. + * + * The message receive function must receive a buffer of data and associate it + * with an ODK_Message using ODK_Message_Create, which is defined in the file + * odk/include/odk_message.h. If the message is passed through shared memory, + * the data in the message buffer must be copied into memory that is only + * accessible to the TEE prior to creating the ODK_Message. + * + * OPK_DispatchMessage unpacks the data buffer from the received ODK_Message + * request and calls into the OEMCrypto function specified in the message. After + * the call completes, the results are packed into a response message that is + * returned to the caller, which is the trusted app. + * + * The message delivery protocol must handle timeouts and error conditions. + * Those are out of scope of this function. + * + * OPK_Initialize() must be called before using OPK_DispatchMesage(). + * + * Parameters: + * request - The received message that is to be processed. The caller + * retains ownership of the request messsage and must release any + * memory associated with it after OPK_DispatchMessage returns. + * + * response - A newly created message that should be sent to the REE using + * a message delivery protocol. The caller takes ownership of the request + * messsage and must release any memory associated with it after + * OPK_DispatchMessage returns. + * + * Return value: + * If |request| or |response| are NULL, MESSAGE_STATUS_NULL_POINTER_ERROR will + * be returned. + * + * If the provided request message is invalid or there is an error in + * unpacking the message, *|response| will be a valid message to send, but + * will be an empty message consisting of only of the end-of-message tag. This + * message must be delivered to ensure that the communication is not broken, + * due to the sender waiting for a reply. In this case, the return value will + * be MESSAGE_STATUS_OK, indicating that the creation of the empty response + * was successful and the response is a valid message. The status of the + * request message will be set to the error that occurred while it was being + * unpacked. + * + * If there is an error allocating the response message, *|response| will be + * NULL and MESSAGE_STATUS_OUT_OF_MEMORY will be returned. + * + * Otherwise *|response| will be set to the status of packing the response + * message. The caller takes ownership of the response message and must + * dispose of it after sending. + */ +ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, + ODK_Message* response); + +#ifdef __cplusplus +} +#endif + +#endif /* OPK_DISPATCHER_INTERFACE_H_ */ diff --git a/oemcrypto/opk/serialization/os_interfaces/opk_init.h b/oemcrypto/opk/serialization/os_interfaces/opk_init.h new file mode 100644 index 0000000..7b62281 --- /dev/null +++ b/oemcrypto/opk/serialization/os_interfaces/opk_init.h @@ -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 OPK_INIT_H_ +#define OPK_INIT_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Initialize the OPK serialization library. In the REE this must be + * called by the serialization adapter when the liboemcrypto.so + * library is loaded. In the TEE it must be called prior to using + * the dispatcher. + * + * Return true on success, false on failure + */ +bool OPK_Initialize(void); + +/* + * Terminate the OPK serialization library. In the REE This must be + * called by the serialization adapter when the liboemcrypto.so + * library is unloaded. In the TEE it must be called with the trusted + * app exits. + */ +void OPK_Terminate(void); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPK_INIT_H_ diff --git a/oemcrypto/opk/serialization/os_interfaces/tos_logging_interface.h b/oemcrypto/opk/serialization/os_interfaces/tos_logging_interface.h new file mode 100644 index 0000000..603c3ef --- /dev/null +++ b/oemcrypto/opk/serialization/os_interfaces/tos_logging_interface.h @@ -0,0 +1,59 @@ +/* + * 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 TOS_LOGGING_INTERFACE_H_ +#define TOS_LOGGING_INTERFACE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup tos-logging TOS Logging Interface + * + * Logging interface used by the serialization code. + * + * This is the interface to logging that must be implemented in a port + * for a specific trusted OS. The interface defines a function to log + * messages of various levels. + * + * @{ + */ + +/** Log level for use with TOS_Log(). + */ +typedef enum { + OPK_LogLevel_Verbose = 0, + OPK_LogLevel_Debug = 1, + OPK_LogLevel_Info = 2, + OPK_LogLevel_Warning = 3, + OPK_LogLevel_Error = 4, + OPK_LogLevel_Fatal = 5 +} OPK_LogLevel; + +/** + * Write a log message. + * + * The destination of the log message is defined by the TOS implementation. + * The format string contains printf style formatting characters that control + * how subsequent arguments are converted for output. + * + * @param[in] file: the name of the source file where the log message was + * generated + * @param[in] func: the name of the function that generated the log message + * @param[in] line: the line number in |file| of the log message + * @param[in] level: the log level, indicating the severity of the message + * @param[in] fmt: printf style format string + */ +void TOS_Log(const char* file, const char* func, int line, OPK_LogLevel level, + const char* fmt, ...); + +/// @} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TOS_LOGGING_INTERFACE_H_ diff --git a/oemcrypto/opk/serialization/os_interfaces/tos_secure_buffer_interface.h b/oemcrypto/opk/serialization/os_interfaces/tos_secure_buffer_interface.h new file mode 100644 index 0000000..1d43ad2 --- /dev/null +++ b/oemcrypto/opk/serialization/os_interfaces/tos_secure_buffer_interface.h @@ -0,0 +1,108 @@ +/* + * 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 TOS_SECURE_BUFFER_INTERFACE_H_ +#define TOS_SECURE_BUFFER_INTERFACE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include "OEMCryptoCENC.h" +#include "odk_message.h" + +/** @defgroup tos-secure-buffer TOS Secure Buffer Interface + * + * Functions used to pass a secure buffer handle from the REE to the TEE. + * + * The secure buffer interface is responsible for serializing secure output + * buffer descriptors in messages sent from the REE to the TEE during decrypt + * and copy operations. + * + * These interface functions need to be defined in both the REE and TEE + * libraries because the functions are called from code that is common to + * both. However TOS_SecureBuffer_Pack() will never be called in the TEE + * library, and TOS_SecureBuffer_Unpack() will never be called in the REE + * library. + * + * A secure buffer is a region of memory that is accessible by the TEE but not + * by the REE. It is used to pass decrypted content to the secure decoder. + * Secure buffers are integral to an L1 OEMCrypto implementation and must be + * supported with SoC hardware features to implement a protected video path. The + * data structure, or descriptor, used to reference a secure buffer is + * device-specific. The TOS\_SecureBuffer interface provides a way for the OPK + * to serialize and deserialize secure buffer descriptors in messages in a way + * that is device-independent. Widevine provides a secure buffer implementation + * for the Trusty OS. + * + * @{ + */ + +/** + * TOS_SecureBuffer_Pack is called during request message serialization in the + * REE. It receives the |secure| variant of the OEMCrypto_DestBufferDesc union, + * performs platform-specific operations using the data in the fields, then + * serializes fields into the message which the TEE will use to establish access + * to the secure output buffer memory region for decrypt or copy operations. On + * entry, the fields of descriptor->buffer.secure are set to the values supplied + * by the caller to OEMCrypto. + * + * If there is a fatal error in this function, set the message status to + * MESSAGE_STATUS_SECURE_BUFFER_ERROR using ODK_MESSAGE_SETSTATUS(). This will + * terminate message processing and prevent the message from being delivered + * to the TEE. + * + * @param[in,out] message: message to which the descriptor should be packed. + * @param[in] descriptor: descriptor of the secure buffer in the REE. + */ +void TOS_SecureBuffer_Pack(ODK_Message* message, + const OEMCrypto_DestBufferDesc* descriptor); + +/** + * TOS_SecureBuffer_Unpack is called during request message deserialization in + * the TEE. It unpacks the fields from the message that were packed by the REE + * in TOS_SecureBuffer_Pack, performs platform-specific operations using the + * data in the fields, then initializes the |secure| variant of the + * OEMCrypto_DestBufferDesc union. + * + * If there is a fatal error in this function, set the message status to + * MESSAGE_STATUS_SECURE_BUFFER_ERROR using ODK_MESSAGE_SETSTATUS(). This will + * terminate message processing and prevent the oemcrypto function from being + * called in the TEE. + * + * @param[in] message: message from which the descriptor should be unpacked. + * @param[out] descriptor: descriptor of the secure buffer in the TEE. + */ +void TOS_SecureBuffer_Unpack(ODK_Message* message, + OEMCrypto_DestBufferDesc* descriptor); + +/** + * TOS_SecureBuffer_CheckSize is called during request message deserialization + * in the TEE. Its job is to check that the secure buffer pointed to by + * |handle|, which was unpacked by a previous call to TOS_SecureBuffer_Unpack(), + * is at least as big as the given size. It should return true if the buffer + * pointed to by |handle| is at least |size| bytes in size and should return + * false otherwise. + * + * @param[in] handle: handle to secure buffer. + * @param[in] size: size to be verified. + * + * @return true if the buffer pointed to by |handle| is at least |size| bytes + * in size and should return false otherwise. + */ +bool TOS_SecureBuffer_CheckSize(void* handle, size_t size); + +/// @} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TOS_SECURE_BUFFER_INTERFACE_H_ diff --git a/oemcrypto/opk/serialization/os_interfaces/tos_shared_memory_interface.h b/oemcrypto/opk/serialization/os_interfaces/tos_shared_memory_interface.h new file mode 100644 index 0000000..5f36737 --- /dev/null +++ b/oemcrypto/opk/serialization/os_interfaces/tos_shared_memory_interface.h @@ -0,0 +1,163 @@ +/* + * 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 TOS_SHARED_MEMORY_INTERFACE_H_ +#define TOS_SHARED_MEMORY_INTERFACE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/** @defgroup tos-shared-memory TOS Shared Memory Interface + * + * Interface for sharing buffers across the REE/TEE boundary. + * + * This is the interface to share memory that must be implemented in + * a port for a specific trusted OS. The interface defines functions + * to allocate, access and free a block of shared memory. + * + * A shared memory block is memory that is shared between the + * non-secure REE and the secure TEE for the purpose of efficiently + * transferring data. The shared memory is allocated and managed + * in the REE by the trusted OS device driver. + * + * Shared memory is used to transfer OEMCrypto function arguments + * between the REE and the TEE. Only arguments that are safe to pass + * in shared memory are placed in this block. These arguments have the + * property that any concurrent modification of the data while it is + * being processed can only affect the output of the operation, it + * must not be able to alter the execution flow in any way. + * + * While shared memory mapping is driven from the REE side, this + * interface must be implemented in both the REE and the TEE. The + * implementations are expected to be completely different as they are + * layered on different APIs. However, having a common interface + * enables the serialization packing and unpacking code to be + * symmetrical in the REE and the TEE. + * + * Input parameter data in the non-shared REE memory must be copied + * into the shared memory block, and data in the shared memory block + * must be copied out to the output parameter non-shared memory in the + * REE. The copy-in and copy-out must be performed by the shared + * memory implementation, which is done in TOS_SharedMemory_Finalize() + * after all shared buffer allocations have been completed. + * + * @{ + */ + +/** + * The OPK_SharedMemory_CopyVector struct identifies which ranges of + * memory need to be copied. + * + * @param[in] address: the non-shared address to copy to/from + * @param[in] offset: offset from the beginning of the block + * @param[in] length: number of bytes to copy + */ +typedef struct { + void* address; /* the non-shared address to copy to/from */ + size_t offset; /* offset from the beginning of the block */ + size_t length; /* number of bytes to copy */ +} OPK_SharedMemory_CopyVector; + +/** + * Allocate a block of shared memory of at least |block_size| + * bytes. Once the shared memory block is allocated, it must remain + * allocated until TOS_SharedMemory_Release is called. + * + * This function must be implemented in the REE and must not + * be implemented in the TEE. + * + * @param[in] block_size: the size of the block to allocate, in bytes + * + * @return true on success, false on failure + */ +bool TOS_SharedMemory_Allocate(size_t block_size); + +/** + * Release the shared memory block that was previously allocated + * by TOS_SharedMemory_Allocate. + * + * This function must be implemented in the REE and must not + * be implemented in the TEE. + */ +void TOS_SharedMemory_Release(void); + +/** + * Return the base address for the shared memory block + * + * This function must be implemented in both the REE and the TEE + + * @return the base address for the shared memory block + */ +uint8_t* TOS_SharedMemory_GetAddress(void); + +/** + * Return the size of the shared memory block + * + * This function must be implemented in both the REE and the TEE + * + * @return the size of the shared memory block + */ +size_t TOS_SharedMemory_GetSize(void); + +/** + * Copy data into and/or out of the memory block + * + * This function is called after all shared buffers have been + * allocated, to sync REE non-shared memory with shared memory. + * + * Two vectors of bytes ranges to copy are provided. Each element in + * the |in_vec| vector identifies a range of bytes to be copied from + * the non-shared address to an offset in the shared memory + * block. Each element in the |out_vec| vector identies a range of + * bytes to be copied from an offset in the shared memory block to the + * non-shared memory address. + * + * This function must be implemented in the REE and must not be + * implemented in the TEE. In the TEE all operations on shared memory + * directly address the shared memory blocks so copy-in/copy-out are + * never required. + * + * @param[in] in_vec: a vector of copy operations describing the + * ranges of bytes to be copied in + * @param[in] in_count: the number of elements in the in_vec array + * @param[in] out_vec: a vector of copy operations describing the + * ranges of bytes to be copied out + * @param[in] out_count: the number of elements in the out_vec array + */ +void TOS_SharedMemory_Finalize(OPK_SharedMemory_CopyVector* in_vec, + size_t in_count, + OPK_SharedMemory_CopyVector* out_vec, + size_t out_count); + +/** + * An OPK TEE implementation must specify the amount of shared memory + * it can make available for passing data in shared memory blocks. + * The shared memory block must be large enough to accommodate the + * minimum sample size required by the OEMCrypto Resource Rating Tier. + * The block size should be 2 * Minimum_Sample_Size, plus 32 KiB for + * various parameters that are annotated as OEMCrypto_SharedMemory + * in OEMCryptoCENC.h. + * + * This function must be implemented in the REE and must not be + * implemented in the TEE. + * + * @return the size of available memory that may be shared between the REE and + * the TEE. + */ +size_t TOS_SharedMemory_AvailableSize(void); + +/// @} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TOS_SHARED_MEMORY_INTERFACE_H_ diff --git a/oemcrypto/opk/serialization/os_interfaces/tos_transport_interface.h b/oemcrypto/opk/serialization/os_interfaces/tos_transport_interface.h new file mode 100644 index 0000000..07f8e94 --- /dev/null +++ b/oemcrypto/opk/serialization/os_interfaces/tos_transport_interface.h @@ -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 TOS_TRANSPORT_INTERFACE_H_ +#define TOS_TRANSPORT_INTERFACE_H_ + +#include + +#include "odk_message.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @mainpage TOS API + * + * An SoC integration of the OPK must implement four Trusted OS interfaces. + * + */ + +/** @defgroup tos-transport TOS Transport Interface + * + * Functions for sending messages between the REE and TEE. + * + * The Transport Interface connects liboemcrypto to the trusted OS's + * method of transporting data between the REE and the TEE. + * + * Separate implementations of this interface are to be provided by a + * port of OPK, one provides services for the REE and the other for + * the TEE. The implementations do not need to be the same, but they + * must implement compatible end points of some message delivery + * protocol that is provided by a trusted OS. + * + * The transport interface includes functions to allocate, deallocate, + * send and receive messages. + * + * In the REE, the oemcrypto API functions use this API to allocate + * messages, send them to the TEE, receive replies and deallocate the + * messages when the function call is complete. + * + * In the TEE, the trusted app implements a message loop, which calls + * the receive function to get a message, then invokes the dispatcher + * function to process the message, and calls the send function to + * deliver the reply. This allows the trusted app to control the + * message processing loop. See dispatcher_interface.h for a + * definition of this interface. + * + * @{ + */ + +/** Error codes returned by TOS_Transport_SendMessage(). + */ +typedef enum { + /* + * OPK_TRANSPORT_STATUS_OK must be returned from transport functions + * if the requested operation completed successfully. + */ + OPK_TRANSPORT_STATUS_OK, + + /* + * OPK_TRANSPORT_STATUS_ALLOC_FAILED must be returned if there is + * insufficient memory. This is a fatal failure that will cause + * OEMCrypto to return OEMCrypto_ERROR_INSUFFICIENT_RESOURCES. + */ + OPK_TRANSPORT_STATUS_ALLOC_FAILED, + + /* + * OPK_TRANSPORT_STATUS_IO_ERROR must be returned from + * TOS_Transport_SendMessage or TOS_Transport_ReceiveMessage if the + * transport interface was unable to deliver or receive a message + * for any reason. The transport implementation should be designed + * to be robust against communication failures, e.g. by providing + * logic to retry delivery or other techniques if possible. The opk + * library does not make any attempts to improve communication + * reliability using these techniques. + * + * This return code indicates a fatal failure of the current command + * which will result in OEMCrypto returning + * OEMCrypto_ERROR_SYSTEM_INVALIDATED. This will cause the app to be + * notified that the drm system must be reinitialized. + */ + OPK_TRANSPORT_STATUS_IO_ERROR, +} OPK_TransportStatus; + +/** + * This is the required size for OPK messages. It is predefined so + * implementations can statically allocate the buffer. + */ +#define OPK_TRANSPORT_MESSAGE_SIZE (32 * 1024) + +/** + * Initialize the transport interface implementation. It is called by + * the OPK prior to calling any other TOS functions. This function is + * called by OPK_Initialize. + * + * Return true on success, false on failure + * + * This function must be implemented in both the REE and the TEE + */ +bool TOS_Transport_Initialize(void); + +/** + * Terminate the transport implementation, releasing any resources in + * use. The OPK must not call any other TOS functions after this + * function, other than TOS_Transport_Initialize. This function is + * called from OPK_Terminate. + * + * This function must be implemented in both the REE and the TEE + */ +void TOS_Transport_Terminate(void); + +/** + * Initialize a new request message with a payload buffer of size + * OPK_TRANSPORT_MESSAGE_SIZE bytes. Must return a message with NULL + * buffer if the transport implementation is unable to allocate the + * buffer for the message. The returned message must be created with + * ODK_Message_Create(). Only one request buffer will be allocated at a + * time. + * + * An implementation may opt to allocate the payload buffer of the + * message as shared memory to avoid having to copy the request data + * into the message at the time it is sent to the TEE. + * + * A single common payload buffer may be used for both the request and + * response messages. They are used sequentially, i.e. all of the data + * is unpacked from the request before any data is packed into the + * response, in both the REE and the TEE. + * + * @return + * The new message, which must be created by ODK_Message_Create(), + * which is defined in odk_message.h. + * + * This function must be implemented in the REE. + */ +ODK_Message TOS_Transport_GetRequest(void); + +/** + * Initialize a new response message with a payload buffer of size + * OPK_TRANSPORT_MESSAGE_SIZE bytes. Must return a message with NULL + * buffer if the transport implementation is unable to allocate the + * buffer for the message. The returned message must be created with + * ODK_Message_Create(). Only one response buffer will be allocated at + * a time. + * + * An implementation may opt to allocate the payload buffer of the + * message as shared memory to avoid having to copy the response data + * into the message at the time it is sent to the TEE. + * + * A single common payload buffer may be used for both the request and + * response messages. They are used sequentially, i.e. all of the data + * is unpacked from the request before any data is packed into the + * response, in both the REE and the TEE. + * + * @return + * The new message, which must be created by ODK_Message_Create(), + * which is defined in odk_message.h. + * + * This function must be implemented in the TEE. + */ +ODK_Message TOS_Transport_GetResponse(void); + +/** + * Release the payload message buffer for a message that was + * previously created with TOS_Transport_GetRequest or returned as + * the response from TOS_Transport_SendMessage. This function is + * called when the message is no longer needed. + * + * @param[in] message: a pointer to the message to release. Any + * resources associated with the message payload must be released. + * + * This function must be implemented in both the REE and the TEE + */ +void TOS_Transport_ReleaseMessage(ODK_Message* message); + +/** + * Send the request message to the TEE from the REE and receive the + * response. If the transaction is successful *response must be + * assigned to with a message that was created with OPK_Message_Create + * and a message payload buffer of size OPK_TRANSPORT_MESSAGE_SIZE and + * OPK_TRANSPORT_STATUS_OK must be returned. If unsuccessful *response + * must be set to a message with a NULL payload, which must be created + * with ODK_Message_Create(NULL, 0), and return + * OPK_TRANSPORT_STATUS_IO_ERROR. Ownership of the request remains + * with the caller, it must not be released. + * + * @return + * A transport status to indicate the status of the send/receive. If + * the receipt is successful OPK_TRANSPORT_STATUS_OK must be + * returned. If send/receive of the message fails, must return + * OPK_TRANSPORT_STATUS_IO_ERROR. + * + * This function must be implemented in the REE and must not be + * implemented in the TEE. + * + * @param[in] request: the request message to be sent. Must not be NULL. + * @param[out] response: a pointer to an ODK_Message that will be set + * to the response message on success and an empty message on + * failure + */ +OPK_TransportStatus TOS_Transport_SendMessage(ODK_Message* request, + ODK_Message* response); + +/// @} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* TOS_TRANSPORT_INTERFACE_H_ */ diff --git a/oemcrypto/opk/serialization/ree/GEN_oemcrypto_api.c b/oemcrypto/opk/serialization/ree/GEN_oemcrypto_api.c new file mode 100644 index 0000000..0ee5f33 --- /dev/null +++ b/oemcrypto/opk/serialization/ree/GEN_oemcrypto_api.c @@ -0,0 +1,2370 @@ +/* + * Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine License + * Agreement. + */ + +/* + * This code is auto-generated, do not edit + */ + +#include +#include + +#include "GEN_common_serializer.h" +#include "GEN_ree_serializer.h" +#include "OEMCryptoCENC.h" +#include "api_support.h" +#include "bump_allocator.h" +#include "log_macros.h" +#include "message_debug.h" +#include "ree_special_cases.h" +#include "ree_version.h" +#include "shared_buffer_allocator.h" +#include "tos_shared_memory_interface.h" +#include "tos_transport_interface.h" + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_SetSandbox(const uint8_t* sandbox_id, + size_t sandbox_id_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_SetSandbox_Request(sandbox_id, sandbox_id_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_SetSandbox_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_Initialize(void) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + if (!OPK_IsVersionCompatible()) { + api_result = OPK_ERROR_INCOMPATIBLE_VERSION; + goto cleanup_and_return; + } + request = OPK_Pack_Initialize_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_Initialize_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_Terminate(void) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_Terminate_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_Terminate_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + API_Terminate(); + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_Idle(OEMCrypto_IdleState state, + uint32_t os_specific_code) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_Idle_Request(state, os_specific_code); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_Idle_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_Wake(void) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_Wake_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_Wake_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult +OEMCrypto_OpenSession(OEMCrypto_SESSION* session) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_OpenSession_Request(session); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_OpenSession_Response(&response, &result, &session); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult +OEMCrypto_CloseSession(OEMCrypto_SESSION session) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_CloseSession_Request(session); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_CloseSession_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_CreateEntitledKeySession( + OEMCrypto_SESSION oec_session, OEMCrypto_SESSION* key_session) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_CreateEntitledKeySession_Request(oec_session, key_session); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_CreateEntitledKeySession_Response(&response, &result, + &key_session); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult +OEMCrypto_RemoveEntitledKeySession(OEMCrypto_SESSION key_session) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_RemoveEntitledKeySession_Request(key_session); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_RemoveEntitledKeySession_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_GenerateDerivedKeys( + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* mac_key_context, + size_t mac_key_context_length, + const OEMCrypto_SharedMemory* enc_key_context, + size_t enc_key_context_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GenerateDerivedKeys_Request( + session, mac_key_context, mac_key_context_length, enc_key_context, + enc_key_context_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GenerateDerivedKeys_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( + OEMCrypto_SESSION session, const uint8_t* derivation_key, + size_t derivation_key_length, const OEMCrypto_SharedMemory* mac_key_context, + size_t mac_key_context_length, + const OEMCrypto_SharedMemory* enc_key_context, + size_t enc_key_context_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_DeriveKeysFromSessionKey_Request( + session, derivation_key, derivation_key_length, mac_key_context, + mac_key_context_length, enc_key_context, enc_key_context_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_DeriveKeysFromSessionKey_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, + uint32_t* nonce) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GenerateNonce_Request(session, nonce); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GenerateNonce_Response(&response, &result, &nonce); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_PrepAndSignLicenseRequest( + OEMCrypto_SESSION session, uint8_t* message, size_t message_length, + size_t* core_message_size, uint8_t* signature, size_t* signature_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_PrepAndSignLicenseRequest_Request( + session, message, message_length, core_message_size, signature, + signature_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_PrepAndSignLicenseRequest_Response( + &response, &result, &message, &message_length, &core_message_size, + &signature, &signature_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_PrepAndSignRenewalRequest( + OEMCrypto_SESSION session, uint8_t* message, size_t message_length, + size_t* core_message_size, uint8_t* signature, size_t* signature_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_PrepAndSignRenewalRequest_Request( + session, message, message_length, core_message_size, signature, + signature_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_PrepAndSignRenewalRequest_Response( + &response, &result, &message, &message_length, &core_message_size, + &signature, &signature_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadKeys( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, + OEMCrypto_Substring enc_mac_keys_iv, OEMCrypto_Substring enc_mac_keys, + size_t key_array_length, const OEMCrypto_KeyObject* key_array, + OEMCrypto_Substring pst, OEMCrypto_Substring srm_restriction_data, + OEMCrypto_LicenseType license_type) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_LoadKeys_Request( + session, message, message_length, signature, signature_length, + enc_mac_keys_iv, enc_mac_keys, key_array_length, key_array, pst, + srm_restriction_data, license_type); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_LoadKeys_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadLicense(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + size_t core_message_length, + const uint8_t* signature, + size_t signature_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_LoadLicense_Request(session, message, message_length, + core_message_length, signature, + signature_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_LoadLicense_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadEntitledContentKeys( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + size_t key_array_length, + const OEMCrypto_EntitledContentKeyObject* key_array) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_LoadEntitledContentKeys_Request( + session, message, message_length, key_array_length, key_array); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_LoadEntitledContentKeys_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_RefreshKeys( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, size_t num_keys, + const OEMCrypto_KeyRefreshObject* key_array) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = + OPK_Pack_RefreshKeys_Request(session, message, message_length, signature, + signature_length, num_keys, key_array); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_RefreshKeys_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadRenewal(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + size_t core_message_length, + const uint8_t* signature, + size_t signature_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_LoadRenewal_Request(session, message, message_length, + core_message_length, signature, + signature_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_LoadRenewal_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_QueryKeyControl( + OEMCrypto_SESSION session, const uint8_t* content_key_id, + size_t content_key_id_length, uint8_t* key_control_block, + size_t* key_control_block_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_QueryKeyControl_Request( + session, content_key_id, content_key_id_length, key_control_block, + key_control_block_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_QueryKeyControl_Response(&response, &result, &key_control_block, + &key_control_block_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_SelectKey( + OEMCrypto_SESSION session, const uint8_t* content_key_id, + size_t content_key_id_length, OEMCryptoCipherMode cipher_mode) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_SelectKey_Request(session, content_key_id, + content_key_id_length, cipher_mode); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_SelectKey_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_DecryptCENC( + OEMCrypto_SESSION session, const OEMCrypto_SampleDescription* samples, + size_t samples_length, const OEMCrypto_CENCEncryptPatternDesc* pattern) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = + OPK_Pack_DecryptCENC_Request(session, samples, samples_length, pattern); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_DecryptCENC_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_CopyBuffer( + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* data_addr, + size_t data_addr_length, + const OEMCrypto_DestBufferDesc* out_buffer_descriptor, + uint8_t subsample_flags) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_CopyBuffer_Request(session, data_addr, data_addr_length, + out_buffer_descriptor, subsample_flags); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_CopyBuffer_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_Generic_Encrypt( + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* in_buffer, + size_t in_buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm, + OEMCrypto_SharedMemory* out_buffer) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_Generic_Encrypt_Request( + session, in_buffer, in_buffer_length, iv, algorithm, out_buffer); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_Generic_Encrypt_Response(&response, &result, &in_buffer_length, + &out_buffer); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_Generic_Decrypt( + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* in_buffer, + size_t in_buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm, + OEMCrypto_SharedMemory* out_buffer) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_Generic_Decrypt_Request( + session, in_buffer, in_buffer_length, iv, algorithm, out_buffer); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_Generic_Decrypt_Response(&response, &result, &in_buffer_length, + &out_buffer); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_Generic_Sign( + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* buffer, + size_t buffer_length, OEMCrypto_Algorithm algorithm, + OEMCrypto_SharedMemory* signature, size_t* signature_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_Generic_Sign_Request( + session, buffer, buffer_length, algorithm, signature, signature_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_Generic_Sign_Response(&response, &result, &signature, + &signature_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_Generic_Verify( + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* buffer, + size_t buffer_length, OEMCrypto_Algorithm algorithm, + const OEMCrypto_SharedMemory* signature, size_t signature_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_Generic_Verify_Request( + session, buffer, buffer_length, algorithm, signature, signature_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_Generic_Verify_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_WrapKeyboxOrOEMCert( + const uint8_t* keybox_or_cert, size_t keybox_or_cert_length, + uint8_t* wrapped_keybox_or_cert, size_t* wrapped_keybox_or_cert_length, + const uint8_t* transport_key, size_t transport_key_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_WrapKeyboxOrOEMCert_Request( + keybox_or_cert, keybox_or_cert_length, wrapped_keybox_or_cert, + wrapped_keybox_or_cert_length, transport_key, transport_key_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_WrapKeyboxOrOEMCert_Response(&response, &result, + &wrapped_keybox_or_cert, + &wrapped_keybox_or_cert_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_InstallKeyboxOrOEMCert( + const uint8_t* keybox_or_cert, size_t keybox_or_cert_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_InstallKeyboxOrOEMCert_Request(keybox_or_cert, + keybox_or_cert_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_InstallKeyboxOrOEMCert_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCrypto_ProvisioningMethod +OEMCrypto_GetProvisioningMethod(void) { + pthread_mutex_lock(&api_lock); + OEMCrypto_ProvisioningMethod result = OEMCrypto_ProvisioningError; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GetProvisioningMethod_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GetProvisioningMethod_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_IsKeyboxOrOEMCertValid(void) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_IsKeyboxOrOEMCertValid_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_IsKeyboxOrOEMCertValid_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* device_id, + size_t* device_id_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GetDeviceID_Request(device_id, device_id_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GetDeviceID_Response(&response, &result, &device_id, + &device_id_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* key_data, + size_t* key_data_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GetKeyData_Request(key_data, key_data_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GetKeyData_Response(&response, &result, &key_data, + &key_data_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadTestKeybox(const uint8_t* buffer, + size_t buffer_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_LoadTestKeybox_Request(buffer, buffer_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_LoadTestKeybox_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult +OEMCrypto_LoadOEMPrivateKey(OEMCrypto_SESSION session) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_LoadOEMPrivateKey_Request(session); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_LoadOEMPrivateKey_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetOEMPublicCertificate( + uint8_t* public_cert, size_t* public_cert_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = + OPK_Pack_GetOEMPublicCertificate_Request(public_cert, public_cert_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GetOEMPublicCertificate_Response(&response, &result, &public_cert, + &public_cert_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetRandom(uint8_t* random_data, + size_t random_data_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GetRandom_Request(random_data, random_data_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GetRandom_Response(&response, &result, &random_data, + &random_data_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API uint32_t OEMCrypto_APIVersion(void) { + pthread_mutex_lock(&api_lock); + uint32_t result = 0; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_APIVersion_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_APIVersion_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API uint32_t OEMCrypto_MinorAPIVersion(void) { + pthread_mutex_lock(&api_lock); + uint32_t result = 0; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_MinorAPIVersion_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_MinorAPIVersion_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult +OEMCrypto_BuildInformation(char* buffer, size_t* buffer_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_BuildInformation_Request(buffer, buffer_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_BuildInformation_Response(&response, &result, &buffer, + &buffer_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API uint8_t OEMCrypto_Security_Patch_Level(void) { + pthread_mutex_lock(&api_lock); + uint8_t result = 0; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_Security_Patch_Level_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_Security_Patch_Level_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCrypto_Security_Level OEMCrypto_SecurityLevel(void) { + pthread_mutex_lock(&api_lock); + OEMCrypto_Security_Level result = OEMCrypto_Level_Unknown; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_SecurityLevel_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_SecurityLevel_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetHDCPCapability( + OEMCrypto_HDCP_Capability* current, OEMCrypto_HDCP_Capability* maximum) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GetHDCPCapability_Request(current, maximum); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GetHDCPCapability_Response(&response, &result, ¤t, &maximum); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult +OEMCrypto_GetDTCP2Capability(OEMCrypto_DTCP2_Capability* capability) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GetDTCP2Capability_Request(capability); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GetDTCP2Capability_Response(&response, &result, &capability); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API bool OEMCrypto_SupportsUsageTable(void) { + pthread_mutex_lock(&api_lock); + bool result = false; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_SupportsUsageTable_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_SupportsUsageTable_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API size_t OEMCrypto_MaximumUsageTableHeaderSize(void) { + pthread_mutex_lock(&api_lock); + size_t result = 0; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_MaximumUsageTableHeaderSize_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_MaximumUsageTableHeaderSize_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API bool OEMCrypto_IsAntiRollbackHwPresent(void) { + pthread_mutex_lock(&api_lock); + bool result = false; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_IsAntiRollbackHwPresent_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_IsAntiRollbackHwPresent_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(size_t* count) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GetNumberOfOpenSessions_Request(count); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GetNumberOfOpenSessions_Response(&response, &result, &count); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(size_t* max) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GetMaxNumberOfSessions_Request(max); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GetMaxNumberOfSessions_Response(&response, &result, &max); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API uint32_t OEMCrypto_SupportedCertificates(void) { + pthread_mutex_lock(&api_lock); + uint32_t result = 0; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_SupportedCertificates_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_SupportedCertificates_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult +OEMCrypto_GetCurrentSRMVersion(uint16_t* version) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GetCurrentSRMVersion_Request(version); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GetCurrentSRMVersion_Response(&response, &result, &version); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API uint32_t OEMCrypto_GetAnalogOutputFlags(void) { + pthread_mutex_lock(&api_lock); + uint32_t result = 0; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GetAnalogOutputFlags_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GetAnalogOutputFlags_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API uint32_t OEMCrypto_ResourceRatingTier(void) { + pthread_mutex_lock(&api_lock); + uint32_t result = 0; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_ResourceRatingTier_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_ResourceRatingTier_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_ProductionReady(void) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_ProductionReady_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_ProductionReady_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCrypto_WatermarkingSupport +OEMCrypto_GetWatermarkingSupport(void) { + pthread_mutex_lock(&api_lock); + OEMCrypto_WatermarkingSupport result = OEMCrypto_WatermarkingNotSupported; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GetWatermarkingSupport_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GetWatermarkingSupport_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadProvisioning( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + size_t core_message_length, const uint8_t* signature, + size_t signature_length, uint8_t* wrapped_private_key, + size_t* wrapped_private_key_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_LoadProvisioning_Request( + session, message, message_length, core_message_length, signature, + signature_length, wrapped_private_key, wrapped_private_key_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_LoadProvisioning_Response(&response, &result, &wrapped_private_key, + &wrapped_private_key_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadDRMPrivateKey( + OEMCrypto_SESSION session, OEMCrypto_PrivateKeyType key_type, + const uint8_t* wrapped_private_key, size_t wrapped_private_key_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_LoadDRMPrivateKey_Request( + session, key_type, wrapped_private_key, wrapped_private_key_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_LoadDRMPrivateKey_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadTestRSAKey(void) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_LoadTestRSAKey_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_LoadTestRSAKey_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_GenerateRSASignature( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length, + RSA_Padding_Scheme padding_scheme) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GenerateRSASignature_Request( + session, message, message_length, signature, signature_length, + padding_scheme); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GenerateRSASignature_Response(&response, &result, &signature, + &signature_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_PrepAndSignProvisioningRequest( + OEMCrypto_SESSION session, uint8_t* message, size_t message_length, + size_t* core_message_size, uint8_t* signature, size_t* signature_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_PrepAndSignProvisioningRequest_Request( + session, message, message_length, core_message_size, signature, + signature_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_PrepAndSignProvisioningRequest_Response( + &response, &result, &message, &message_length, &core_message_size, + &signature, &signature_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_CreateUsageTableHeader( + uint8_t* header_buffer, size_t* header_buffer_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_CreateUsageTableHeader_Request(header_buffer, + header_buffer_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_CreateUsageTableHeader_Response(&response, &result, &header_buffer, + &header_buffer_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult +OEMCrypto_LoadUsageTableHeader(const uint8_t* buffer, size_t buffer_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_LoadUsageTableHeader_Request(buffer, buffer_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_LoadUsageTableHeader_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_CreateNewUsageEntry( + OEMCrypto_SESSION session, uint32_t* usage_entry_number) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_CreateNewUsageEntry_Request(session, usage_entry_number); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_CreateNewUsageEntry_Response(&response, &result, + &usage_entry_number); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_ReuseUsageEntry( + OEMCrypto_SESSION session, uint32_t usage_entry_number) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_ReuseUsageEntry_Request(session, usage_entry_number); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_ReuseUsageEntry_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult +OEMCrypto_LoadUsageEntry(OEMCrypto_SESSION session, uint32_t usage_entry_number, + const uint8_t* buffer, size_t buffer_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_LoadUsageEntry_Request(session, usage_entry_number, buffer, + buffer_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_LoadUsageEntry_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_UpdateUsageEntry( + OEMCrypto_SESSION session, OEMCrypto_SharedMemory* header_buffer, + size_t* header_buffer_length, OEMCrypto_SharedMemory* entry_buffer, + size_t* entry_buffer_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_UpdateUsageEntry_Request( + session, header_buffer, header_buffer_length, entry_buffer, + entry_buffer_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_UpdateUsageEntry_Response(&response, &result, &header_buffer, + &header_buffer_length, &entry_buffer, + &entry_buffer_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_DeactivateUsageEntry( + OEMCrypto_SESSION session, const uint8_t* pst, size_t pst_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_DeactivateUsageEntry_Request(session, pst, pst_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_DeactivateUsageEntry_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session, + const uint8_t* pst, + size_t pst_length, + uint8_t* buffer, + size_t* buffer_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_ReportUsage_Request(session, pst, pst_length, buffer, + buffer_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_ReportUsage_Response(&response, &result, &buffer, &buffer_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_MoveEntry(OEMCrypto_SESSION session, + uint32_t new_index) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_MoveEntry_Request(session, new_index); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_MoveEntry_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader( + uint32_t new_entry_count, uint8_t* header_buffer, + size_t* header_buffer_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_ShrinkUsageTableHeader_Request( + new_entry_count, header_buffer, header_buffer_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_ShrinkUsageTableHeader_Response(&response, &result, &header_buffer, + &header_buffer_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetBootCertificateChain( + uint8_t* bcc, size_t* bcc_length, uint8_t* additional_signature, + size_t* additional_signature_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GetBootCertificateChain_Request( + bcc, bcc_length, additional_signature, additional_signature_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GetBootCertificateChain_Response( + &response, &result, &bcc, &bcc_length, &additional_signature, + &additional_signature_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_GenerateCertificateKeyPair( + 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) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GenerateCertificateKeyPair_Request( + session, public_key, public_key_length, public_key_signature, + public_key_signature_length, wrapped_private_key, + wrapped_private_key_length, key_type); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GenerateCertificateKeyPair_Response( + &response, &result, &public_key, &public_key_length, + &public_key_signature, &public_key_signature_length, &wrapped_private_key, + &wrapped_private_key_length, &key_type); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API uint32_t OEMCrypto_SupportsDecryptHash(void) { + pthread_mutex_lock(&api_lock); + uint32_t result = 0; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_SupportsDecryptHash_Request(); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_SupportsDecryptHash_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult +OEMCrypto_SetDecryptHash(OEMCrypto_SESSION session, uint32_t frame_number, + const uint8_t* hash, size_t hash_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = + OPK_Pack_SetDecryptHash_Request(session, frame_number, hash, hash_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_SetDecryptHash_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetHashErrorCode( + OEMCrypto_SESSION session, uint32_t* failed_frame_number) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GetHashErrorCode_Request(session, failed_frame_number); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GetHashErrorCode_Response(&response, &result, + &failed_frame_number); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_AllocateSecureBuffer( + OEMCrypto_SESSION session, size_t buffer_size, + OEMCrypto_DestBufferDesc* output_descriptor, int* secure_fd) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_AllocateSecureBuffer_Request(session, buffer_size, + output_descriptor, secure_fd); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_AllocateSecureBuffer_Response(&response, &result, + &output_descriptor, &secure_fd); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_FreeSecureBuffer( + OEMCrypto_SESSION session, OEMCrypto_DestBufferDesc* output_descriptor, + int secure_fd) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = + OPK_Pack_FreeSecureBuffer_Request(session, output_descriptor, secure_fd); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_FreeSecureBuffer_Response(&response, &result, &output_descriptor); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_InstallOemPrivateKey( + OEMCrypto_SESSION session, OEMCrypto_PrivateKeyType key_type, + const uint8_t* wrapped_private_key, size_t wrapped_private_key_length) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_InstallOemPrivateKey_Request( + session, key_type, wrapped_private_key, wrapped_private_key_length); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_InstallOemPrivateKey_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_ReassociateEntitledKeySession( + OEMCrypto_SESSION key_session, OEMCrypto_SESSION oec_session) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = + OPK_Pack_ReassociateEntitledKeySession_Request(key_session, oec_session); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_ReassociateEntitledKeySession_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadCasECMKeys( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const OEMCrypto_EntitledContentKeyObject* even_key, + const OEMCrypto_EntitledContentKeyObject* odd_key) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_LoadCasECMKeys_Request(session, message, message_length, + even_key, odd_key); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_LoadCasECMKeys_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult +OEMCrypto_GenerateOTARequest(OEMCrypto_SESSION session, uint8_t* buffer, + size_t* buffer_length, uint32_t use_test_key) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_GenerateOTARequest_Request(session, buffer, buffer_length, + use_test_key); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_GenerateOTARequest_Response(&response, &result, &buffer, + &buffer_length); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} + +OEMCRYPTO_API OEMCryptoResult +OEMCrypto_ProcessOTAKeybox(OEMCrypto_SESSION session, const uint8_t* buffer, + size_t buffer_length, uint32_t use_test_key) { + pthread_mutex_lock(&api_lock); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message request = ODK_Message_Create(NULL, 0); + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + request = OPK_Pack_ProcessOTAKeybox_Request(session, buffer, buffer_length, + use_test_key); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + response = API_Transact(&request); + OPK_Unpack_ProcessOTAKeybox_Response(&response, &result); + + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + + result = API_CheckResult(result); + pthread_mutex_unlock(&api_lock); + return result; +} diff --git a/oemcrypto/opk/serialization/ree/GEN_ree_serializer.c b/oemcrypto/opk/serialization/ree/GEN_ree_serializer.c new file mode 100644 index 0000000..9472037 --- /dev/null +++ b/oemcrypto/opk/serialization/ree/GEN_ree_serializer.c @@ -0,0 +1,3063 @@ +/* + * Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine License + * Agreement. + */ + +/* + * This code is auto-generated, do not edit + */ +#include "GEN_ree_serializer.h" + +#include +#include + +#include "GEN_common_serializer.h" +#include "bump_allocator.h" +#include "common_special_cases.h" +#include "log_macros.h" +#include "marshaller_base.h" +#include "odk_overflow.h" +#include "opk_serialization_base.h" +#include "ree_special_cases.h" +#include "shared_buffer_allocator.h" +#include "tos_transport_interface.h" + +ODK_Message OPK_Pack_SetSandbox_Request(const uint8_t* sandbox_id, + size_t sandbox_id_length) { + uint32_t api_value = 84; /* from _oecc84 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &sandbox_id_length); + OPK_PackMemory(&msg, (const uint8_t*)sandbox_id, + OPK_ToLengthType(sandbox_id_length)); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_SetSandbox_Response(ODK_Message* msg, OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 84) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_Initialize_Request(void) { + uint32_t api_value = 1; /* from _oecc1 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Initialize_Response(ODK_Message* msg, OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 1) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_Terminate_Request(void) { + uint32_t api_value = 2; /* from _oecc2 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Terminate_Response(ODK_Message* msg, OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 2) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_Idle_Request(OEMCrypto_IdleState state, + uint32_t os_specific_code) { + uint32_t api_value = 123; /* from _oecc123 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_uint32_t(&msg, &state); + OPK_Pack_uint32_t(&msg, &os_specific_code); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Idle_Response(ODK_Message* msg, OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 123) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_Wake_Request(void) { + uint32_t api_value = 124; /* from _oecc124 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Wake_Response(ODK_Message* msg, OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 124) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_OpenSession_Request(const OEMCrypto_SESSION* session) { + uint32_t api_value = 9; /* from _oecc9 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackIsNull(&msg, session); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_OpenSession_Response(ODK_Message* msg, OEMCryptoResult* result, + OEMCrypto_SESSION** session) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 9) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_uint32_t(msg, session); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_CloseSession_Request(OEMCrypto_SESSION session) { + uint32_t api_value = 10; /* from _oecc10 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_CloseSession_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_CreateEntitledKeySession_Request( + OEMCrypto_SESSION oec_session, const OEMCrypto_SESSION* key_session) { + uint32_t api_value = 111; /* from _oecc111 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_uint32_t(&msg, &oec_session); + OPK_PackIsNull(&msg, key_session); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_CreateEntitledKeySession_Response( + ODK_Message* msg, OEMCryptoResult* result, + OEMCrypto_SESSION** key_session) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 111) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_uint32_t(msg, key_session); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_RemoveEntitledKeySession_Request( + OEMCrypto_SESSION key_session) { + uint32_t api_value = 112; /* from _oecc112 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_uint32_t(&msg, &key_session); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_RemoveEntitledKeySession_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 112) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_GenerateDerivedKeys_Request( + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* mac_key_context, + size_t mac_key_context_length, + const OEMCrypto_SharedMemory* enc_key_context, + size_t enc_key_context_length) { + uint32_t api_value = 95; /* from _oecc95 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &mac_key_context_length); + OPK_Pack_size_t(&msg, &enc_key_context_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackSharedBuffer(&msg, mac_key_context, + OPK_ToLengthType(mac_key_context_length), /* map */ true, + /* copy_in */ true, /* is_output */ false); + OPK_PackSharedBuffer(&msg, enc_key_context, + OPK_ToLengthType(enc_key_context_length), /* map */ true, + /* copy_in */ true, /* is_output */ false); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GenerateDerivedKeys_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 95) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_DeriveKeysFromSessionKey_Request( + OEMCrypto_SESSION session, const uint8_t* derivation_key, + size_t derivation_key_length, const OEMCrypto_SharedMemory* mac_key_context, + size_t mac_key_context_length, + const OEMCrypto_SharedMemory* enc_key_context, + size_t enc_key_context_length) { + uint32_t api_value = 21; /* from _oecc21 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &derivation_key_length); + OPK_Pack_size_t(&msg, &mac_key_context_length); + OPK_Pack_size_t(&msg, &enc_key_context_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackMemory(&msg, (const uint8_t*)derivation_key, + OPK_ToLengthType(derivation_key_length)); + OPK_PackSharedBuffer(&msg, mac_key_context, + OPK_ToLengthType(mac_key_context_length), /* map */ true, + /* copy_in */ true, /* is_output */ false); + OPK_PackSharedBuffer(&msg, enc_key_context, + OPK_ToLengthType(enc_key_context_length), /* map */ true, + /* copy_in */ true, /* is_output */ false); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_DeriveKeysFromSessionKey_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 21) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_GenerateNonce_Request(OEMCrypto_SESSION session, + const uint32_t* nonce) { + uint32_t api_value = 14; /* from _oecc14 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackIsNull(&msg, nonce); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GenerateNonce_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint32_t** nonce) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 14) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_uint32_t(msg, nonce); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_PrepAndSignLicenseRequest_Request( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const size_t* core_message_size, const uint8_t* signature, + const size_t* signature_length) { + uint32_t api_value = 96; /* from _oecc96 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &message_length); + OPK_PackNullable_size_t(&msg, signature_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackMemory(&msg, (const uint8_t*)message, + OPK_ToLengthType(message_length)); + OPK_PackNullable_size_t(&msg, core_message_size); + OPK_PackAlloc(&msg, signature); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_PrepAndSignLicenseRequest_Response( + ODK_Message* msg, OEMCryptoResult* result, uint8_t** message, + size_t* message_length, size_t** core_message_size, uint8_t** signature, + size_t** signature_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 96) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_size_t(msg, message_length); + OPK_UnpackNullable_size_t(msg, signature_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtr(message_length)); + if (p && *message) { + memcpy(*message, p, OPK_SafeDerefSizeTPtr(message_length)); + } + } + OPK_UnpackNullable_size_t(msg, core_message_size); + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(signature_length)); + if (p && *signature) { + memcpy(*signature, p, OPK_SafeDerefSizeTPtrPtr(signature_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_PrepAndSignRenewalRequest_Request( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const size_t* core_message_size, const uint8_t* signature, + const size_t* signature_length) { + uint32_t api_value = 97; /* from _oecc97 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &message_length); + OPK_PackNullable_size_t(&msg, signature_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackMemory(&msg, (const uint8_t*)message, + OPK_ToLengthType(message_length)); + OPK_PackNullable_size_t(&msg, core_message_size); + OPK_PackAlloc(&msg, signature); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_PrepAndSignRenewalRequest_Response( + ODK_Message* msg, OEMCryptoResult* result, uint8_t** message, + size_t* message_length, size_t** core_message_size, uint8_t** signature, + size_t** signature_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 97) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_size_t(msg, message_length); + OPK_UnpackNullable_size_t(msg, signature_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtr(message_length)); + if (p && *message) { + memcpy(*message, p, OPK_SafeDerefSizeTPtr(message_length)); + } + } + OPK_UnpackNullable_size_t(msg, core_message_size); + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(signature_length)); + if (p && *signature) { + memcpy(*signature, p, OPK_SafeDerefSizeTPtrPtr(signature_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_LoadKeys_Request( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, + OEMCrypto_Substring enc_mac_keys_iv, OEMCrypto_Substring enc_mac_keys, + size_t key_array_length, const OEMCrypto_KeyObject* key_array, + OEMCrypto_Substring pst, OEMCrypto_Substring srm_restriction_data, + OEMCrypto_LicenseType license_type) { + uint32_t api_value = 83; /* from _oecc83 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &message_length); + OPK_Pack_size_t(&msg, &signature_length); + OPK_Pack_size_t(&msg, &key_array_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackMemory(&msg, (const uint8_t*)message, + OPK_ToLengthType(message_length)); + OPK_PackMemory(&msg, (const uint8_t*)signature, + OPK_ToLengthType(signature_length)); + OPK_Pack_OEMCrypto_Substring(&msg, &enc_mac_keys_iv); + OPK_Pack_OEMCrypto_Substring(&msg, &enc_mac_keys); + /* pack object array with packer function OPK_Pack_OEMCrypto_KeyObject */ + ODK_Message* const odk_message = &msg; + const void* const objs = (const void*)key_array; + const LengthType count = OPK_ToLengthType(key_array_length); + const size_t size = sizeof(OEMCrypto_KeyObject); + const bool is_null = objs == NULL || OPK_LengthIsNull(count); + if (!OPK_PackBoolValue(odk_message, is_null)) { + for (size_t i = 0; i < OPK_ToSizeT(count); i++) { + OPK_Pack_OEMCrypto_KeyObject( + odk_message, (const OEMCrypto_KeyObject*)(objs + i * size)); + } + } + OPK_Pack_OEMCrypto_Substring(&msg, &pst); + OPK_Pack_OEMCrypto_Substring(&msg, &srm_restriction_data); + OPK_Pack_uint32_t(&msg, &license_type); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadKeys_Response(ODK_Message* msg, OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 83) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_LoadLicense_Request(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + size_t core_message_length, + const uint8_t* signature, + size_t signature_length) { + uint32_t api_value = 99; /* from _oecc99 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &message_length); + OPK_Pack_size_t(&msg, &signature_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackMemory(&msg, (const uint8_t*)message, + OPK_ToLengthType(message_length)); + OPK_Pack_size_t(&msg, &core_message_length); + OPK_PackMemory(&msg, (const uint8_t*)signature, + OPK_ToLengthType(signature_length)); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadLicense_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 99) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_LoadEntitledContentKeys_Request( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + size_t key_array_length, + const OEMCrypto_EntitledContentKeyObject* key_array) { + uint32_t api_value = 121; /* from _oecc121 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &message_length); + OPK_Pack_size_t(&msg, &key_array_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackMemory(&msg, (const uint8_t*)message, + OPK_ToLengthType(message_length)); + /* pack object array with packer function + * OPK_Pack_OEMCrypto_EntitledContentKeyObject */ + ODK_Message* const odk_message = &msg; + const void* const objs = (const void*)key_array; + const LengthType count = OPK_ToLengthType(key_array_length); + const size_t size = sizeof(OEMCrypto_EntitledContentKeyObject); + const bool is_null = objs == NULL || OPK_LengthIsNull(count); + if (!OPK_PackBoolValue(odk_message, is_null)) { + for (size_t i = 0; i < OPK_ToSizeT(count); i++) { + OPK_Pack_OEMCrypto_EntitledContentKeyObject( + odk_message, + (const OEMCrypto_EntitledContentKeyObject*)(objs + i * size)); + } + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadEntitledContentKeys_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 121) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_RefreshKeys_Request( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, size_t num_keys, + const OEMCrypto_KeyRefreshObject* key_array) { + uint32_t api_value = 91; /* from _oecc91 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &message_length); + OPK_Pack_size_t(&msg, &signature_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackMemory(&msg, (const uint8_t*)message, + OPK_ToLengthType(message_length)); + OPK_PackMemory(&msg, (const uint8_t*)signature, + OPK_ToLengthType(signature_length)); + OPK_Pack_size_t(&msg, &num_keys); + OPK_PackNullable_OEMCrypto_KeyRefreshObject(&msg, key_array); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_RefreshKeys_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 91) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_LoadRenewal_Request(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + size_t core_message_length, + const uint8_t* signature, + size_t signature_length) { + uint32_t api_value = 101; /* from _oecc101 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &message_length); + OPK_Pack_size_t(&msg, &signature_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackMemory(&msg, (const uint8_t*)message, + OPK_ToLengthType(message_length)); + OPK_Pack_size_t(&msg, &core_message_length); + OPK_PackMemory(&msg, (const uint8_t*)signature, + OPK_ToLengthType(signature_length)); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadRenewal_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 101) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_QueryKeyControl_Request( + OEMCrypto_SESSION session, const uint8_t* content_key_id, + size_t content_key_id_length, const uint8_t* key_control_block, + const size_t* key_control_block_length) { + uint32_t api_value = 41; /* from _oecc41 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &content_key_id_length); + OPK_PackNullable_size_t(&msg, key_control_block_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackMemory(&msg, (const uint8_t*)content_key_id, + OPK_ToLengthType(content_key_id_length)); + OPK_PackAlloc(&msg, key_control_block); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_QueryKeyControl_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** key_control_block, + size_t** key_control_block_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 41) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, key_control_block_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(key_control_block_length)); + if (p && *key_control_block) { + memcpy(*key_control_block, p, + OPK_SafeDerefSizeTPtrPtr(key_control_block_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_SelectKey_Request(OEMCrypto_SESSION session, + const uint8_t* content_key_id, + size_t content_key_id_length, + OEMCryptoCipherMode cipher_mode) { + uint32_t api_value = 81; /* from _oecc81 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &content_key_id_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackMemory(&msg, (const uint8_t*)content_key_id, + OPK_ToLengthType(content_key_id_length)); + OPK_Pack_uint32_t(&msg, &cipher_mode); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_SelectKey_Response(ODK_Message* msg, OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 81) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_DecryptCENC_Request( + OEMCrypto_SESSION session, const OEMCrypto_SampleDescription* samples, + size_t samples_length, const OEMCrypto_CENCEncryptPatternDesc* pattern) { + uint32_t api_value = 105; /* from _oecc105 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &samples_length); + OPK_Pack_uint32_t(&msg, &session); + /* pack object array with packer function OPK_Pack_OEMCrypto_SampleDescription + */ + ODK_Message* const odk_message = &msg; + const void* const objs = (const void*)samples; + const LengthType count = OPK_ToLengthType(samples_length); + const size_t size = sizeof(OEMCrypto_SampleDescription); + const bool is_null = objs == NULL || OPK_LengthIsNull(count); + if (!OPK_PackBoolValue(odk_message, is_null)) { + for (size_t i = 0; i < OPK_ToSizeT(count); i++) { + OPK_Pack_OEMCrypto_SampleDescription( + odk_message, (const OEMCrypto_SampleDescription*)(objs + i * size)); + } + } + OPK_PackNullable_OEMCrypto_CENCEncryptPatternDesc(&msg, pattern); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_DecryptCENC_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 105) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_CopyBuffer_Request( + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* data_addr, + size_t data_addr_length, + const OEMCrypto_DestBufferDesc* out_buffer_descriptor, + uint8_t subsample_flags) { + uint32_t api_value = 93; /* from _oecc93 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &data_addr_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackSharedBuffer(&msg, data_addr, OPK_ToLengthType(data_addr_length), + /* map */ true, /* copy_in */ true, + /* is_output */ false); + OPK_PackNullable_OEMCrypto_DestBufferDesc(&msg, out_buffer_descriptor); + OPK_Pack_uint8_t(&msg, &subsample_flags); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_CopyBuffer_Response(ODK_Message* msg, OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 93) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_Generic_Encrypt_Request( + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* in_buffer, + size_t in_buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm, + const OEMCrypto_SharedMemory* out_buffer) { + uint32_t api_value = 24; /* from _oecc24 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &in_buffer_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackSharedBuffer(&msg, in_buffer, OPK_ToLengthType(in_buffer_length), + /* map */ true, /* copy_in */ true, + /* is_output */ false); + OPK_PackArray(&msg, &iv[0], 16); + OPK_Pack_uint32_t(&msg, &algorithm); + OPK_PackSharedBuffer(&msg, out_buffer, OPK_ToLengthType(in_buffer_length), + /* map */ true, /* copy_in */ false, + /* is_output */ true); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Generic_Encrypt_Response(ODK_Message* msg, + OEMCryptoResult* result, + size_t* in_buffer_length, + OEMCrypto_SharedMemory** out_buffer) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 24) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_size_t(msg, in_buffer_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + OPK_UnpackSharedBuffer(msg, out_buffer, OPK_FromSizeTPtr(in_buffer_length), + /* map */ false, /* is_output */ true); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_Generic_Decrypt_Request( + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* in_buffer, + size_t in_buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm, + const OEMCrypto_SharedMemory* out_buffer) { + uint32_t api_value = 25; /* from _oecc25 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &in_buffer_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackSharedBuffer(&msg, in_buffer, OPK_ToLengthType(in_buffer_length), + /* map */ true, /* copy_in */ true, + /* is_output */ false); + OPK_PackArray(&msg, &iv[0], 16); + OPK_Pack_uint32_t(&msg, &algorithm); + OPK_PackSharedBuffer(&msg, out_buffer, OPK_ToLengthType(in_buffer_length), + /* map */ true, /* copy_in */ false, + /* is_output */ true); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Generic_Decrypt_Response(ODK_Message* msg, + OEMCryptoResult* result, + size_t* in_buffer_length, + OEMCrypto_SharedMemory** out_buffer) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 25) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_size_t(msg, in_buffer_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + OPK_UnpackSharedBuffer(msg, out_buffer, OPK_FromSizeTPtr(in_buffer_length), + /* map */ false, /* is_output */ true); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_Generic_Sign_Request( + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* buffer, + size_t buffer_length, OEMCrypto_Algorithm algorithm, + const OEMCrypto_SharedMemory* signature, const size_t* signature_length) { + uint32_t api_value = 26; /* from _oecc26 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &buffer_length); + OPK_PackNullable_size_t(&msg, signature_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackSharedBuffer(&msg, buffer, OPK_ToLengthType(buffer_length), + /* map */ true, /* copy_in */ true, + /* is_output */ false); + OPK_Pack_uint32_t(&msg, &algorithm); + OPK_PackSharedBuffer(&msg, signature, OPK_FromSizeTPtr(signature_length), + /* map */ true, /* copy_in */ false, + /* is_output */ true); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Generic_Sign_Response(ODK_Message* msg, OEMCryptoResult* result, + OEMCrypto_SharedMemory** signature, + size_t** signature_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 26) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, signature_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + OPK_UnpackSharedBuffer(msg, signature, + OPK_FromSizeTPtrPtr(signature_length), + /* map */ false, /* is_output */ true); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_Generic_Verify_Request( + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* buffer, + size_t buffer_length, OEMCrypto_Algorithm algorithm, + const OEMCrypto_SharedMemory* signature, size_t signature_length) { + uint32_t api_value = 27; /* from _oecc27 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &buffer_length); + OPK_Pack_size_t(&msg, &signature_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackSharedBuffer(&msg, buffer, OPK_ToLengthType(buffer_length), + /* map */ true, /* copy_in */ true, + /* is_output */ false); + OPK_Pack_uint32_t(&msg, &algorithm); + OPK_PackSharedBuffer(&msg, signature, OPK_ToLengthType(signature_length), + /* map */ true, /* copy_in */ true, + /* is_output */ false); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Generic_Verify_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 27) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_WrapKeyboxOrOEMCert_Request( + const uint8_t* keybox_or_cert, size_t keybox_or_cert_length, + const uint8_t* wrapped_keybox_or_cert, + const size_t* wrapped_keybox_or_cert_length, const uint8_t* transport_key, + size_t transport_key_length) { + uint32_t api_value = 8; /* from _oecc8 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &keybox_or_cert_length); + OPK_PackNullable_size_t(&msg, wrapped_keybox_or_cert_length); + OPK_Pack_size_t(&msg, &transport_key_length); + OPK_PackMemory(&msg, (const uint8_t*)keybox_or_cert, + OPK_ToLengthType(keybox_or_cert_length)); + OPK_PackAlloc(&msg, wrapped_keybox_or_cert); + OPK_PackMemory(&msg, (const uint8_t*)transport_key, + OPK_ToLengthType(transport_key_length)); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_WrapKeyboxOrOEMCert_Response( + ODK_Message* msg, OEMCryptoResult* result, uint8_t** wrapped_keybox_or_cert, + size_t** wrapped_keybox_or_cert_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 8) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, wrapped_keybox_or_cert_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, + OPK_FromSizeTPtrPtr(wrapped_keybox_or_cert_length)); + if (p && *wrapped_keybox_or_cert) { + memcpy(*wrapped_keybox_or_cert, p, + OPK_SafeDerefSizeTPtrPtr(wrapped_keybox_or_cert_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_InstallKeyboxOrOEMCert_Request( + const uint8_t* keybox_or_cert, size_t keybox_or_cert_length) { + uint32_t api_value = 3; /* from _oecc3 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &keybox_or_cert_length); + OPK_PackMemory(&msg, (const uint8_t*)keybox_or_cert, + OPK_ToLengthType(keybox_or_cert_length)); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_InstallKeyboxOrOEMCert_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 3) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_GetProvisioningMethod_Request(void) { + uint32_t api_value = 49; /* from _oecc49 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetProvisioningMethod_Response( + ODK_Message* msg, OEMCrypto_ProvisioningMethod* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 49) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCrypto_ProvisioningMethod(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_IsKeyboxOrOEMCertValid_Request(void) { + uint32_t api_value = 5; /* from _oecc5 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_IsKeyboxOrOEMCertValid_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 5) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_GetDeviceID_Request(const uint8_t* device_id, + const size_t* device_id_length) { + uint32_t api_value = 7; /* from _oecc7 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackNullable_size_t(&msg, device_id_length); + OPK_PackAlloc(&msg, device_id); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetDeviceID_Response(ODK_Message* msg, OEMCryptoResult* result, + uint8_t** device_id, + size_t** device_id_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 7) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, device_id_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(device_id_length)); + if (p && *device_id) { + memcpy(*device_id, p, OPK_SafeDerefSizeTPtrPtr(device_id_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_GetKeyData_Request(const uint8_t* key_data, + const size_t* key_data_length) { + uint32_t api_value = 4; /* from _oecc4 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackNullable_size_t(&msg, key_data_length); + OPK_PackAlloc(&msg, key_data); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetKeyData_Response(ODK_Message* msg, OEMCryptoResult* result, + uint8_t** key_data, + size_t** key_data_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 4) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, key_data_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(key_data_length)); + if (p && *key_data) { + memcpy(*key_data, p, OPK_SafeDerefSizeTPtrPtr(key_data_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_LoadTestKeybox_Request(const uint8_t* buffer, + size_t buffer_length) { + uint32_t api_value = 78; /* from _oecc78 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &buffer_length); + OPK_PackMemory(&msg, (const uint8_t*)buffer, OPK_ToLengthType(buffer_length)); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadTestKeybox_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 78) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_LoadOEMPrivateKey_Request(OEMCrypto_SESSION session) { + uint32_t api_value = 103; /* from _oecc103 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadOEMPrivateKey_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 103) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_GetOEMPublicCertificate_Request( + const uint8_t* public_cert, const size_t* public_cert_length) { + uint32_t api_value = 104; /* from _oecc104 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackNullable_size_t(&msg, public_cert_length); + OPK_PackAlloc(&msg, public_cert); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetOEMPublicCertificate_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** public_cert, + size_t** public_cert_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 104) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, public_cert_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(public_cert_length)); + if (p && *public_cert) { + memcpy(*public_cert, p, OPK_SafeDerefSizeTPtrPtr(public_cert_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_GetRandom_Request(const uint8_t* random_data, + size_t random_data_length) { + uint32_t api_value = 6; /* from _oecc6 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &random_data_length); + OPK_PackAlloc(&msg, random_data); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetRandom_Response(ODK_Message* msg, OEMCryptoResult* result, + uint8_t** random_data, + size_t* random_data_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 6) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_size_t(msg, random_data_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtr(random_data_length)); + if (p && *random_data) { + memcpy(*random_data, p, OPK_SafeDerefSizeTPtr(random_data_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_APIVersion_Request(void) { + uint32_t api_value = 22; /* from _oecc22 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_APIVersion_Response(ODK_Message* msg, uint32_t* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 22) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_MinorAPIVersion_Request(void) { + uint32_t api_value = 108; /* from _oecc108 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_MinorAPIVersion_Response(ODK_Message* msg, uint32_t* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 108) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_BuildInformation_Request(const char* buffer, + const size_t* buffer_length) { + uint32_t api_value = 125; /* from _oecc125 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackNullable_size_t(&msg, buffer_length); + OPK_PackAlloc(&msg, buffer); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_BuildInformation_Response(ODK_Message* msg, + OEMCryptoResult* result, + char** buffer, + size_t** buffer_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 125) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, buffer_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(buffer_length)); + if (p && *buffer) { + memcpy(*buffer, p, OPK_SafeDerefSizeTPtrPtr(buffer_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_Security_Patch_Level_Request(void) { + uint32_t api_value = 46; /* from _oecc46 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Security_Patch_Level_Response(ODK_Message* msg, + uint8_t* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 46) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint8_t(msg, result); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_SecurityLevel_Request(void) { + uint32_t api_value = 126; /* from _oecc126 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_SecurityLevel_Response(ODK_Message* msg, + OEMCrypto_Security_Level* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 126) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCrypto_Security_Level(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GetHDCPCapability_Request( + const OEMCrypto_HDCP_Capability* current, + const OEMCrypto_HDCP_Capability* maximum) { + uint32_t api_value = 44; /* from _oecc44 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackIsNull(&msg, current); + OPK_PackIsNull(&msg, maximum); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetHDCPCapability_Response( + ODK_Message* msg, OEMCryptoResult* result, + OEMCrypto_HDCP_Capability** current, OEMCrypto_HDCP_Capability** maximum) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 44) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_uint32_t(msg, current); + if (*current) { + if (!Is_Valid_OEMCrypto_HDCP_Capability(**current)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + } + OPK_UnpackNullable_uint32_t(msg, maximum); + if (*maximum) { + if (!Is_Valid_OEMCrypto_HDCP_Capability(**maximum)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_GetDTCP2Capability_Request( + const OEMCrypto_DTCP2_Capability* capability) { + uint32_t api_value = 128; /* from _oecc128 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackIsNull(&msg, capability); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetDTCP2Capability_Response( + ODK_Message* msg, OEMCryptoResult* result, + OEMCrypto_DTCP2_Capability** capability) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 128) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_uint32_t(msg, capability); + if (*capability) { + if (!Is_Valid_OEMCrypto_DTCP2_Capability(**capability)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_SupportsUsageTable_Request(void) { + uint32_t api_value = 29; /* from _oecc29 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_SupportsUsageTable_Response(ODK_Message* msg, bool* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 29) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_bool(msg, result); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_MaximumUsageTableHeaderSize_Request(void) { + uint32_t api_value = 94; /* from _oecc94 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_MaximumUsageTableHeaderSize_Response(ODK_Message* msg, + size_t* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 94) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_size_t(msg, result); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_IsAntiRollbackHwPresent_Request(void) { + uint32_t api_value = 39; /* from _oecc39 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_IsAntiRollbackHwPresent_Response(ODK_Message* msg, + bool* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 39) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_bool(msg, result); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GetNumberOfOpenSessions_Request(const size_t* count) { + uint32_t api_value = 38; /* from _oecc38 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackIsNull(&msg, count); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetNumberOfOpenSessions_Response(ODK_Message* msg, + OEMCryptoResult* result, + size_t** count) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 38) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_size_t(msg, count); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_GetMaxNumberOfSessions_Request(const size_t* max) { + uint32_t api_value = 37; /* from _oecc37 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackIsNull(&msg, max); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetMaxNumberOfSessions_Response(ODK_Message* msg, + OEMCryptoResult* result, + size_t** max) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 37) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_size_t(msg, max); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_SupportedCertificates_Request(void) { + uint32_t api_value = 52; /* from _oecc52 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_SupportedCertificates_Response(ODK_Message* msg, + uint32_t* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 52) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GetCurrentSRMVersion_Request(const uint16_t* version) { + uint32_t api_value = 54; /* from _oecc54 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackIsNull(&msg, version); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetCurrentSRMVersion_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint16_t** version) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 54) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_uint16_t(msg, version); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_GetAnalogOutputFlags_Request(void) { + uint32_t api_value = 71; /* from _oecc71 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetAnalogOutputFlags_Response(ODK_Message* msg, + uint32_t* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 71) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_ResourceRatingTier_Request(void) { + uint32_t api_value = 85; /* from _oecc85 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_ResourceRatingTier_Response(ODK_Message* msg, + uint32_t* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 85) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_ProductionReady_Request(void) { + uint32_t api_value = 122; /* from _oecc122 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_ProductionReady_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 122) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_GetWatermarkingSupport_Request(void) { + uint32_t api_value = 129; /* from _oecc129 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetWatermarkingSupport_Response( + ODK_Message* msg, OEMCrypto_WatermarkingSupport* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 129) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCrypto_WatermarkingSupport(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_LoadProvisioning_Request( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + size_t core_message_length, const uint8_t* signature, + size_t signature_length, const uint8_t* wrapped_private_key, + const size_t* wrapped_private_key_length) { + uint32_t api_value = 102; /* from _oecc102 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &message_length); + OPK_Pack_size_t(&msg, &signature_length); + OPK_PackNullable_size_t(&msg, wrapped_private_key_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackMemory(&msg, (const uint8_t*)message, + OPK_ToLengthType(message_length)); + OPK_Pack_size_t(&msg, &core_message_length); + OPK_PackMemory(&msg, (const uint8_t*)signature, + OPK_ToLengthType(signature_length)); + OPK_PackAlloc(&msg, wrapped_private_key); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadProvisioning_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** wrapped_private_key, + size_t** wrapped_private_key_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 102) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, wrapped_private_key_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(wrapped_private_key_length)); + if (p && *wrapped_private_key) { + memcpy(*wrapped_private_key, p, + OPK_SafeDerefSizeTPtrPtr(wrapped_private_key_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_LoadDRMPrivateKey_Request( + OEMCrypto_SESSION session, OEMCrypto_PrivateKeyType key_type, + const uint8_t* wrapped_private_key, size_t wrapped_private_key_length) { + uint32_t api_value = 107; /* from _oecc107 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &wrapped_private_key_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_Pack_uint32_t(&msg, &key_type); + OPK_PackMemory(&msg, (const uint8_t*)wrapped_private_key, + OPK_ToLengthType(wrapped_private_key_length)); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadDRMPrivateKey_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 107) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_LoadTestRSAKey_Request(void) { + uint32_t api_value = 45; /* from _oecc45 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadTestRSAKey_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 45) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_GenerateRSASignature_Request( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, const size_t* signature_length, + RSA_Padding_Scheme padding_scheme) { + uint32_t api_value = 36; /* from _oecc36 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &message_length); + OPK_PackNullable_size_t(&msg, signature_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackMemory(&msg, (const uint8_t*)message, + OPK_ToLengthType(message_length)); + OPK_PackAlloc(&msg, signature); + OPK_Pack_uint8_t(&msg, &padding_scheme); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GenerateRSASignature_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** signature, + size_t** signature_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 36) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, signature_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(signature_length)); + if (p && *signature) { + memcpy(*signature, p, OPK_SafeDerefSizeTPtrPtr(signature_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_PrepAndSignProvisioningRequest_Request( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const size_t* core_message_size, const uint8_t* signature, + const size_t* signature_length) { + uint32_t api_value = 98; /* from _oecc98 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &message_length); + OPK_PackNullable_size_t(&msg, signature_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackMemory(&msg, (const uint8_t*)message, + OPK_ToLengthType(message_length)); + OPK_PackNullable_size_t(&msg, core_message_size); + OPK_PackAlloc(&msg, signature); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_PrepAndSignProvisioningRequest_Response( + ODK_Message* msg, OEMCryptoResult* result, uint8_t** message, + size_t* message_length, size_t** core_message_size, uint8_t** signature, + size_t** signature_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 98) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_size_t(msg, message_length); + OPK_UnpackNullable_size_t(msg, signature_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtr(message_length)); + if (p && *message) { + memcpy(*message, p, OPK_SafeDerefSizeTPtr(message_length)); + } + } + OPK_UnpackNullable_size_t(msg, core_message_size); + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(signature_length)); + if (p && *signature) { + memcpy(*signature, p, OPK_SafeDerefSizeTPtrPtr(signature_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_CreateUsageTableHeader_Request( + const uint8_t* header_buffer, const size_t* header_buffer_length) { + uint32_t api_value = 61; /* from _oecc61 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackNullable_size_t(&msg, header_buffer_length); + OPK_PackAlloc(&msg, header_buffer); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_CreateUsageTableHeader_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** header_buffer, + size_t** header_buffer_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 61) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, header_buffer_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(header_buffer_length)); + if (p && *header_buffer) { + memcpy(*header_buffer, p, OPK_SafeDerefSizeTPtrPtr(header_buffer_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_LoadUsageTableHeader_Request(const uint8_t* buffer, + size_t buffer_length) { + uint32_t api_value = 62; /* from _oecc62 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &buffer_length); + OPK_PackMemory(&msg, (const uint8_t*)buffer, OPK_ToLengthType(buffer_length)); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadUsageTableHeader_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 62) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_CreateNewUsageEntry_Request( + OEMCrypto_SESSION session, const uint32_t* usage_entry_number) { + uint32_t api_value = 63; /* from _oecc63 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackIsNull(&msg, usage_entry_number); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_CreateNewUsageEntry_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint32_t** usage_entry_number) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 63) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_uint32_t(msg, usage_entry_number); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_ReuseUsageEntry_Request(OEMCrypto_SESSION session, + uint32_t usage_entry_number) { + uint32_t api_value = 127; /* from _oecc127 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_uint32_t(&msg, &session); + OPK_Pack_uint32_t(&msg, &usage_entry_number); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_ReuseUsageEntry_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 127) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_LoadUsageEntry_Request(OEMCrypto_SESSION session, + uint32_t usage_entry_number, + const uint8_t* buffer, + size_t buffer_length) { + uint32_t api_value = 64; /* from _oecc64 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &buffer_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_Pack_uint32_t(&msg, &usage_entry_number); + OPK_PackMemory(&msg, (const uint8_t*)buffer, OPK_ToLengthType(buffer_length)); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadUsageEntry_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 64) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_UpdateUsageEntry_Request( + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* header_buffer, + const size_t* header_buffer_length, + const OEMCrypto_SharedMemory* entry_buffer, + const size_t* entry_buffer_length) { + uint32_t api_value = 65; /* from _oecc65 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackNullable_size_t(&msg, header_buffer_length); + OPK_PackNullable_size_t(&msg, entry_buffer_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackSharedBuffer(&msg, header_buffer, + OPK_FromSizeTPtr(header_buffer_length), /* map */ true, + /* copy_in */ false, /* is_output */ true); + OPK_PackSharedBuffer(&msg, entry_buffer, + OPK_FromSizeTPtr(entry_buffer_length), /* map */ true, + /* copy_in */ false, /* is_output */ true); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_UpdateUsageEntry_Response( + ODK_Message* msg, OEMCryptoResult* result, + OEMCrypto_SharedMemory** header_buffer, size_t** header_buffer_length, + OEMCrypto_SharedMemory** entry_buffer, size_t** entry_buffer_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 65) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, header_buffer_length); + OPK_UnpackNullable_size_t(msg, entry_buffer_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + OPK_UnpackSharedBuffer(msg, header_buffer, + OPK_FromSizeTPtrPtr(header_buffer_length), + /* map */ false, /* is_output */ true); + } + if (SuccessResult(*result)) { + OPK_UnpackSharedBuffer(msg, entry_buffer, + OPK_FromSizeTPtrPtr(entry_buffer_length), + /* map */ false, /* is_output */ true); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_DeactivateUsageEntry_Request(OEMCrypto_SESSION session, + const uint8_t* pst, + size_t pst_length) { + uint32_t api_value = 66; /* from _oecc66 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &pst_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackMemory(&msg, (const uint8_t*)pst, OPK_ToLengthType(pst_length)); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_DeactivateUsageEntry_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 66) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_ReportUsage_Request(OEMCrypto_SESSION session, + const uint8_t* pst, size_t pst_length, + const uint8_t* buffer, + const size_t* buffer_length) { + uint32_t api_value = 32; /* from _oecc32 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &pst_length); + OPK_PackNullable_size_t(&msg, buffer_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackMemory(&msg, (const uint8_t*)pst, OPK_ToLengthType(pst_length)); + OPK_PackAlloc(&msg, buffer); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_ReportUsage_Response(ODK_Message* msg, OEMCryptoResult* result, + uint8_t** buffer, size_t** buffer_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 32) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, buffer_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(buffer_length)); + if (p && *buffer) { + memcpy(*buffer, p, OPK_SafeDerefSizeTPtrPtr(buffer_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_MoveEntry_Request(OEMCrypto_SESSION session, + uint32_t new_index) { + uint32_t api_value = 68; /* from _oecc68 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_uint32_t(&msg, &session); + OPK_Pack_uint32_t(&msg, &new_index); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_MoveEntry_Response(ODK_Message* msg, OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 68) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_ShrinkUsageTableHeader_Request( + uint32_t new_entry_count, const uint8_t* header_buffer, + const size_t* header_buffer_length) { + uint32_t api_value = 67; /* from _oecc67 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackNullable_size_t(&msg, header_buffer_length); + OPK_Pack_uint32_t(&msg, &new_entry_count); + OPK_PackAlloc(&msg, header_buffer); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_ShrinkUsageTableHeader_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** header_buffer, + size_t** header_buffer_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 67) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, header_buffer_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(header_buffer_length)); + if (p && *header_buffer) { + memcpy(*header_buffer, p, OPK_SafeDerefSizeTPtrPtr(header_buffer_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_GetBootCertificateChain_Request( + const uint8_t* bcc, const size_t* bcc_length, + const uint8_t* additional_signature, + const size_t* additional_signature_length) { + uint32_t api_value = 116; /* from _oecc116 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackNullable_size_t(&msg, bcc_length); + OPK_PackNullable_size_t(&msg, additional_signature_length); + OPK_PackAlloc(&msg, bcc); + OPK_PackAlloc(&msg, additional_signature); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetBootCertificateChain_Response( + ODK_Message* msg, OEMCryptoResult* result, uint8_t** bcc, + size_t** bcc_length, uint8_t** additional_signature, + size_t** additional_signature_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 116) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, bcc_length); + OPK_UnpackNullable_size_t(msg, additional_signature_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(bcc_length)); + if (p && *bcc) { + memcpy(*bcc, p, OPK_SafeDerefSizeTPtrPtr(bcc_length)); + } + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, + OPK_FromSizeTPtrPtr(additional_signature_length)); + if (p && *additional_signature) { + memcpy(*additional_signature, p, + OPK_SafeDerefSizeTPtrPtr(additional_signature_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_GenerateCertificateKeyPair_Request( + OEMCrypto_SESSION session, const uint8_t* public_key, + const size_t* public_key_length, const uint8_t* public_key_signature, + const size_t* public_key_signature_length, + const uint8_t* wrapped_private_key, + const size_t* wrapped_private_key_length, + const OEMCrypto_PrivateKeyType* key_type) { + uint32_t api_value = 117; /* from _oecc117 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackNullable_size_t(&msg, public_key_length); + OPK_PackNullable_size_t(&msg, public_key_signature_length); + OPK_PackNullable_size_t(&msg, wrapped_private_key_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackAlloc(&msg, public_key); + OPK_PackAlloc(&msg, public_key_signature); + OPK_PackAlloc(&msg, wrapped_private_key); + OPK_PackIsNull(&msg, key_type); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GenerateCertificateKeyPair_Response( + ODK_Message* msg, OEMCryptoResult* result, 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) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 117) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, public_key_length); + OPK_UnpackNullable_size_t(msg, public_key_signature_length); + OPK_UnpackNullable_size_t(msg, wrapped_private_key_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(public_key_length)); + if (p && *public_key) { + memcpy(*public_key, p, OPK_SafeDerefSizeTPtrPtr(public_key_length)); + } + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, + OPK_FromSizeTPtrPtr(public_key_signature_length)); + if (p && *public_key_signature) { + memcpy(*public_key_signature, p, + OPK_SafeDerefSizeTPtrPtr(public_key_signature_length)); + } + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(wrapped_private_key_length)); + if (p && *wrapped_private_key) { + memcpy(*wrapped_private_key, p, + OPK_SafeDerefSizeTPtrPtr(wrapped_private_key_length)); + } + } + OPK_UnpackNullable_uint32_t(msg, key_type); + if (*key_type) { + if (!Is_Valid_OEMCrypto_PrivateKeyType(**key_type)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_SupportsDecryptHash_Request(void) { + uint32_t api_value = 86; /* from _oecc86 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_SupportsDecryptHash_Response(ODK_Message* msg, + uint32_t* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 86) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_SetDecryptHash_Request(OEMCrypto_SESSION session, + uint32_t frame_number, + const uint8_t* hash, + size_t hash_length) { + uint32_t api_value = 88; /* from _oecc88 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &hash_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_Pack_uint32_t(&msg, &frame_number); + OPK_PackMemory(&msg, (const uint8_t*)hash, OPK_ToLengthType(hash_length)); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_SetDecryptHash_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 88) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_GetHashErrorCode_Request( + OEMCrypto_SESSION session, const uint32_t* failed_frame_number) { + uint32_t api_value = 89; /* from _oecc89 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackIsNull(&msg, failed_frame_number); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetHashErrorCode_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint32_t** failed_frame_number) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 89) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_uint32_t(msg, failed_frame_number); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_AllocateSecureBuffer_Request( + OEMCrypto_SESSION session, size_t buffer_size, + const OEMCrypto_DestBufferDesc* output_descriptor, const int* secure_fd) { + uint32_t api_value = 109; /* from _oecc109 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_uint32_t(&msg, &session); + OPK_Pack_size_t(&msg, &buffer_size); + OPK_PackIsNull(&msg, output_descriptor); + OPK_PackIsNull(&msg, secure_fd); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_AllocateSecureBuffer_Response( + ODK_Message* msg, OEMCryptoResult* result, + OEMCrypto_DestBufferDesc** output_descriptor, int** secure_fd) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 109) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_OEMCrypto_DestBufferDesc(msg, output_descriptor); + OPK_UnpackNullable_int(msg, secure_fd); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_FreeSecureBuffer_Request( + OEMCrypto_SESSION session, + const OEMCrypto_DestBufferDesc* output_descriptor, int secure_fd) { + uint32_t api_value = 110; /* from _oecc110 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackIsNull(&msg, output_descriptor); + OPK_Pack_int(&msg, &secure_fd); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_FreeSecureBuffer_Response( + ODK_Message* msg, OEMCryptoResult* result, + OEMCrypto_DestBufferDesc** output_descriptor) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 110) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_OEMCrypto_DestBufferDesc(msg, output_descriptor); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_InstallOemPrivateKey_Request( + OEMCrypto_SESSION session, OEMCrypto_PrivateKeyType key_type, + const uint8_t* wrapped_private_key, size_t wrapped_private_key_length) { + uint32_t api_value = 118; /* from _oecc118 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &wrapped_private_key_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_Pack_uint32_t(&msg, &key_type); + OPK_PackMemory(&msg, (const uint8_t*)wrapped_private_key, + OPK_ToLengthType(wrapped_private_key_length)); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_InstallOemPrivateKey_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 118) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_ReassociateEntitledKeySession_Request( + OEMCrypto_SESSION key_session, OEMCrypto_SESSION oec_session) { + uint32_t api_value = 119; /* from _oecc119 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_uint32_t(&msg, &key_session); + OPK_Pack_uint32_t(&msg, &oec_session); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_ReassociateEntitledKeySession_Response( + ODK_Message* msg, OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 119) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_LoadCasECMKeys_Request( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const OEMCrypto_EntitledContentKeyObject* even_key, + const OEMCrypto_EntitledContentKeyObject* odd_key) { + uint32_t api_value = 120; /* from _oecc120 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &message_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackMemory(&msg, (const uint8_t*)message, + OPK_ToLengthType(message_length)); + OPK_PackNullable_OEMCrypto_EntitledContentKeyObject(&msg, even_key); + OPK_PackNullable_OEMCrypto_EntitledContentKeyObject(&msg, odd_key); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadCasECMKeys_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 120) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_OPK_SerializationVersion_Request( + const uint32_t* ree_major, const uint32_t* ree_minor, + const uint32_t* tee_major, const uint32_t* tee_minor) { + uint32_t api_value = 115; /* from _oecc115 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackNullable_uint32_t(&msg, ree_major); + OPK_PackNullable_uint32_t(&msg, ree_minor); + OPK_PackIsNull(&msg, tee_major); + OPK_PackIsNull(&msg, tee_minor); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_OPK_SerializationVersion_Response( + ODK_Message* msg, OEMCryptoResult* result, uint32_t** ree_major, + uint32_t** ree_minor, uint32_t** tee_major, uint32_t** tee_minor) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 115) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackNullable_uint32_t(msg, ree_major); + OPK_UnpackNullable_uint32_t(msg, ree_minor); + OPK_UnpackNullable_uint32_t(msg, tee_major); + OPK_UnpackNullable_uint32_t(msg, tee_minor); + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_GenerateOTARequest_Request(OEMCrypto_SESSION session, + const uint8_t* buffer, + const size_t* buffer_length, + uint32_t use_test_key) { + uint32_t api_value = 113; /* from _oecc113 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_PackNullable_size_t(&msg, buffer_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackAlloc(&msg, buffer); + OPK_Pack_uint32_t(&msg, &use_test_key); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GenerateOTARequest_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** buffer, + size_t** buffer_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 113) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_UnpackNullable_size_t(msg, buffer_length); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + if (SuccessResult(*result)) { + uint8_t* p; + OPK_UnpackInPlace(msg, &p, OPK_FromSizeTPtrPtr(buffer_length)); + if (p && *buffer) { + memcpy(*buffer, p, OPK_SafeDerefSizeTPtrPtr(buffer_length)); + } + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} + +ODK_Message OPK_Pack_ProcessOTAKeybox_Request(OEMCrypto_SESSION session, + const uint8_t* buffer, + size_t buffer_length, + uint32_t use_test_key) { + uint32_t api_value = 114; /* from _oecc114 */ + ODK_Message msg = TOS_Transport_GetRequest(); + OPK_Pack_uint32_t(&msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(&msg, ×tamp); + OPK_Pack_size_t(&msg, &buffer_length); + OPK_Pack_uint32_t(&msg, &session); + OPK_PackMemory(&msg, (const uint8_t*)buffer, OPK_ToLengthType(buffer_length)); + OPK_Pack_uint32_t(&msg, &use_test_key); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_ProcessOTAKeybox_Response(ODK_Message* msg, + OEMCryptoResult* result) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 114) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + OPK_Unpack_uint32_t(msg, result); + if (!Is_Valid_OEMCryptoResult(*result)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + + if (SuccessResult(*result)) { + OPK_SharedBuffer_FinalizeUnpacking(); + } +} diff --git a/oemcrypto/opk/serialization/ree/GEN_ree_serializer.h b/oemcrypto/opk/serialization/ree/GEN_ree_serializer.h new file mode 100644 index 0000000..147658c --- /dev/null +++ b/oemcrypto/opk/serialization/ree/GEN_ree_serializer.h @@ -0,0 +1,447 @@ +/* + * Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine License + * Agreement. + */ + +/* + * This code is auto-generated, do not edit + */ +#ifndef OPK_REE_SERIALIZER_H_ +#define OPK_REE_SERIALIZER_H_ + +#include "log_macros.h" +#include "opk_serialization_base.h" + +#ifdef __cplusplus +extern "C" { +#endif + +ODK_Message OPK_Pack_SetSandbox_Request(const uint8_t* sandbox_id, + size_t sandbox_id_length); +void OPK_Unpack_SetSandbox_Response(ODK_Message* msg, OEMCryptoResult* result); +ODK_Message OPK_Pack_Initialize_Request(void); +void OPK_Unpack_Initialize_Response(ODK_Message* msg, OEMCryptoResult* result); +ODK_Message OPK_Pack_Terminate_Request(void); +void OPK_Unpack_Terminate_Response(ODK_Message* msg, OEMCryptoResult* result); +ODK_Message OPK_Pack_Idle_Request(OEMCrypto_IdleState state, + uint32_t os_specific_code); +void OPK_Unpack_Idle_Response(ODK_Message* msg, OEMCryptoResult* result); +ODK_Message OPK_Pack_Wake_Request(void); +void OPK_Unpack_Wake_Response(ODK_Message* msg, OEMCryptoResult* result); +ODK_Message OPK_Pack_OpenSession_Request(const OEMCrypto_SESSION* session); +void OPK_Unpack_OpenSession_Response(ODK_Message* msg, OEMCryptoResult* result, + OEMCrypto_SESSION** session); +ODK_Message OPK_Pack_CloseSession_Request(OEMCrypto_SESSION session); +void OPK_Unpack_CloseSession_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_CreateEntitledKeySession_Request( + OEMCrypto_SESSION oec_session, const OEMCrypto_SESSION* key_session); +void OPK_Unpack_CreateEntitledKeySession_Response( + ODK_Message* msg, OEMCryptoResult* result, OEMCrypto_SESSION** key_session); +ODK_Message OPK_Pack_RemoveEntitledKeySession_Request( + OEMCrypto_SESSION key_session); +void OPK_Unpack_RemoveEntitledKeySession_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_GenerateDerivedKeys_Request( + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* mac_key_context, + size_t mac_key_context_length, + const OEMCrypto_SharedMemory* enc_key_context, + size_t enc_key_context_length); +void OPK_Unpack_GenerateDerivedKeys_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_DeriveKeysFromSessionKey_Request( + OEMCrypto_SESSION session, const uint8_t* derivation_key, + size_t derivation_key_length, const OEMCrypto_SharedMemory* mac_key_context, + size_t mac_key_context_length, + const OEMCrypto_SharedMemory* enc_key_context, + size_t enc_key_context_length); +void OPK_Unpack_DeriveKeysFromSessionKey_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_GenerateNonce_Request(OEMCrypto_SESSION session, + const uint32_t* nonce); +void OPK_Unpack_GenerateNonce_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint32_t** nonce); +ODK_Message OPK_Pack_PrepAndSignLicenseRequest_Request( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const size_t* core_message_size, const uint8_t* signature, + const size_t* signature_length); +void OPK_Unpack_PrepAndSignLicenseRequest_Response( + ODK_Message* msg, OEMCryptoResult* result, uint8_t** message, + size_t* message_length, size_t** core_message_size, uint8_t** signature, + size_t** signature_length); +ODK_Message OPK_Pack_PrepAndSignRenewalRequest_Request( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const size_t* core_message_size, const uint8_t* signature, + const size_t* signature_length); +void OPK_Unpack_PrepAndSignRenewalRequest_Response( + ODK_Message* msg, OEMCryptoResult* result, uint8_t** message, + size_t* message_length, size_t** core_message_size, uint8_t** signature, + size_t** signature_length); +ODK_Message OPK_Pack_LoadKeys_Request( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, + OEMCrypto_Substring enc_mac_keys_iv, OEMCrypto_Substring enc_mac_keys, + size_t key_array_length, const OEMCrypto_KeyObject* key_array, + OEMCrypto_Substring pst, OEMCrypto_Substring srm_restriction_data, + OEMCrypto_LicenseType license_type); +void OPK_Unpack_LoadKeys_Response(ODK_Message* msg, OEMCryptoResult* result); +ODK_Message OPK_Pack_LoadLicense_Request(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + size_t core_message_length, + const uint8_t* signature, + size_t signature_length); +void OPK_Unpack_LoadLicense_Response(ODK_Message* msg, OEMCryptoResult* result); +ODK_Message OPK_Pack_LoadEntitledContentKeys_Request( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + size_t key_array_length, + const OEMCrypto_EntitledContentKeyObject* key_array); +void OPK_Unpack_LoadEntitledContentKeys_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_RefreshKeys_Request( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, size_t num_keys, + const OEMCrypto_KeyRefreshObject* key_array); +void OPK_Unpack_RefreshKeys_Response(ODK_Message* msg, OEMCryptoResult* result); +ODK_Message OPK_Pack_LoadRenewal_Request(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + size_t core_message_length, + const uint8_t* signature, + size_t signature_length); +void OPK_Unpack_LoadRenewal_Response(ODK_Message* msg, OEMCryptoResult* result); +ODK_Message OPK_Pack_QueryKeyControl_Request( + OEMCrypto_SESSION session, const uint8_t* content_key_id, + size_t content_key_id_length, const uint8_t* key_control_block, + const size_t* key_control_block_length); +void OPK_Unpack_QueryKeyControl_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** key_control_block, + size_t** key_control_block_length); +ODK_Message OPK_Pack_SelectKey_Request(OEMCrypto_SESSION session, + const uint8_t* content_key_id, + size_t content_key_id_length, + OEMCryptoCipherMode cipher_mode); +void OPK_Unpack_SelectKey_Response(ODK_Message* msg, OEMCryptoResult* result); +ODK_Message OPK_Pack_DecryptCENC_Request( + OEMCrypto_SESSION session, const OEMCrypto_SampleDescription* samples, + size_t samples_length, const OEMCrypto_CENCEncryptPatternDesc* pattern); +void OPK_Unpack_DecryptCENC_Response(ODK_Message* msg, OEMCryptoResult* result); +ODK_Message OPK_Pack_CopyBuffer_Request( + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* data_addr, + size_t data_addr_length, + const OEMCrypto_DestBufferDesc* out_buffer_descriptor, + uint8_t subsample_flags); +void OPK_Unpack_CopyBuffer_Response(ODK_Message* msg, OEMCryptoResult* result); +ODK_Message OPK_Pack_Generic_Encrypt_Request( + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* in_buffer, + size_t in_buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm, + const OEMCrypto_SharedMemory* out_buffer); +void OPK_Unpack_Generic_Encrypt_Response(ODK_Message* msg, + OEMCryptoResult* result, + size_t* in_buffer_length, + OEMCrypto_SharedMemory** out_buffer); +ODK_Message OPK_Pack_Generic_Decrypt_Request( + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* in_buffer, + size_t in_buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm, + const OEMCrypto_SharedMemory* out_buffer); +void OPK_Unpack_Generic_Decrypt_Response(ODK_Message* msg, + OEMCryptoResult* result, + size_t* in_buffer_length, + OEMCrypto_SharedMemory** out_buffer); +ODK_Message OPK_Pack_Generic_Sign_Request( + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* buffer, + size_t buffer_length, OEMCrypto_Algorithm algorithm, + const OEMCrypto_SharedMemory* signature, const size_t* signature_length); +void OPK_Unpack_Generic_Sign_Response(ODK_Message* msg, OEMCryptoResult* result, + OEMCrypto_SharedMemory** signature, + size_t** signature_length); +ODK_Message OPK_Pack_Generic_Verify_Request( + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* buffer, + size_t buffer_length, OEMCrypto_Algorithm algorithm, + const OEMCrypto_SharedMemory* signature, size_t signature_length); +void OPK_Unpack_Generic_Verify_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_WrapKeyboxOrOEMCert_Request( + const uint8_t* keybox_or_cert, size_t keybox_or_cert_length, + const uint8_t* wrapped_keybox_or_cert, + const size_t* wrapped_keybox_or_cert_length, const uint8_t* transport_key, + size_t transport_key_length); +void OPK_Unpack_WrapKeyboxOrOEMCert_Response( + ODK_Message* msg, OEMCryptoResult* result, uint8_t** wrapped_keybox_or_cert, + size_t** wrapped_keybox_or_cert_length); +ODK_Message OPK_Pack_InstallKeyboxOrOEMCert_Request( + const uint8_t* keybox_or_cert, size_t keybox_or_cert_length); +void OPK_Unpack_InstallKeyboxOrOEMCert_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_GetProvisioningMethod_Request(void); +void OPK_Unpack_GetProvisioningMethod_Response( + ODK_Message* msg, OEMCrypto_ProvisioningMethod* result); +ODK_Message OPK_Pack_IsKeyboxOrOEMCertValid_Request(void); +void OPK_Unpack_IsKeyboxOrOEMCertValid_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_GetDeviceID_Request(const uint8_t* device_id, + const size_t* device_id_length); +void OPK_Unpack_GetDeviceID_Response(ODK_Message* msg, OEMCryptoResult* result, + uint8_t** device_id, + size_t** device_id_length); +ODK_Message OPK_Pack_GetKeyData_Request(const uint8_t* key_data, + const size_t* key_data_length); +void OPK_Unpack_GetKeyData_Response(ODK_Message* msg, OEMCryptoResult* result, + uint8_t** key_data, + size_t** key_data_length); +ODK_Message OPK_Pack_LoadTestKeybox_Request(const uint8_t* buffer, + size_t buffer_length); +void OPK_Unpack_LoadTestKeybox_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_LoadOEMPrivateKey_Request(OEMCrypto_SESSION session); +void OPK_Unpack_LoadOEMPrivateKey_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_GetOEMPublicCertificate_Request( + const uint8_t* public_cert, const size_t* public_cert_length); +void OPK_Unpack_GetOEMPublicCertificate_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** public_cert, + size_t** public_cert_length); +ODK_Message OPK_Pack_GetRandom_Request(const uint8_t* random_data, + size_t random_data_length); +void OPK_Unpack_GetRandom_Response(ODK_Message* msg, OEMCryptoResult* result, + uint8_t** random_data, + size_t* random_data_length); +ODK_Message OPK_Pack_APIVersion_Request(void); +void OPK_Unpack_APIVersion_Response(ODK_Message* msg, uint32_t* result); +ODK_Message OPK_Pack_MinorAPIVersion_Request(void); +void OPK_Unpack_MinorAPIVersion_Response(ODK_Message* msg, uint32_t* result); +ODK_Message OPK_Pack_BuildInformation_Request(const char* buffer, + const size_t* buffer_length); +void OPK_Unpack_BuildInformation_Response(ODK_Message* msg, + OEMCryptoResult* result, + char** buffer, + size_t** buffer_length); +ODK_Message OPK_Pack_Security_Patch_Level_Request(void); +void OPK_Unpack_Security_Patch_Level_Response(ODK_Message* msg, + uint8_t* result); +ODK_Message OPK_Pack_SecurityLevel_Request(void); +void OPK_Unpack_SecurityLevel_Response(ODK_Message* msg, + OEMCrypto_Security_Level* result); +ODK_Message OPK_Pack_GetHDCPCapability_Request( + const OEMCrypto_HDCP_Capability* current, + const OEMCrypto_HDCP_Capability* maximum); +void OPK_Unpack_GetHDCPCapability_Response(ODK_Message* msg, + OEMCryptoResult* result, + OEMCrypto_HDCP_Capability** current, + OEMCrypto_HDCP_Capability** maximum); +ODK_Message OPK_Pack_GetDTCP2Capability_Request( + const OEMCrypto_DTCP2_Capability* capability); +void OPK_Unpack_GetDTCP2Capability_Response( + ODK_Message* msg, OEMCryptoResult* result, + OEMCrypto_DTCP2_Capability** capability); +ODK_Message OPK_Pack_SupportsUsageTable_Request(void); +void OPK_Unpack_SupportsUsageTable_Response(ODK_Message* msg, bool* result); +ODK_Message OPK_Pack_MaximumUsageTableHeaderSize_Request(void); +void OPK_Unpack_MaximumUsageTableHeaderSize_Response(ODK_Message* msg, + size_t* result); +ODK_Message OPK_Pack_IsAntiRollbackHwPresent_Request(void); +void OPK_Unpack_IsAntiRollbackHwPresent_Response(ODK_Message* msg, + bool* result); +ODK_Message OPK_Pack_GetNumberOfOpenSessions_Request(const size_t* count); +void OPK_Unpack_GetNumberOfOpenSessions_Response(ODK_Message* msg, + OEMCryptoResult* result, + size_t** count); +ODK_Message OPK_Pack_GetMaxNumberOfSessions_Request(const size_t* max); +void OPK_Unpack_GetMaxNumberOfSessions_Response(ODK_Message* msg, + OEMCryptoResult* result, + size_t** max); +ODK_Message OPK_Pack_SupportedCertificates_Request(void); +void OPK_Unpack_SupportedCertificates_Response(ODK_Message* msg, + uint32_t* result); +ODK_Message OPK_Pack_GetCurrentSRMVersion_Request(const uint16_t* version); +void OPK_Unpack_GetCurrentSRMVersion_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint16_t** version); +ODK_Message OPK_Pack_GetAnalogOutputFlags_Request(void); +void OPK_Unpack_GetAnalogOutputFlags_Response(ODK_Message* msg, + uint32_t* result); +ODK_Message OPK_Pack_ResourceRatingTier_Request(void); +void OPK_Unpack_ResourceRatingTier_Response(ODK_Message* msg, uint32_t* result); +ODK_Message OPK_Pack_ProductionReady_Request(void); +void OPK_Unpack_ProductionReady_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_GetWatermarkingSupport_Request(void); +void OPK_Unpack_GetWatermarkingSupport_Response( + ODK_Message* msg, OEMCrypto_WatermarkingSupport* result); +ODK_Message OPK_Pack_LoadProvisioning_Request( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + size_t core_message_length, const uint8_t* signature, + size_t signature_length, const uint8_t* wrapped_private_key, + const size_t* wrapped_private_key_length); +void OPK_Unpack_LoadProvisioning_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** wrapped_private_key, + size_t** wrapped_private_key_length); +ODK_Message OPK_Pack_LoadDRMPrivateKey_Request( + OEMCrypto_SESSION session, OEMCrypto_PrivateKeyType key_type, + const uint8_t* wrapped_private_key, size_t wrapped_private_key_length); +void OPK_Unpack_LoadDRMPrivateKey_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_LoadTestRSAKey_Request(void); +void OPK_Unpack_LoadTestRSAKey_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_GenerateRSASignature_Request( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, const size_t* signature_length, + RSA_Padding_Scheme padding_scheme); +void OPK_Unpack_GenerateRSASignature_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** signature, + size_t** signature_length); +ODK_Message OPK_Pack_PrepAndSignProvisioningRequest_Request( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const size_t* core_message_size, const uint8_t* signature, + const size_t* signature_length); +void OPK_Unpack_PrepAndSignProvisioningRequest_Response( + ODK_Message* msg, OEMCryptoResult* result, uint8_t** message, + size_t* message_length, size_t** core_message_size, uint8_t** signature, + size_t** signature_length); +ODK_Message OPK_Pack_CreateUsageTableHeader_Request( + const uint8_t* header_buffer, const size_t* header_buffer_length); +void OPK_Unpack_CreateUsageTableHeader_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** header_buffer, + size_t** header_buffer_length); +ODK_Message OPK_Pack_LoadUsageTableHeader_Request(const uint8_t* buffer, + size_t buffer_length); +void OPK_Unpack_LoadUsageTableHeader_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_CreateNewUsageEntry_Request( + OEMCrypto_SESSION session, const uint32_t* usage_entry_number); +void OPK_Unpack_CreateNewUsageEntry_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint32_t** usage_entry_number); +ODK_Message OPK_Pack_ReuseUsageEntry_Request(OEMCrypto_SESSION session, + uint32_t usage_entry_number); +void OPK_Unpack_ReuseUsageEntry_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_LoadUsageEntry_Request(OEMCrypto_SESSION session, + uint32_t usage_entry_number, + const uint8_t* buffer, + size_t buffer_length); +void OPK_Unpack_LoadUsageEntry_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_UpdateUsageEntry_Request( + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* header_buffer, + const size_t* header_buffer_length, + const OEMCrypto_SharedMemory* entry_buffer, + const size_t* entry_buffer_length); +void OPK_Unpack_UpdateUsageEntry_Response( + ODK_Message* msg, OEMCryptoResult* result, + OEMCrypto_SharedMemory** header_buffer, size_t** header_buffer_length, + OEMCrypto_SharedMemory** entry_buffer, size_t** entry_buffer_length); +ODK_Message OPK_Pack_DeactivateUsageEntry_Request(OEMCrypto_SESSION session, + const uint8_t* pst, + size_t pst_length); +void OPK_Unpack_DeactivateUsageEntry_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_ReportUsage_Request(OEMCrypto_SESSION session, + const uint8_t* pst, size_t pst_length, + const uint8_t* buffer, + const size_t* buffer_length); +void OPK_Unpack_ReportUsage_Response(ODK_Message* msg, OEMCryptoResult* result, + uint8_t** buffer, size_t** buffer_length); +ODK_Message OPK_Pack_MoveEntry_Request(OEMCrypto_SESSION session, + uint32_t new_index); +void OPK_Unpack_MoveEntry_Response(ODK_Message* msg, OEMCryptoResult* result); +ODK_Message OPK_Pack_ShrinkUsageTableHeader_Request( + uint32_t new_entry_count, const uint8_t* header_buffer, + const size_t* header_buffer_length); +void OPK_Unpack_ShrinkUsageTableHeader_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** header_buffer, + size_t** header_buffer_length); +ODK_Message OPK_Pack_GetBootCertificateChain_Request( + const uint8_t* bcc, const size_t* bcc_length, + const uint8_t* additional_signature, + const size_t* additional_signature_length); +void OPK_Unpack_GetBootCertificateChain_Response( + ODK_Message* msg, OEMCryptoResult* result, uint8_t** bcc, + size_t** bcc_length, uint8_t** additional_signature, + size_t** additional_signature_length); +ODK_Message OPK_Pack_GenerateCertificateKeyPair_Request( + OEMCrypto_SESSION session, const uint8_t* public_key, + const size_t* public_key_length, const uint8_t* public_key_signature, + const size_t* public_key_signature_length, + const uint8_t* wrapped_private_key, + const size_t* wrapped_private_key_length, + const OEMCrypto_PrivateKeyType* key_type); +void OPK_Unpack_GenerateCertificateKeyPair_Response( + ODK_Message* msg, OEMCryptoResult* result, 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); +ODK_Message OPK_Pack_SupportsDecryptHash_Request(void); +void OPK_Unpack_SupportsDecryptHash_Response(ODK_Message* msg, + uint32_t* result); +ODK_Message OPK_Pack_SetDecryptHash_Request(OEMCrypto_SESSION session, + uint32_t frame_number, + const uint8_t* hash, + size_t hash_length); +void OPK_Unpack_SetDecryptHash_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_GetHashErrorCode_Request( + OEMCrypto_SESSION session, const uint32_t* failed_frame_number); +void OPK_Unpack_GetHashErrorCode_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint32_t** failed_frame_number); +ODK_Message OPK_Pack_AllocateSecureBuffer_Request( + OEMCrypto_SESSION session, size_t buffer_size, + const OEMCrypto_DestBufferDesc* output_descriptor, const int* secure_fd); +void OPK_Unpack_AllocateSecureBuffer_Response( + ODK_Message* msg, OEMCryptoResult* result, + OEMCrypto_DestBufferDesc** output_descriptor, int** secure_fd); +ODK_Message OPK_Pack_FreeSecureBuffer_Request( + OEMCrypto_SESSION session, + const OEMCrypto_DestBufferDesc* output_descriptor, int secure_fd); +void OPK_Unpack_FreeSecureBuffer_Response( + ODK_Message* msg, OEMCryptoResult* result, + OEMCrypto_DestBufferDesc** output_descriptor); +ODK_Message OPK_Pack_InstallOemPrivateKey_Request( + OEMCrypto_SESSION session, OEMCrypto_PrivateKeyType key_type, + const uint8_t* wrapped_private_key, size_t wrapped_private_key_length); +void OPK_Unpack_InstallOemPrivateKey_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_ReassociateEntitledKeySession_Request( + OEMCrypto_SESSION key_session, OEMCrypto_SESSION oec_session); +void OPK_Unpack_ReassociateEntitledKeySession_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_LoadCasECMKeys_Request( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const OEMCrypto_EntitledContentKeyObject* even_key, + const OEMCrypto_EntitledContentKeyObject* odd_key); +void OPK_Unpack_LoadCasECMKeys_Response(ODK_Message* msg, + OEMCryptoResult* result); +ODK_Message OPK_Pack_OPK_SerializationVersion_Request( + const uint32_t* ree_major, const uint32_t* ree_minor, + const uint32_t* tee_major, const uint32_t* tee_minor); +void OPK_Unpack_OPK_SerializationVersion_Response( + ODK_Message* msg, OEMCryptoResult* result, uint32_t** ree_major, + uint32_t** ree_minor, uint32_t** tee_major, uint32_t** tee_minor); +ODK_Message OPK_Pack_GenerateOTARequest_Request(OEMCrypto_SESSION session, + const uint8_t* buffer, + const size_t* buffer_length, + uint32_t use_test_key); +void OPK_Unpack_GenerateOTARequest_Response(ODK_Message* msg, + OEMCryptoResult* result, + uint8_t** buffer, + size_t** buffer_length); +ODK_Message OPK_Pack_ProcessOTAKeybox_Request(OEMCrypto_SESSION session, + const uint8_t* buffer, + size_t buffer_length, + uint32_t use_test_key); +void OPK_Unpack_ProcessOTAKeybox_Response(ODK_Message* msg, + OEMCryptoResult* result); +#ifdef __cplusplus +} // extern "C" +#endif +#endif /* OPK_REE_SERIALIZER_H_ */ diff --git a/oemcrypto/opk/serialization/ree/api_support.c b/oemcrypto/opk/serialization/ree/api_support.c new file mode 100644 index 0000000..af715bf --- /dev/null +++ b/oemcrypto/opk/serialization/ree/api_support.c @@ -0,0 +1,103 @@ +/* + * Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine + * License Agreement. + */ + +/* + * Support functions for the OEMCrypto API functions, related to + * message handling + */ +#include + +#include "GEN_common_serializer.h" +#include "GEN_ree_serializer.h" +#include "OEMCryptoCENC.h" +#include "api_support.h" +#include "bump_allocator.h" +#include "log_macros.h" +#include "message_debug.h" +#include "odk_message.h" +#include "shared_buffer_allocator.h" +#include "tos_shared_memory_interface.h" +#include "tos_transport_interface.h" +#include "version.h" + +/* + * api_result is set to the success or failure status of the local execution of + * the OEMCrypto API function. If it is anything other than OEMCrypto_SUCCESS, + * the call to the TEE failed, so the result code in the response message will + * not be valid. In this case api_result will be the result returned from the + * OEMCrypto API function call. If api_result is OEMCrypto_SUCCESS, it means + * that the remote call was completed successfully and the result code from the + * remote call will be used as the return value. + */ +OEMCryptoResult api_result; + +/* + * opk OEMCrypto API is single threaded + */ +pthread_mutex_t api_lock; + +/* + * Called at the beginning of every API function. Initializes other + * modules and sets api_result code to OEMCrypto_SUCCESS. api_result is + * set from the return value of any API function that returns an + * OEMCryptoResult. It is updated in API_Transact and finally returned + * in API_CheckResult to determine the overall success of the function + * call. On the first API call, a version check is performed to make + * sure that the REE and TEE are compatible. + */ +void API_Initialize(void) { + api_result = OEMCrypto_SUCCESS; + OPK_SharedBuffer_Reset(); + OPK_BumpAllocator_Reset(); +} + +/* + * Called to send the request message to the TEE and receive the response. Sets + * api_result based on the status of the operations. Returns a message + * containing the payload from the TEE's response. + * + * If there is an error sending or receiving the message, api_result will be set + * to OPK_ERROR_REMOTE_CALL. If the response message is invalid, api_result will + * be set to OEMCrypto_UNKNOWN_FAILURE. + * + * In any error condition, the returned message will contain a NULL payload as + * created by ODK_Message_Create(NULL, 0) and the request message payload will + * not be released. + */ +ODK_Message API_Transact(ODK_Message* request) { + ODK_Message response = ODK_Message_Create(NULL, 0); + if (api_result != OEMCrypto_SUCCESS || request == NULL) { + return response; + } + OPK_TransportStatus transport_status = + TOS_Transport_SendMessage(request, &response); + if (transport_status != OPK_TRANSPORT_STATUS_OK) { + LOGE("Send request %s", OPK_TransportStatus_Str(transport_status)); + api_result = OPK_ERROR_REMOTE_CALL; + return response; + } + ODK_MessageStatus message_status = ODK_Message_GetStatus(&response); + if (message_status != MESSAGE_STATUS_OK) { + LOGE("Response %s", OPK_MessageStatus_Str(message_status)); + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return response; +} + +/* + * Called at the end of every API function. If the result from the execution of + * the local function is not successful for any reason, return that error + * code. Otherwise if the remote call completed okay, return the result that was + * unpacked from the response. + */ +OEMCryptoResult API_CheckResult(OEMCryptoResult unpacked_result) { + return (api_result != OEMCrypto_SUCCESS) ? api_result : unpacked_result; +} + +/* + * Called by OEMCrypto_Terminate + */ +void API_Terminate(void) {} diff --git a/oemcrypto/opk/serialization/ree/api_support.h b/oemcrypto/opk/serialization/ree/api_support.h new file mode 100644 index 0000000..566ac77 --- /dev/null +++ b/oemcrypto/opk/serialization/ree/api_support.h @@ -0,0 +1,42 @@ +/* + * 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 OPK_API_SUPPORT_H_ +#define OPK_API_SUPPORT_H_ + +/* + * Support functions for the OEMCrypto API functions, related to + * message handling + */ + +#include + +#include "OEMCryptoCENC.h" +#include "bump_allocator.h" +#include "marshaller_base.h" +#include "shared_buffer_allocator.h" +#include "tos_shared_memory_interface.h" +#include "tos_transport_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define OEMCRYPTO_API __attribute__((visibility("default"))) + +extern pthread_mutex_t api_lock; +extern OEMCryptoResult api_result; + +void API_Initialize(void); +ODK_Message API_Transact(ODK_Message* request); +OEMCryptoResult API_CheckResult(OEMCryptoResult unpacked_result); +void API_Terminate(void); + +#ifdef __cplusplus +} +#endif + +#endif /* OPK_API_SUPPORT_H_ */ diff --git a/oemcrypto/opk/serialization/ree/ree.gyp b/oemcrypto/opk/serialization/ree/ree.gyp new file mode 100644 index 0000000..323910a --- /dev/null +++ b/oemcrypto/opk/serialization/ree/ree.gyp @@ -0,0 +1,53 @@ +# +# Builds the static library opk_ree.a from generated sources based on +# OEMCryptoCENC.h. This library gets linked in with port-specific files to +# make liboemcrypto.so. +# +#'dependencies: [ ' ree.gyp : opk_ree' ] +# +{ + 'includes' : [ + '../settings.gypi' + ], + 'targets' : [ + { + 'target_name' : 'opk_ree', + 'type' : 'static_library', + 'standalone_static_library' : 1, + 'sources' : [ + 'api_support.c', + 'GEN_ree_serializer.c', + 'GEN_oemcrypto_api.c', + 'ree_os_type.c', + 'ree_version.c', + 'ree_special_cases.c', + 'special_case_apis.c', + '<(common_dir)/bump_allocator.c', + '<(common_dir)/common_special_cases.c', + '<(common_dir)/GEN_common_serializer.c', + '<(common_dir)/log_macros.c', + '<(common_dir)/length_types.c', + '<(common_dir)/marshaller_base.c', + '<(common_dir)/message_debug.c', + '<(common_dir)/opk_init.c', + '<(common_dir)/opk_serialization_base.c', + '<(common_dir)/shared_buffer_allocator.c', + '<(odk_dir)/src/odk_message.c', + '<(odk_dir)/src/odk_overflow.c', + ], + 'conditions': [ + # If |enable_code_generator| is true, the GEN_* files will be + # updated if they are out of date. Disable the code generator + # when building the release distribution since the code + # generator isn't included. + ['enable_code_generator=="true"', { + 'dependencies' : [ + '<(generator_dir)/make_source.gyp:make_api_src', + '<(generator_dir)/make_source.gyp:make_common_serializer_src', + '<(generator_dir)/make_source.gyp:make_ree_serializer_src', + ], + }], + ], + } + ] +} diff --git a/oemcrypto/opk/serialization/ree/ree_os_type.c b/oemcrypto/opk/serialization/ree/ree_os_type.c new file mode 100644 index 0000000..0a5f152 --- /dev/null +++ b/oemcrypto/opk/serialization/ree/ree_os_type.c @@ -0,0 +1,12 @@ +/* + * 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 "os_type.h" + +/* + * Indicate that the code is running in the REE + */ +OPK_OSType OPK_Get_OSType(void) { return OPK_RICH_OS; } diff --git a/oemcrypto/opk/serialization/ree/ree_special_cases.c b/oemcrypto/opk/serialization/ree/ree_special_cases.c new file mode 100644 index 0000000..434d5db --- /dev/null +++ b/oemcrypto/opk/serialization/ree/ree_special_cases.c @@ -0,0 +1,76 @@ +/* + * 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 "ree_special_cases.h" + +#include +#include +#include +#include +#include + +#include "GEN_common_serializer.h" +#include "GEN_ree_serializer.h" +#include "OEMCryptoCENC.h" +#include "log_macros.h" +#include "opk_serialization_base.h" +#include "shared_buffer_allocator.h" +#include "tos_shared_memory_interface.h" + +/* + * Special serialization cases due to some parameters being defined as + * pointers into other parameters. + */ + +void OPK_Pack_RewrapDeviceRSAKey_Request( + ODK_Message* msg, OEMCrypto_SESSION session, const uint8_t* message, + size_t message_length, const uint8_t* signature, size_t signature_length, + const uint32_t* unaligned_nonce, const uint8_t* enc_rsa_key, + size_t enc_rsa_key_length, const uint8_t* enc_rsa_key_iv, + const uint8_t* wrapped_rsa_key, const size_t* wrapped_rsa_key_length) { + uint32_t api_value = 18; /* from _oecc18 */ + OPK_Pack_uint32_t(msg, &api_value); + uint64_t timestamp = time(0); + OPK_Pack_uint64_t(msg, ×tamp); + OPK_Pack_size_t(msg, &message_length); + OPK_Pack_size_t(msg, &signature_length); + OPK_Pack_size_t(msg, &enc_rsa_key_length); + OPK_PackNullable_size_t(msg, wrapped_rsa_key_length); + OPK_Pack_uint32_t(msg, &session); + OPK_PackSharedBuffer(msg, message, OPK_ToLengthType(message_length), + /* map */ true, /* copy_in */ true, + /* is_output */ false); + OPK_PackSharedBuffer(msg, signature, OPK_ToLengthType(signature_length), + /* map */ true, /* copy_in */ true, + /* is_output */ false); + + size_t unaligned_nonce_offset = 0; + if (__builtin_sub_overflow((uintptr_t)unaligned_nonce, (uintptr_t)message, + &unaligned_nonce_offset)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_PARSE_ERROR); + return; + } + OPK_Pack_size_t(msg, &unaligned_nonce_offset); + + size_t enc_rsa_key_offset = 0; + if (__builtin_sub_overflow((uintptr_t)enc_rsa_key, (uintptr_t)message, + &enc_rsa_key_offset)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_PARSE_ERROR); + return; + } + OPK_Pack_size_t(msg, &enc_rsa_key_offset); + + size_t enc_rsa_key_iv_offset = 0; + if (__builtin_sub_overflow((uintptr_t)enc_rsa_key_iv, (uintptr_t)message, + &enc_rsa_key_iv_offset)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_PARSE_ERROR); + return; + } + OPK_Pack_size_t(msg, &enc_rsa_key_iv_offset); + OPK_PackAlloc(msg, wrapped_rsa_key); + OPK_PackEOM(msg); + OPK_SharedBuffer_FinalizePacking(); +} diff --git a/oemcrypto/opk/serialization/ree/ree_special_cases.h b/oemcrypto/opk/serialization/ree/ree_special_cases.h new file mode 100644 index 0000000..cb3580c --- /dev/null +++ b/oemcrypto/opk/serialization/ree/ree_special_cases.h @@ -0,0 +1,35 @@ +/* + * 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 OPK_REE_SPECIAL_CASES_H_ +#define OPK_REE_SPECIAL_CASES_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "OEMCryptoCENC.h" +#include "odk_message.h" + +/* + * Special cases due to parameters defined as having pointers into other + * parameters + */ +void OPK_Pack_RewrapDeviceRSAKey_Request( + ODK_Message* msg, OEMCrypto_SESSION session, const uint8_t* message, + size_t message_length, const uint8_t* signature, size_t signature_length, + const uint32_t* unaligned_nonce, const uint8_t* enc_rsa_key, + size_t enc_rsa_key_length, const uint8_t* enc_rsa_key_iv, + const uint8_t* wrapped_rsa_key, const size_t* wrapped_rsa_key_length); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPK_REE_SPECIAL_CASES_H_ diff --git a/oemcrypto/opk/serialization/ree/ree_version.c b/oemcrypto/opk/serialization/ree/ree_version.c new file mode 100644 index 0000000..7720311 --- /dev/null +++ b/oemcrypto/opk/serialization/ree/ree_version.c @@ -0,0 +1,106 @@ +/* + * 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 + +#include "GEN_common_serializer.h" +#include "GEN_ree_serializer.h" +#include "api_support.h" +#include "log_macros.h" +#include "message_debug.h" +#include "version.h" + +/* + * Cached copies of the REE and TEE serialization versions + */ +static uint32_t ree_major_version = OPK_SERIALIZATION_VERSION_MAJOR; +static uint32_t ree_minor_version = OPK_SERIALIZATION_VERSION_MINOR; +static uint32_t tee_major_version = 0; +static uint32_t tee_minor_version = 0; + +OEMCryptoResult OPK_GetSerializationVersions(uint32_t* ree_major, + uint32_t* ree_minor, + uint32_t* tee_major, + uint32_t* tee_minor) { + if (!ree_major || !ree_minor || !tee_major || !tee_minor) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + /* If specific version is passed in, use it. Mostly for testing */ + ree_major_version = *ree_major ? *ree_major : OPK_SERIALIZATION_VERSION_MAJOR; + ree_minor_version = *ree_minor ? *ree_minor : OPK_SERIALIZATION_VERSION_MINOR; + + MDBG_NEXT_MESSAGE; + MDBG(BOLD BLUE "--> OPK_GetSerializationVersions " COLOR_OFF "entry"); + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + ODK_Message response = ODK_Message_Create(NULL, 0); + API_Initialize(); + ODK_Message request = OPK_Pack_OPK_SerializationVersion_Request( + &ree_major_version, &ree_minor_version, tee_major, tee_minor); + MDBG(GREEN "uint32_t*" RED " ree_major" COLOR_OFF "=%s", + OPK_Pointer_Str((const void*)&ree_major_version, "ree_major", + ree_major_version)); + MDBG(GREEN "uint32_t*" RED " ree_minor" COLOR_OFF "=%s", + OPK_Pointer_Str((const void*)&ree_minor_version, "ree_minor", + ree_minor_version)); + if (ODK_Message_GetStatus(&request) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + goto cleanup_and_return; + } + MDBG(COLOR_OFF "Sending REQUEST (%zu bytes)", ODK_Message_GetSize(&request)); + response = API_Transact(&request); + MDBG(COLOR_OFF "Received RESPONSE: (%zu bytes)", + ODK_Message_GetSize(&response)); + + uint32_t* tee_major_ptr = &tee_major_version; + uint32_t* tee_minor_ptr = &tee_minor_version; + OPK_Unpack_OPK_SerializationVersion_Response(&response, &result, &ree_major, + &ree_minor, &tee_major_ptr, + &tee_minor_ptr); + *tee_major = tee_major_version; + *tee_minor = tee_minor_version; + + MDBG(GREEN "OEMCryptoResult" RED " result" COLOR_OFF "=%s", + OEMCryptoResult_Str(result)); + MDBG(GREEN "uint32_t*" RED " tee_major" COLOR_OFF "=%s", + OPK_Pointer_Str((const void*)tee_major_ptr, "tee_major", + tee_major ? *tee_major_ptr : 0)); + MDBG(GREEN "uint32_t*" RED " tee_minor" COLOR_OFF "=%s", + OPK_Pointer_Str((const void*)tee_minor_ptr, "tee_minor", + tee_minor ? *tee_minor_ptr : 0)); + if (ODK_Message_GetStatus(&response) != MESSAGE_STATUS_OK) { + api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + +cleanup_and_return: + TOS_Transport_ReleaseMessage(&request); + TOS_Transport_ReleaseMessage(&response); + MDBG(BOLD BLUE "<-- OEMCrypto_OPK_SerializationVersion " COLOR_OFF "exit"); + result = API_CheckResult(result); + return result; +} + +bool OPK_IsVersionCompatible(void) { + if (!tee_major_version) { + OEMCryptoResult result = + OPK_GetSerializationVersions(&ree_major_version, &ree_minor_version, + &tee_major_version, &tee_minor_version); + if (result != OEMCrypto_SUCCESS) { + LOGE("Error getting serialization versions: %d", (int)result); + return true; // don't return false until we are sure of incompatibility + } + if (tee_major_version == ree_major_version) { + LOGI("REE version %" PRIu32 ".%" PRIu32 + " is compatible with TEE version is %" PRIu32 ".%" PRIu32, + ree_major_version, ree_minor_version, tee_major_version, + tee_minor_version); + } else { + LOGE("REE version %" PRIu32 ".%" PRIu32 + " is incompatible with TEE version is %" PRIu32 ".%" PRIu32, + ree_major_version, ree_minor_version, tee_major_version, + tee_minor_version); + } + } + return (tee_major_version == ree_major_version); +} diff --git a/oemcrypto/opk/serialization/ree/ree_version.h b/oemcrypto/opk/serialization/ree/ree_version.h new file mode 100644 index 0000000..f50fd0e --- /dev/null +++ b/oemcrypto/opk/serialization/ree/ree_version.h @@ -0,0 +1,30 @@ +/* + * 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 OPK_REE_VERSION_H_ +#define OPK_REE_VERSION_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Check if the REE and TEE versions are compatible. If they are, return + * true. Otherwise return false. This is called from OEMCrypto_Initialize. + */ +bool OPK_IsVersionCompatible(void); + +/* + * Retrieve the REE and TEE serialization versions. + */ +OEMCryptoResult OPK_GetSerializationVersions(uint32_t* ree_major, + uint32_t* ree_minor, + uint32_t* tee_major, + uint32_t* tee_minor); +#ifdef __cplusplus +} // extern "C" +#endif +#endif /* OPK_REE_VERSION_H_ */ diff --git a/oemcrypto/opk/serialization/ree/special_case_apis.c b/oemcrypto/opk/serialization/ree/special_case_apis.c new file mode 100644 index 0000000..593045d --- /dev/null +++ b/oemcrypto/opk/serialization/ree/special_case_apis.c @@ -0,0 +1,40 @@ +/* + * 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 +#include +#include +#include + +#include "GEN_common_serializer.h" +#include "GEN_ree_serializer.h" +#include "OEMCryptoCENC.h" +#include "api_support.h" +#include "common_special_cases.h" +#include "log_macros.h" +#include "opk_serialization_base.h" +#include "ree_special_cases.h" +#include "ree_version.h" +#include "shared_buffer_allocator.h" +#include "tos_shared_memory_interface.h" + +OEMCRYPTO_API OEMCryptoResult +OEMCrypto_OPK_SerializationVersion(uint32_t* ree_major, uint32_t* ree_minor, + uint32_t* tee_major, uint32_t* tee_minor) { + OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + pthread_mutex_lock(&api_lock); + if (!ree_major || !ree_minor || !tee_major || !tee_minor) { + result = OEMCrypto_ERROR_UNKNOWN_FAILURE; + } else { + result = OPK_GetSerializationVersions(ree_major, ree_minor, tee_major, + tee_minor); + if (result == OEMCrypto_SUCCESS && !OPK_IsVersionCompatible()) { + result = OPK_ERROR_INCOMPATIBLE_VERSION; + } + } + pthread_mutex_unlock(&api_lock); + return result; +} diff --git a/oemcrypto/opk/serialization/settings.gypi b/oemcrypto/opk/serialization/settings.gypi new file mode 100644 index 0000000..c2a970f --- /dev/null +++ b/oemcrypto/opk/serialization/settings.gypi @@ -0,0 +1,42 @@ +# Common settings for OPK serialization +{ + 'variables': { + 'oemcrypto_dir' : '<(DEPTH)/oemcrypto', + 'odk_dir' : '<(oemcrypto_dir)/odk', + 'oemcrypto_ta_dir' : '<(oemcrypto_dir)/opk/oemcrypto_ta', + 'serialization_dir' : '<(oemcrypto_dir)/opk/serialization', + 'common_dir' : '<(serialization_dir)/common', + 'generator_dir' : '<(serialization_dir)/generator', + 'os_interfaces_dir' : '<(serialization_dir)/os_interfaces', + 'ree_dir' : '<(serialization_dir)/ree', + 'tee_dir' : '<(serialization_dir)/tee', + 'third_party_dir' : '<(DEPTH)/third_party', + 'json_dir' : '<(third_party_dir)/nlohmann-json', + 'enable_message_debug' : ' + +#include "GEN_common_serializer.h" +#include "GEN_tee_serializer.h" +#include "OEMCryptoCENC.h" +#include "bump_allocator.h" +#include "log_macros.h" +#include "marshaller_base.h" +#include "message_debug.h" +#include "oemcrypto_wall_clock.h" +#include "opk_dispatcher.h" +#include "shared_buffer_allocator.h" +#include "tee_special_cases.h" +#include "tos_shared_memory_interface.h" +#include "tos_transport_interface.h" + +static ODK_Message CreateEmptyMessage(void) { + static uint8_t buffer[1]; + ODK_Message msg = ODK_Message_Create(buffer, sizeof(buffer)); + OPK_PackEOM(&msg); + return msg; +} + +static ODK_Message NullMessage(void) { return ODK_Message_Create(NULL, 0); } + +void OPK_Init_OEMCrypto_Substring(OEMCrypto_Substring* obj) { + OPK_Init_size_t((size_t*)&obj->offset); + OPK_Init_size_t((size_t*)&obj->length); +} + +void OPK_Init_OEMCrypto_DTCP2_CMI_Descriptor_0( + OEMCrypto_DTCP2_CMI_Descriptor_0* obj) { + OPK_Init_uint8_t((uint8_t*)&obj->id); + OPK_Init_uint8_t((uint8_t*)&obj->extension); + OPK_Init_uint16_t((uint16_t*)&obj->length); + OPK_Init_uint8_t((uint8_t*)&obj->data); +} + +void OPK_Init_OEMCrypto_DTCP2_CMI_Descriptor_1( + OEMCrypto_DTCP2_CMI_Descriptor_1* obj) { + OPK_Init_uint8_t((uint8_t*)&obj->id); + OPK_Init_uint8_t((uint8_t*)&obj->extension); + OPK_Init_uint16_t((uint16_t*)&obj->length); + OPK_InitMemory(&obj->data[0], 3); +} + +void OPK_Init_OEMCrypto_DTCP2_CMI_Descriptor_2( + OEMCrypto_DTCP2_CMI_Descriptor_2* obj) { + OPK_Init_uint8_t((uint8_t*)&obj->id); + OPK_Init_uint8_t((uint8_t*)&obj->extension); + OPK_Init_uint16_t((uint16_t*)&obj->length); + OPK_InitMemory(&obj->data[0], 3); +} + +void OPK_Init_OEMCrypto_DTCP2_CMI_Packet(OEMCrypto_DTCP2_CMI_Packet* obj) { + OPK_Init_uint8_t((uint8_t*)&obj->dtcp2_required); + OPK_Init_OEMCrypto_DTCP2_CMI_Descriptor_0( + (OEMCrypto_DTCP2_CMI_Descriptor_0*)&obj->cmi_descriptor_0); + OPK_Init_OEMCrypto_DTCP2_CMI_Descriptor_1( + (OEMCrypto_DTCP2_CMI_Descriptor_1*)&obj->cmi_descriptor_1); + OPK_Init_OEMCrypto_DTCP2_CMI_Descriptor_2( + (OEMCrypto_DTCP2_CMI_Descriptor_2*)&obj->cmi_descriptor_2); +} + +void OPK_Init_OEMCrypto_KeyObject(OEMCrypto_KeyObject* obj) { + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&obj->key_id); + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&obj->key_data_iv); + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&obj->key_data); + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&obj->key_control_iv); + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&obj->key_control); +} + +void OPK_Init_OEMCrypto_InputOutputPair(OEMCrypto_InputOutputPair* obj) { + OPK_Init_size_t((size_t*)&obj->input_data_length); + OEMCrypto_SharedMemory* input_data; + input_data = (OEMCrypto_SharedMemory*)OPK_VarAlloc(obj->input_data_length); + OPK_InitMemory((uint8_t*)input_data, obj->input_data_length); + OPK_Init_OEMCrypto_DestBufferDesc( + (OEMCrypto_DestBufferDesc*)&obj->output_descriptor); + memcpy(&obj->input_data, &input_data, sizeof(input_data)); +} + +void OPK_Init_OEMCrypto_SubSampleDescription( + OEMCrypto_SubSampleDescription* obj) { + OPK_Init_size_t((size_t*)&obj->num_bytes_clear); + OPK_Init_size_t((size_t*)&obj->num_bytes_encrypted); + OPK_Init_uint8_t((uint8_t*)&obj->subsample_flags); + OPK_Init_size_t((size_t*)&obj->block_offset); +} + +void OPK_Init_OEMCrypto_SampleDescription(OEMCrypto_SampleDescription* obj) { + OPK_Init_size_t((size_t*)&obj->subsamples_length); + OPK_Init_OEMCrypto_InputOutputPair((OEMCrypto_InputOutputPair*)&obj->buffers); + OPK_InitMemory(&obj->iv[0], 16); + OEMCrypto_SubSampleDescription* subsamples; + subsamples = (OEMCrypto_SubSampleDescription*)OPK_VarAlloc( + obj->subsamples_length * sizeof(OEMCrypto_SubSampleDescription)); + if (subsamples) { + for (size_t i = 0; i < obj->subsamples_length; i++) { + OPK_Init_OEMCrypto_SubSampleDescription( + (OEMCrypto_SubSampleDescription*)&subsamples[i]); + } + } + memcpy(&obj->subsamples, &subsamples, sizeof(subsamples)); +} + +void OPK_Init_OEMCrypto_CENCEncryptPatternDesc( + OEMCrypto_CENCEncryptPatternDesc* obj) { + OPK_Init_size_t((size_t*)&obj->encrypt); + OPK_Init_size_t((size_t*)&obj->skip); +} + +void OPK_Init_OEMCrypto_EntitledContentKeyObject( + OEMCrypto_EntitledContentKeyObject* obj) { + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&obj->entitlement_key_id); + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&obj->content_key_id); + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&obj->content_key_data_iv); + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&obj->content_key_data); + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&obj->content_iv); + OPK_Init_uint32_t((uint32_t*)&obj->cipher_mode); +} + +void OPK_Init_OEMCrypto_KeyRefreshObject(OEMCrypto_KeyRefreshObject* obj) { + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&obj->key_id); + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&obj->key_control_iv); + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&obj->key_control); +} + +void OPK_Init_OEMCrypto_CENCEncryptPatternDesc_V15( + OEMCrypto_CENCEncryptPatternDesc_V15* obj) { + OPK_Init_size_t((size_t*)&obj->encrypt); + OPK_Init_size_t((size_t*)&obj->skip); + OPK_Init_size_t((size_t*)&obj->offset); +} + +void OPK_Init_OEMCrypto_EntitledContentKeyObject_V16( + OEMCrypto_EntitledContentKeyObject_V16* obj) { + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&obj->entitlement_key_id); + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&obj->content_key_id); + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&obj->content_key_data_iv); + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&obj->content_key_data); +} + +/* See opk_dispatcher.h for definition of OPK_DispatchMessage() */ +ODK_MessageStatus OPK_DispatchMessage(ODK_Message* request, + ODK_Message* response) { + if (request == NULL || response == NULL) + return MESSAGE_STATUS_NULL_POINTER_ERROR; + *response = NullMessage(); + uint32_t api_value; + OPK_Unpack_uint32_t(request, &api_value); + uint64_t timestamp; + OPK_Unpack_uint64_t(request, ×tamp); + OPK_SetWallClockTime(timestamp); + OPK_BumpAllocator_Reset(); + OPK_SharedBuffer_Reset(); + ODK_Message_Reset(request); + switch (api_value) { + case 84: /* OEMCrypto_SetSandbox */ + { + size_t sandbox_id_length; + OPK_Init_size_t((size_t*)&sandbox_id_length); + uint8_t* sandbox_id; + OPK_InitPointer((uint8_t**)&sandbox_id); + OPK_Unpack_SetSandbox_Request(request, &sandbox_id, &sandbox_id_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("SetSandbox"); + result = OEMCrypto_SetSandbox(sandbox_id, sandbox_id_length); + *response = OPK_Pack_SetSandbox_Response(result); + break; + } + case 1: /* OEMCrypto_Initialize */ + { + OPK_Unpack_Initialize_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("Initialize"); + result = OEMCrypto_Initialize(); + *response = OPK_Pack_Initialize_Response(result); + break; + } + case 2: /* OEMCrypto_Terminate */ + { + OPK_Unpack_Terminate_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("Terminate"); + result = OEMCrypto_Terminate(); + *response = OPK_Pack_Terminate_Response(result); + break; + } + case 123: /* OEMCrypto_Idle */ + { + OEMCrypto_IdleState state; + OPK_Init_uint32_t((uint32_t*)&state); + uint32_t os_specific_code; + OPK_Init_uint32_t((uint32_t*)&os_specific_code); + OPK_Unpack_Idle_Request(request, &state, &os_specific_code); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("Idle"); + result = OEMCrypto_Idle(state, os_specific_code); + *response = OPK_Pack_Idle_Response(result); + break; + } + case 124: /* OEMCrypto_Wake */ + { + OPK_Unpack_Wake_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("Wake"); + result = OEMCrypto_Wake(); + *response = OPK_Pack_Wake_Response(result); + break; + } + case 9: /* OEMCrypto_OpenSession */ + { + OEMCrypto_SESSION* session; + OPK_InitPointer((uint8_t**)&session); + OPK_Unpack_OpenSession_Request(request, &session); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("OpenSession"); + result = OEMCrypto_OpenSession(session); + *response = OPK_Pack_OpenSession_Response(result, session); + break; + } + case 10: /* OEMCrypto_CloseSession */ + { + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + OPK_Unpack_CloseSession_Request(request, &session); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("CloseSession"); + result = OEMCrypto_CloseSession(session); + *response = OPK_Pack_CloseSession_Response(result); + break; + } + case 111: /* OEMCrypto_CreateEntitledKeySession */ + { + OEMCrypto_SESSION oec_session; + OPK_Init_uint32_t((uint32_t*)&oec_session); + OEMCrypto_SESSION* key_session; + OPK_InitPointer((uint8_t**)&key_session); + OPK_Unpack_CreateEntitledKeySession_Request(request, &oec_session, + &key_session); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("CreateEntitledKeySession"); + result = OEMCrypto_CreateEntitledKeySession(oec_session, key_session); + *response = + OPK_Pack_CreateEntitledKeySession_Response(result, key_session); + break; + } + case 112: /* OEMCrypto_RemoveEntitledKeySession */ + { + OEMCrypto_SESSION key_session; + OPK_Init_uint32_t((uint32_t*)&key_session); + OPK_Unpack_RemoveEntitledKeySession_Request(request, &key_session); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("RemoveEntitledKeySession"); + result = OEMCrypto_RemoveEntitledKeySession(key_session); + *response = OPK_Pack_RemoveEntitledKeySession_Response(result); + break; + } + case 95: /* OEMCrypto_GenerateDerivedKeys */ + { + size_t mac_key_context_length; + OPK_Init_size_t((size_t*)&mac_key_context_length); + size_t enc_key_context_length; + OPK_Init_size_t((size_t*)&enc_key_context_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + OEMCrypto_SharedMemory* mac_key_context; + OPK_InitPointer((uint8_t**)&mac_key_context); + OEMCrypto_SharedMemory* enc_key_context; + OPK_InitPointer((uint8_t**)&enc_key_context); + OPK_Unpack_GenerateDerivedKeys_Request( + request, &session, &mac_key_context, &mac_key_context_length, + &enc_key_context, &enc_key_context_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GenerateDerivedKeys"); + result = OEMCrypto_GenerateDerivedKeys( + session, mac_key_context, mac_key_context_length, enc_key_context, + enc_key_context_length); + *response = OPK_Pack_GenerateDerivedKeys_Response(result); + break; + } + case 21: /* OEMCrypto_DeriveKeysFromSessionKey */ + { + size_t derivation_key_length; + OPK_Init_size_t((size_t*)&derivation_key_length); + size_t mac_key_context_length; + OPK_Init_size_t((size_t*)&mac_key_context_length); + size_t enc_key_context_length; + OPK_Init_size_t((size_t*)&enc_key_context_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint8_t* derivation_key; + OPK_InitPointer((uint8_t**)&derivation_key); + OEMCrypto_SharedMemory* mac_key_context; + OPK_InitPointer((uint8_t**)&mac_key_context); + OEMCrypto_SharedMemory* enc_key_context; + OPK_InitPointer((uint8_t**)&enc_key_context); + OPK_Unpack_DeriveKeysFromSessionKey_Request( + request, &session, &derivation_key, &derivation_key_length, + &mac_key_context, &mac_key_context_length, &enc_key_context, + &enc_key_context_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("DeriveKeysFromSessionKey"); + result = OEMCrypto_DeriveKeysFromSessionKey( + session, derivation_key, derivation_key_length, mac_key_context, + mac_key_context_length, enc_key_context, enc_key_context_length); + *response = OPK_Pack_DeriveKeysFromSessionKey_Response(result); + break; + } + case 14: /* OEMCrypto_GenerateNonce */ + { + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint32_t* nonce; + OPK_InitPointer((uint8_t**)&nonce); + OPK_Unpack_GenerateNonce_Request(request, &session, &nonce); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GenerateNonce"); + result = OEMCrypto_GenerateNonce(session, nonce); + *response = OPK_Pack_GenerateNonce_Response(result, nonce); + break; + } + case 96: /* OEMCrypto_PrepAndSignLicenseRequest */ + { + size_t message_length; + OPK_Init_size_t((size_t*)&message_length); + size_t* signature_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(signature_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint8_t* message; + OPK_InitPointer((uint8_t**)&message); + size_t* core_message_size = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(core_message_size); + uint8_t* signature; + OPK_InitPointer((uint8_t**)&signature); + OPK_Unpack_PrepAndSignLicenseRequest_Request( + request, &session, &message, &message_length, &core_message_size, + &signature, &signature_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("PrepAndSignLicenseRequest"); + result = OEMCrypto_PrepAndSignLicenseRequest( + session, message, message_length, core_message_size, signature, + signature_length); + *response = OPK_Pack_PrepAndSignLicenseRequest_Response( + result, message, message_length, core_message_size, signature, + signature_length); + break; + } + case 97: /* OEMCrypto_PrepAndSignRenewalRequest */ + { + size_t message_length; + OPK_Init_size_t((size_t*)&message_length); + size_t* signature_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(signature_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint8_t* message; + OPK_InitPointer((uint8_t**)&message); + size_t* core_message_size = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(core_message_size); + uint8_t* signature; + OPK_InitPointer((uint8_t**)&signature); + OPK_Unpack_PrepAndSignRenewalRequest_Request( + request, &session, &message, &message_length, &core_message_size, + &signature, &signature_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("PrepAndSignRenewalRequest"); + result = OEMCrypto_PrepAndSignRenewalRequest( + session, message, message_length, core_message_size, signature, + signature_length); + *response = OPK_Pack_PrepAndSignRenewalRequest_Response( + result, message, message_length, core_message_size, signature, + signature_length); + break; + } + case 83: /* OEMCrypto_LoadKeys */ + { + size_t message_length; + OPK_Init_size_t((size_t*)&message_length); + size_t signature_length; + OPK_Init_size_t((size_t*)&signature_length); + size_t key_array_length; + OPK_Init_size_t((size_t*)&key_array_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint8_t* message; + OPK_InitPointer((uint8_t**)&message); + uint8_t* signature; + OPK_InitPointer((uint8_t**)&signature); + OEMCrypto_Substring enc_mac_keys_iv; + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&enc_mac_keys_iv); + OEMCrypto_Substring enc_mac_keys; + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&enc_mac_keys); + OEMCrypto_KeyObject* key_array; + OPK_InitPointer((uint8_t**)&key_array); + OEMCrypto_Substring pst; + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&pst); + OEMCrypto_Substring srm_restriction_data; + OPK_Init_OEMCrypto_Substring((OEMCrypto_Substring*)&srm_restriction_data); + OEMCrypto_LicenseType license_type; + OPK_Init_uint32_t((uint32_t*)&license_type); + OPK_Unpack_LoadKeys_Request( + request, &session, &message, &message_length, &signature, + &signature_length, &enc_mac_keys_iv, &enc_mac_keys, &key_array_length, + &key_array, &pst, &srm_restriction_data, &license_type); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("LoadKeys"); + result = OEMCrypto_LoadKeys(session, message, message_length, signature, + signature_length, enc_mac_keys_iv, + enc_mac_keys, key_array_length, key_array, + pst, srm_restriction_data, license_type); + *response = OPK_Pack_LoadKeys_Response(result); + break; + } + case 99: /* OEMCrypto_LoadLicense */ + { + size_t message_length; + OPK_Init_size_t((size_t*)&message_length); + size_t signature_length; + OPK_Init_size_t((size_t*)&signature_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint8_t* message; + OPK_InitPointer((uint8_t**)&message); + size_t core_message_length; + OPK_Init_size_t((size_t*)&core_message_length); + uint8_t* signature; + OPK_InitPointer((uint8_t**)&signature); + OPK_Unpack_LoadLicense_Request(request, &session, &message, + &message_length, &core_message_length, + &signature, &signature_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("LoadLicense"); + result = OEMCrypto_LoadLicense(session, message, message_length, + core_message_length, signature, + signature_length); + *response = OPK_Pack_LoadLicense_Response(result); + break; + } + case 121: /* OEMCrypto_LoadEntitledContentKeys */ + { + size_t message_length; + OPK_Init_size_t((size_t*)&message_length); + size_t key_array_length; + OPK_Init_size_t((size_t*)&key_array_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint8_t* message; + OPK_InitPointer((uint8_t**)&message); + OEMCrypto_EntitledContentKeyObject* key_array; + OPK_InitPointer((uint8_t**)&key_array); + OPK_Unpack_LoadEntitledContentKeys_Request(request, &session, &message, + &message_length, + &key_array_length, &key_array); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("LoadEntitledContentKeys"); + result = OEMCrypto_LoadEntitledContentKeys( + session, message, message_length, key_array_length, key_array); + *response = OPK_Pack_LoadEntitledContentKeys_Response(result); + break; + } + case 91: /* OEMCrypto_RefreshKeys */ + { + size_t message_length; + OPK_Init_size_t((size_t*)&message_length); + size_t signature_length; + OPK_Init_size_t((size_t*)&signature_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint8_t* message; + OPK_InitPointer((uint8_t**)&message); + uint8_t* signature; + OPK_InitPointer((uint8_t**)&signature); + size_t num_keys; + OPK_Init_size_t((size_t*)&num_keys); + OEMCrypto_KeyRefreshObject* key_array = + (OEMCrypto_KeyRefreshObject*)OPK_VarAlloc( + sizeof(OEMCrypto_KeyRefreshObject)); + OPK_Init_OEMCrypto_KeyRefreshObject( + (OEMCrypto_KeyRefreshObject*)key_array); + OPK_Unpack_RefreshKeys_Request(request, &session, &message, + &message_length, &signature, + &signature_length, &num_keys, &key_array); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("RefreshKeys"); + result = + OEMCrypto_RefreshKeys(session, message, message_length, signature, + signature_length, num_keys, key_array); + *response = OPK_Pack_RefreshKeys_Response(result); + break; + } + case 101: /* OEMCrypto_LoadRenewal */ + { + size_t message_length; + OPK_Init_size_t((size_t*)&message_length); + size_t signature_length; + OPK_Init_size_t((size_t*)&signature_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint8_t* message; + OPK_InitPointer((uint8_t**)&message); + size_t core_message_length; + OPK_Init_size_t((size_t*)&core_message_length); + uint8_t* signature; + OPK_InitPointer((uint8_t**)&signature); + OPK_Unpack_LoadRenewal_Request(request, &session, &message, + &message_length, &core_message_length, + &signature, &signature_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("LoadRenewal"); + result = OEMCrypto_LoadRenewal(session, message, message_length, + core_message_length, signature, + signature_length); + *response = OPK_Pack_LoadRenewal_Response(result); + break; + } + case 41: /* OEMCrypto_QueryKeyControl */ + { + size_t content_key_id_length; + OPK_Init_size_t((size_t*)&content_key_id_length); + size_t* key_control_block_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(key_control_block_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint8_t* content_key_id; + OPK_InitPointer((uint8_t**)&content_key_id); + uint8_t* key_control_block; + OPK_InitPointer((uint8_t**)&key_control_block); + OPK_Unpack_QueryKeyControl_Request( + request, &session, &content_key_id, &content_key_id_length, + &key_control_block, &key_control_block_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("QueryKeyControl"); + result = OEMCrypto_QueryKeyControl( + session, content_key_id, content_key_id_length, key_control_block, + key_control_block_length); + *response = OPK_Pack_QueryKeyControl_Response(result, key_control_block, + key_control_block_length); + break; + } + case 81: /* OEMCrypto_SelectKey */ + { + size_t content_key_id_length; + OPK_Init_size_t((size_t*)&content_key_id_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint8_t* content_key_id; + OPK_InitPointer((uint8_t**)&content_key_id); + OEMCryptoCipherMode cipher_mode; + OPK_Init_uint32_t((uint32_t*)&cipher_mode); + OPK_Unpack_SelectKey_Request(request, &session, &content_key_id, + &content_key_id_length, &cipher_mode); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + result = OEMCrypto_SelectKey(session, content_key_id, + content_key_id_length, cipher_mode); + *response = OPK_Pack_SelectKey_Response(result); + break; + } + case 105: /* OEMCrypto_DecryptCENC */ + { + size_t samples_length; + OPK_Init_size_t((size_t*)&samples_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + OEMCrypto_SampleDescription* samples; + OPK_InitPointer((uint8_t**)&samples); + OEMCrypto_CENCEncryptPatternDesc* pattern = + (OEMCrypto_CENCEncryptPatternDesc*)OPK_VarAlloc( + sizeof(OEMCrypto_CENCEncryptPatternDesc)); + OPK_Init_OEMCrypto_CENCEncryptPatternDesc( + (OEMCrypto_CENCEncryptPatternDesc*)pattern); + OPK_Unpack_DecryptCENC_Request(request, &session, &samples, + &samples_length, &pattern); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + result = OEMCrypto_DecryptCENC(session, samples, samples_length, pattern); + *response = OPK_Pack_DecryptCENC_Response(result); + break; + } + case 93: /* OEMCrypto_CopyBuffer */ + { + size_t data_addr_length; + OPK_Init_size_t((size_t*)&data_addr_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + OEMCrypto_SharedMemory* data_addr; + OPK_InitPointer((uint8_t**)&data_addr); + OEMCrypto_DestBufferDesc* out_buffer_descriptor = + (OEMCrypto_DestBufferDesc*)OPK_VarAlloc( + sizeof(OEMCrypto_DestBufferDesc)); + OPK_Init_OEMCrypto_DestBufferDesc( + (OEMCrypto_DestBufferDesc*)out_buffer_descriptor); + uint8_t subsample_flags; + OPK_Init_uint8_t((uint8_t*)&subsample_flags); + OPK_Unpack_CopyBuffer_Request(request, &session, &data_addr, + &data_addr_length, &out_buffer_descriptor, + &subsample_flags); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + result = OEMCrypto_CopyBuffer(session, data_addr, data_addr_length, + out_buffer_descriptor, subsample_flags); + *response = OPK_Pack_CopyBuffer_Response(result); + break; + } + case 24: /* OEMCrypto_Generic_Encrypt */ + { + size_t in_buffer_length; + OPK_Init_size_t((size_t*)&in_buffer_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + OEMCrypto_SharedMemory* in_buffer; + OPK_InitPointer((uint8_t**)&in_buffer); + uint8_t iv[16]; + OPK_InitMemory(&iv[0], 16); + OEMCrypto_Algorithm algorithm; + OPK_Init_uint32_t((uint32_t*)&algorithm); + OEMCrypto_SharedMemory* out_buffer; + OPK_InitPointer((uint8_t**)&out_buffer); + OPK_Unpack_Generic_Encrypt_Request(request, &session, &in_buffer, + &in_buffer_length, &iv[0], &algorithm, + &out_buffer); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("Generic_Encrypt"); + result = OEMCrypto_Generic_Encrypt(session, in_buffer, in_buffer_length, + iv, algorithm, out_buffer); + *response = OPK_Pack_Generic_Encrypt_Response( + result, in_buffer_length, OPK_SharedBuffer_NextOutputBuffer()); + break; + } + case 25: /* OEMCrypto_Generic_Decrypt */ + { + size_t in_buffer_length; + OPK_Init_size_t((size_t*)&in_buffer_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + OEMCrypto_SharedMemory* in_buffer; + OPK_InitPointer((uint8_t**)&in_buffer); + uint8_t iv[16]; + OPK_InitMemory(&iv[0], 16); + OEMCrypto_Algorithm algorithm; + OPK_Init_uint32_t((uint32_t*)&algorithm); + OEMCrypto_SharedMemory* out_buffer; + OPK_InitPointer((uint8_t**)&out_buffer); + OPK_Unpack_Generic_Decrypt_Request(request, &session, &in_buffer, + &in_buffer_length, &iv[0], &algorithm, + &out_buffer); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("Generic_Decrypt"); + result = OEMCrypto_Generic_Decrypt(session, in_buffer, in_buffer_length, + iv, algorithm, out_buffer); + *response = OPK_Pack_Generic_Decrypt_Response( + result, in_buffer_length, OPK_SharedBuffer_NextOutputBuffer()); + break; + } + case 26: /* OEMCrypto_Generic_Sign */ + { + size_t buffer_length; + OPK_Init_size_t((size_t*)&buffer_length); + size_t* signature_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(signature_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + OEMCrypto_SharedMemory* buffer; + OPK_InitPointer((uint8_t**)&buffer); + OEMCrypto_Algorithm algorithm; + OPK_Init_uint32_t((uint32_t*)&algorithm); + OEMCrypto_SharedMemory* signature; + OPK_InitPointer((uint8_t**)&signature); + OPK_Unpack_Generic_Sign_Request(request, &session, &buffer, + &buffer_length, &algorithm, &signature, + &signature_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("Generic_Sign"); + result = OEMCrypto_Generic_Sign(session, buffer, buffer_length, algorithm, + signature, signature_length); + *response = OPK_Pack_Generic_Sign_Response( + result, OPK_SharedBuffer_NextOutputBuffer(), signature_length); + break; + } + case 27: /* OEMCrypto_Generic_Verify */ + { + size_t buffer_length; + OPK_Init_size_t((size_t*)&buffer_length); + size_t signature_length; + OPK_Init_size_t((size_t*)&signature_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + OEMCrypto_SharedMemory* buffer; + OPK_InitPointer((uint8_t**)&buffer); + OEMCrypto_Algorithm algorithm; + OPK_Init_uint32_t((uint32_t*)&algorithm); + OEMCrypto_SharedMemory* signature; + OPK_InitPointer((uint8_t**)&signature); + OPK_Unpack_Generic_Verify_Request(request, &session, &buffer, + &buffer_length, &algorithm, &signature, + &signature_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("Generic_Verify"); + result = OEMCrypto_Generic_Verify(session, buffer, buffer_length, + algorithm, signature, signature_length); + *response = OPK_Pack_Generic_Verify_Response(result); + break; + } + case 8: /* OEMCrypto_WrapKeyboxOrOEMCert */ + { + size_t keybox_or_cert_length; + OPK_Init_size_t((size_t*)&keybox_or_cert_length); + size_t* wrapped_keybox_or_cert_length = + (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(wrapped_keybox_or_cert_length); + size_t transport_key_length; + OPK_Init_size_t((size_t*)&transport_key_length); + uint8_t* keybox_or_cert; + OPK_InitPointer((uint8_t**)&keybox_or_cert); + uint8_t* wrapped_keybox_or_cert; + OPK_InitPointer((uint8_t**)&wrapped_keybox_or_cert); + uint8_t* transport_key; + OPK_InitPointer((uint8_t**)&transport_key); + OPK_Unpack_WrapKeyboxOrOEMCert_Request( + request, &keybox_or_cert, &keybox_or_cert_length, + &wrapped_keybox_or_cert, &wrapped_keybox_or_cert_length, + &transport_key, &transport_key_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("WrapKeyboxOrOEMCert"); + result = OEMCrypto_WrapKeyboxOrOEMCert( + keybox_or_cert, keybox_or_cert_length, wrapped_keybox_or_cert, + wrapped_keybox_or_cert_length, transport_key, transport_key_length); + *response = OPK_Pack_WrapKeyboxOrOEMCert_Response( + result, wrapped_keybox_or_cert, wrapped_keybox_or_cert_length); + break; + } + case 3: /* OEMCrypto_InstallKeyboxOrOEMCert */ + { + size_t keybox_or_cert_length; + OPK_Init_size_t((size_t*)&keybox_or_cert_length); + uint8_t* keybox_or_cert; + OPK_InitPointer((uint8_t**)&keybox_or_cert); + OPK_Unpack_InstallKeyboxOrOEMCert_Request(request, &keybox_or_cert, + &keybox_or_cert_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("InstallKeyboxOrOEMCert"); + result = OEMCrypto_InstallKeyboxOrOEMCert(keybox_or_cert, + keybox_or_cert_length); + *response = OPK_Pack_InstallKeyboxOrOEMCert_Response(result); + break; + } + case 49: /* OEMCrypto_GetProvisioningMethod */ + { + OPK_Unpack_GetProvisioningMethod_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCrypto_ProvisioningMethod result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GetProvisioningMethod"); + result = OEMCrypto_GetProvisioningMethod(); + *response = OPK_Pack_GetProvisioningMethod_Response(result); + break; + } + case 5: /* OEMCrypto_IsKeyboxOrOEMCertValid */ + { + OPK_Unpack_IsKeyboxOrOEMCertValid_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("IsKeyboxOrOEMCertValid"); + result = OEMCrypto_IsKeyboxOrOEMCertValid(); + *response = OPK_Pack_IsKeyboxOrOEMCertValid_Response(result); + break; + } + case 7: /* OEMCrypto_GetDeviceID */ + { + size_t* device_id_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(device_id_length); + uint8_t* device_id; + OPK_InitPointer((uint8_t**)&device_id); + OPK_Unpack_GetDeviceID_Request(request, &device_id, &device_id_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GetDeviceID"); + result = OEMCrypto_GetDeviceID(device_id, device_id_length); + *response = + OPK_Pack_GetDeviceID_Response(result, device_id, device_id_length); + break; + } + case 4: /* OEMCrypto_GetKeyData */ + { + size_t* key_data_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(key_data_length); + uint8_t* key_data; + OPK_InitPointer((uint8_t**)&key_data); + OPK_Unpack_GetKeyData_Request(request, &key_data, &key_data_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GetKeyData"); + result = OEMCrypto_GetKeyData(key_data, key_data_length); + *response = + OPK_Pack_GetKeyData_Response(result, key_data, key_data_length); + break; + } + case 78: /* OEMCrypto_LoadTestKeybox */ + { + size_t buffer_length; + OPK_Init_size_t((size_t*)&buffer_length); + uint8_t* buffer; + OPK_InitPointer((uint8_t**)&buffer); + OPK_Unpack_LoadTestKeybox_Request(request, &buffer, &buffer_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("LoadTestKeybox"); + result = OEMCrypto_LoadTestKeybox(buffer, buffer_length); + *response = OPK_Pack_LoadTestKeybox_Response(result); + break; + } + case 103: /* OEMCrypto_LoadOEMPrivateKey */ + { + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + OPK_Unpack_LoadOEMPrivateKey_Request(request, &session); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("LoadOEMPrivateKey"); + result = OEMCrypto_LoadOEMPrivateKey(session); + *response = OPK_Pack_LoadOEMPrivateKey_Response(result); + break; + } + case 104: /* OEMCrypto_GetOEMPublicCertificate */ + { + size_t* public_cert_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(public_cert_length); + uint8_t* public_cert; + OPK_InitPointer((uint8_t**)&public_cert); + OPK_Unpack_GetOEMPublicCertificate_Request(request, &public_cert, + &public_cert_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GetOEMPublicCertificate"); + result = + OEMCrypto_GetOEMPublicCertificate(public_cert, public_cert_length); + *response = OPK_Pack_GetOEMPublicCertificate_Response(result, public_cert, + public_cert_length); + break; + } + case 6: /* OEMCrypto_GetRandom */ + { + size_t random_data_length; + OPK_Init_size_t((size_t*)&random_data_length); + uint8_t* random_data; + OPK_InitPointer((uint8_t**)&random_data); + OPK_Unpack_GetRandom_Request(request, &random_data, &random_data_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GetRandom"); + result = OEMCrypto_GetRandom(random_data, random_data_length); + *response = + OPK_Pack_GetRandom_Response(result, random_data, random_data_length); + break; + } + case 22: /* OEMCrypto_APIVersion */ + { + OPK_Unpack_APIVersion_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + uint32_t result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("APIVersion"); + result = OEMCrypto_APIVersion(); + *response = OPK_Pack_APIVersion_Response(result); + break; + } + case 108: /* OEMCrypto_MinorAPIVersion */ + { + OPK_Unpack_MinorAPIVersion_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + uint32_t result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("MinorAPIVersion"); + result = OEMCrypto_MinorAPIVersion(); + *response = OPK_Pack_MinorAPIVersion_Response(result); + break; + } + case 125: /* OEMCrypto_BuildInformation */ + { + size_t* buffer_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(buffer_length); + char* buffer; + OPK_InitPointer((uint8_t**)&buffer); + OPK_Unpack_BuildInformation_Request(request, &buffer, &buffer_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("BuildInformation"); + result = OEMCrypto_BuildInformation(buffer, buffer_length); + *response = + OPK_Pack_BuildInformation_Response(result, buffer, buffer_length); + break; + } + case 46: /* OEMCrypto_Security_Patch_Level */ + { + OPK_Unpack_Security_Patch_Level_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + uint8_t result; + OPK_Init_uint8_t((uint8_t*)&result); + LOGD("Security_Patch_Level"); + result = OEMCrypto_Security_Patch_Level(); + *response = OPK_Pack_Security_Patch_Level_Response(result); + break; + } + case 126: /* OEMCrypto_SecurityLevel */ + { + OPK_Unpack_SecurityLevel_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCrypto_Security_Level result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("SecurityLevel"); + result = OEMCrypto_SecurityLevel(); + *response = OPK_Pack_SecurityLevel_Response(result); + break; + } + case 44: /* OEMCrypto_GetHDCPCapability */ + { + OEMCrypto_HDCP_Capability* current; + OPK_InitPointer((uint8_t**)¤t); + OEMCrypto_HDCP_Capability* maximum; + OPK_InitPointer((uint8_t**)&maximum); + OPK_Unpack_GetHDCPCapability_Request(request, ¤t, &maximum); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GetHDCPCapability"); + result = OEMCrypto_GetHDCPCapability(current, maximum); + *response = OPK_Pack_GetHDCPCapability_Response(result, current, maximum); + break; + } + case 128: /* OEMCrypto_GetDTCP2Capability */ + { + OEMCrypto_DTCP2_Capability* capability; + OPK_InitPointer((uint8_t**)&capability); + OPK_Unpack_GetDTCP2Capability_Request(request, &capability); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GetDTCP2Capability"); + result = OEMCrypto_GetDTCP2Capability(capability); + *response = OPK_Pack_GetDTCP2Capability_Response(result, capability); + break; + } + case 29: /* OEMCrypto_SupportsUsageTable */ + { + OPK_Unpack_SupportsUsageTable_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + bool result; + OPK_Init_bool((bool*)&result); + LOGD("SupportsUsageTable"); + result = OEMCrypto_SupportsUsageTable(); + *response = OPK_Pack_SupportsUsageTable_Response(result); + break; + } + case 94: /* OEMCrypto_MaximumUsageTableHeaderSize */ + { + OPK_Unpack_MaximumUsageTableHeaderSize_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + size_t result; + OPK_Init_size_t((size_t*)&result); + LOGD("MaximumUsageTableHeaderSize"); + result = OEMCrypto_MaximumUsageTableHeaderSize(); + *response = OPK_Pack_MaximumUsageTableHeaderSize_Response(result); + break; + } + case 39: /* OEMCrypto_IsAntiRollbackHwPresent */ + { + OPK_Unpack_IsAntiRollbackHwPresent_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + bool result; + OPK_Init_bool((bool*)&result); + LOGD("IsAntiRollbackHwPresent"); + result = OEMCrypto_IsAntiRollbackHwPresent(); + *response = OPK_Pack_IsAntiRollbackHwPresent_Response(result); + break; + } + case 38: /* OEMCrypto_GetNumberOfOpenSessions */ + { + size_t* count; + OPK_InitPointer((uint8_t**)&count); + OPK_Unpack_GetNumberOfOpenSessions_Request(request, &count); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GetNumberOfOpenSessions"); + result = OEMCrypto_GetNumberOfOpenSessions(count); + *response = OPK_Pack_GetNumberOfOpenSessions_Response(result, count); + break; + } + case 37: /* OEMCrypto_GetMaxNumberOfSessions */ + { + size_t* max; + OPK_InitPointer((uint8_t**)&max); + OPK_Unpack_GetMaxNumberOfSessions_Request(request, &max); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GetMaxNumberOfSessions"); + result = OEMCrypto_GetMaxNumberOfSessions(max); + *response = OPK_Pack_GetMaxNumberOfSessions_Response(result, max); + break; + } + case 52: /* OEMCrypto_SupportedCertificates */ + { + OPK_Unpack_SupportedCertificates_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + uint32_t result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("SupportedCertificates"); + result = OEMCrypto_SupportedCertificates(); + *response = OPK_Pack_SupportedCertificates_Response(result); + break; + } + case 54: /* OEMCrypto_GetCurrentSRMVersion */ + { + uint16_t* version; + OPK_InitPointer((uint8_t**)&version); + OPK_Unpack_GetCurrentSRMVersion_Request(request, &version); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GetCurrentSRMVersion"); + result = OEMCrypto_GetCurrentSRMVersion(version); + *response = OPK_Pack_GetCurrentSRMVersion_Response(result, version); + break; + } + case 71: /* OEMCrypto_GetAnalogOutputFlags */ + { + OPK_Unpack_GetAnalogOutputFlags_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + uint32_t result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GetAnalogOutputFlags"); + result = OEMCrypto_GetAnalogOutputFlags(); + *response = OPK_Pack_GetAnalogOutputFlags_Response(result); + break; + } + case 85: /* OEMCrypto_ResourceRatingTier */ + { + OPK_Unpack_ResourceRatingTier_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + uint32_t result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("ResourceRatingTier"); + result = OEMCrypto_ResourceRatingTier(); + *response = OPK_Pack_ResourceRatingTier_Response(result); + break; + } + case 122: /* OEMCrypto_ProductionReady */ + { + OPK_Unpack_ProductionReady_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("ProductionReady"); + result = OEMCrypto_ProductionReady(); + *response = OPK_Pack_ProductionReady_Response(result); + break; + } + case 129: /* OEMCrypto_GetWatermarkingSupport */ + { + OPK_Unpack_GetWatermarkingSupport_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCrypto_WatermarkingSupport result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GetWatermarkingSupport"); + result = OEMCrypto_GetWatermarkingSupport(); + *response = OPK_Pack_GetWatermarkingSupport_Response(result); + break; + } + case 102: /* OEMCrypto_LoadProvisioning */ + { + size_t message_length; + OPK_Init_size_t((size_t*)&message_length); + size_t signature_length; + OPK_Init_size_t((size_t*)&signature_length); + size_t* wrapped_private_key_length = + (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(wrapped_private_key_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint8_t* message; + OPK_InitPointer((uint8_t**)&message); + size_t core_message_length; + OPK_Init_size_t((size_t*)&core_message_length); + uint8_t* signature; + OPK_InitPointer((uint8_t**)&signature); + uint8_t* wrapped_private_key; + OPK_InitPointer((uint8_t**)&wrapped_private_key); + OPK_Unpack_LoadProvisioning_Request( + request, &session, &message, &message_length, &core_message_length, + &signature, &signature_length, &wrapped_private_key, + &wrapped_private_key_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("LoadProvisioning"); + result = OEMCrypto_LoadProvisioning( + session, message, message_length, core_message_length, signature, + signature_length, wrapped_private_key, wrapped_private_key_length); + *response = OPK_Pack_LoadProvisioning_Response( + result, wrapped_private_key, wrapped_private_key_length); + break; + } + case 107: /* OEMCrypto_LoadDRMPrivateKey */ + { + size_t wrapped_private_key_length; + OPK_Init_size_t((size_t*)&wrapped_private_key_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + OEMCrypto_PrivateKeyType key_type; + OPK_Init_uint32_t((uint32_t*)&key_type); + uint8_t* wrapped_private_key; + OPK_InitPointer((uint8_t**)&wrapped_private_key); + OPK_Unpack_LoadDRMPrivateKey_Request(request, &session, &key_type, + &wrapped_private_key, + &wrapped_private_key_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("LoadDRMPrivateKey"); + result = OEMCrypto_LoadDRMPrivateKey( + session, key_type, wrapped_private_key, wrapped_private_key_length); + *response = OPK_Pack_LoadDRMPrivateKey_Response(result); + break; + } + case 45: /* OEMCrypto_LoadTestRSAKey */ + { + OPK_Unpack_LoadTestRSAKey_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("LoadTestRSAKey"); + result = OEMCrypto_LoadTestRSAKey(); + *response = OPK_Pack_LoadTestRSAKey_Response(result); + break; + } + case 36: /* OEMCrypto_GenerateRSASignature */ + { + size_t message_length; + OPK_Init_size_t((size_t*)&message_length); + size_t* signature_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(signature_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint8_t* message; + OPK_InitPointer((uint8_t**)&message); + uint8_t* signature; + OPK_InitPointer((uint8_t**)&signature); + RSA_Padding_Scheme padding_scheme; + OPK_Init_uint8_t((uint8_t*)&padding_scheme); + OPK_Unpack_GenerateRSASignature_Request( + request, &session, &message, &message_length, &signature, + &signature_length, &padding_scheme); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GenerateRSASignature"); + result = OEMCrypto_GenerateRSASignature(session, message, message_length, + signature, signature_length, + padding_scheme); + *response = OPK_Pack_GenerateRSASignature_Response(result, signature, + signature_length); + break; + } + case 98: /* OEMCrypto_PrepAndSignProvisioningRequest */ + { + size_t message_length; + OPK_Init_size_t((size_t*)&message_length); + size_t* signature_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(signature_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint8_t* message; + OPK_InitPointer((uint8_t**)&message); + size_t* core_message_size = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(core_message_size); + uint8_t* signature; + OPK_InitPointer((uint8_t**)&signature); + OPK_Unpack_PrepAndSignProvisioningRequest_Request( + request, &session, &message, &message_length, &core_message_size, + &signature, &signature_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("PrepAndSignProvisioningRequest"); + result = OEMCrypto_PrepAndSignProvisioningRequest( + session, message, message_length, core_message_size, signature, + signature_length); + *response = OPK_Pack_PrepAndSignProvisioningRequest_Response( + result, message, message_length, core_message_size, signature, + signature_length); + break; + } + case 61: /* OEMCrypto_CreateUsageTableHeader */ + { + size_t* header_buffer_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(header_buffer_length); + uint8_t* header_buffer; + OPK_InitPointer((uint8_t**)&header_buffer); + OPK_Unpack_CreateUsageTableHeader_Request(request, &header_buffer, + &header_buffer_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("CreateUsageTableHeader"); + result = + OEMCrypto_CreateUsageTableHeader(header_buffer, header_buffer_length); + *response = OPK_Pack_CreateUsageTableHeader_Response( + result, header_buffer, header_buffer_length); + break; + } + case 62: /* OEMCrypto_LoadUsageTableHeader */ + { + size_t buffer_length; + OPK_Init_size_t((size_t*)&buffer_length); + uint8_t* buffer; + OPK_InitPointer((uint8_t**)&buffer); + OPK_Unpack_LoadUsageTableHeader_Request(request, &buffer, &buffer_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("LoadUsageTableHeader"); + result = OEMCrypto_LoadUsageTableHeader(buffer, buffer_length); + *response = OPK_Pack_LoadUsageTableHeader_Response(result); + break; + } + case 63: /* OEMCrypto_CreateNewUsageEntry */ + { + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint32_t* usage_entry_number; + OPK_InitPointer((uint8_t**)&usage_entry_number); + OPK_Unpack_CreateNewUsageEntry_Request(request, &session, + &usage_entry_number); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("CreateNewUsageEntry"); + result = OEMCrypto_CreateNewUsageEntry(session, usage_entry_number); + *response = + OPK_Pack_CreateNewUsageEntry_Response(result, usage_entry_number); + break; + } + case 127: /* OEMCrypto_ReuseUsageEntry */ + { + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint32_t usage_entry_number; + OPK_Init_uint32_t((uint32_t*)&usage_entry_number); + OPK_Unpack_ReuseUsageEntry_Request(request, &session, + &usage_entry_number); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("ReuseUsageEntry"); + result = OEMCrypto_ReuseUsageEntry(session, usage_entry_number); + *response = OPK_Pack_ReuseUsageEntry_Response(result); + break; + } + case 64: /* OEMCrypto_LoadUsageEntry */ + { + size_t buffer_length; + OPK_Init_size_t((size_t*)&buffer_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint32_t usage_entry_number; + OPK_Init_uint32_t((uint32_t*)&usage_entry_number); + uint8_t* buffer; + OPK_InitPointer((uint8_t**)&buffer); + OPK_Unpack_LoadUsageEntry_Request(request, &session, &usage_entry_number, + &buffer, &buffer_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("LoadUsageEntry"); + result = OEMCrypto_LoadUsageEntry(session, usage_entry_number, buffer, + buffer_length); + *response = OPK_Pack_LoadUsageEntry_Response(result); + break; + } + case 65: /* OEMCrypto_UpdateUsageEntry */ + { + size_t* header_buffer_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(header_buffer_length); + size_t* entry_buffer_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(entry_buffer_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + OEMCrypto_SharedMemory* header_buffer; + OPK_InitPointer((uint8_t**)&header_buffer); + OEMCrypto_SharedMemory* entry_buffer; + OPK_InitPointer((uint8_t**)&entry_buffer); + OPK_Unpack_UpdateUsageEntry_Request(request, &session, &header_buffer, + &header_buffer_length, &entry_buffer, + &entry_buffer_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("UpdateUsageEntry"); + result = OEMCrypto_UpdateUsageEntry(session, header_buffer, + header_buffer_length, entry_buffer, + entry_buffer_length); + *response = OPK_Pack_UpdateUsageEntry_Response( + result, OPK_SharedBuffer_NextOutputBuffer(), header_buffer_length, + OPK_SharedBuffer_NextOutputBuffer(), entry_buffer_length); + break; + } + case 66: /* OEMCrypto_DeactivateUsageEntry */ + { + size_t pst_length; + OPK_Init_size_t((size_t*)&pst_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint8_t* pst; + OPK_InitPointer((uint8_t**)&pst); + OPK_Unpack_DeactivateUsageEntry_Request(request, &session, &pst, + &pst_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("DeactivateUsageEntry"); + result = OEMCrypto_DeactivateUsageEntry(session, pst, pst_length); + *response = OPK_Pack_DeactivateUsageEntry_Response(result); + break; + } + case 32: /* OEMCrypto_ReportUsage */ + { + size_t pst_length; + OPK_Init_size_t((size_t*)&pst_length); + size_t* buffer_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(buffer_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint8_t* pst; + OPK_InitPointer((uint8_t**)&pst); + uint8_t* buffer; + OPK_InitPointer((uint8_t**)&buffer); + OPK_Unpack_ReportUsage_Request(request, &session, &pst, &pst_length, + &buffer, &buffer_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("ReportUsage"); + result = OEMCrypto_ReportUsage(session, pst, pst_length, buffer, + buffer_length); + *response = OPK_Pack_ReportUsage_Response(result, buffer, buffer_length); + break; + } + case 68: /* OEMCrypto_MoveEntry */ + { + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint32_t new_index; + OPK_Init_uint32_t((uint32_t*)&new_index); + OPK_Unpack_MoveEntry_Request(request, &session, &new_index); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("MoveEntry"); + result = OEMCrypto_MoveEntry(session, new_index); + *response = OPK_Pack_MoveEntry_Response(result); + break; + } + case 67: /* OEMCrypto_ShrinkUsageTableHeader */ + { + size_t* header_buffer_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(header_buffer_length); + uint32_t new_entry_count; + OPK_Init_uint32_t((uint32_t*)&new_entry_count); + uint8_t* header_buffer; + OPK_InitPointer((uint8_t**)&header_buffer); + OPK_Unpack_ShrinkUsageTableHeader_Request( + request, &new_entry_count, &header_buffer, &header_buffer_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("ShrinkUsageTableHeader"); + result = OEMCrypto_ShrinkUsageTableHeader(new_entry_count, header_buffer, + header_buffer_length); + *response = OPK_Pack_ShrinkUsageTableHeader_Response( + result, header_buffer, header_buffer_length); + break; + } + case 116: /* OEMCrypto_GetBootCertificateChain */ + { + size_t* bcc_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(bcc_length); + size_t* additional_signature_length = + (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(additional_signature_length); + uint8_t* bcc; + OPK_InitPointer((uint8_t**)&bcc); + uint8_t* additional_signature; + OPK_InitPointer((uint8_t**)&additional_signature); + OPK_Unpack_GetBootCertificateChain_Request(request, &bcc, &bcc_length, + &additional_signature, + &additional_signature_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GetBootCertificateChain"); + result = OEMCrypto_GetBootCertificateChain( + bcc, bcc_length, additional_signature, additional_signature_length); + *response = OPK_Pack_GetBootCertificateChain_Response( + result, bcc, bcc_length, additional_signature, + additional_signature_length); + break; + } + case 117: /* OEMCrypto_GenerateCertificateKeyPair */ + { + size_t* public_key_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(public_key_length); + size_t* public_key_signature_length = + (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(public_key_signature_length); + size_t* wrapped_private_key_length = + (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(wrapped_private_key_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint8_t* public_key; + OPK_InitPointer((uint8_t**)&public_key); + uint8_t* public_key_signature; + OPK_InitPointer((uint8_t**)&public_key_signature); + uint8_t* wrapped_private_key; + OPK_InitPointer((uint8_t**)&wrapped_private_key); + OEMCrypto_PrivateKeyType* key_type; + OPK_InitPointer((uint8_t**)&key_type); + OPK_Unpack_GenerateCertificateKeyPair_Request( + request, &session, &public_key, &public_key_length, + &public_key_signature, &public_key_signature_length, + &wrapped_private_key, &wrapped_private_key_length, &key_type); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GenerateCertificateKeyPair"); + result = OEMCrypto_GenerateCertificateKeyPair( + session, public_key, public_key_length, public_key_signature, + public_key_signature_length, wrapped_private_key, + wrapped_private_key_length, key_type); + *response = OPK_Pack_GenerateCertificateKeyPair_Response( + result, public_key, public_key_length, public_key_signature, + public_key_signature_length, wrapped_private_key, + wrapped_private_key_length, key_type); + break; + } + case 86: /* OEMCrypto_SupportsDecryptHash */ + { + OPK_Unpack_SupportsDecryptHash_Request(request); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + uint32_t result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("SupportsDecryptHash"); + result = OEMCrypto_SupportsDecryptHash(); + *response = OPK_Pack_SupportsDecryptHash_Response(result); + break; + } + case 88: /* OEMCrypto_SetDecryptHash */ + { + size_t hash_length; + OPK_Init_size_t((size_t*)&hash_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint32_t frame_number; + OPK_Init_uint32_t((uint32_t*)&frame_number); + uint8_t* hash; + OPK_InitPointer((uint8_t**)&hash); + OPK_Unpack_SetDecryptHash_Request(request, &session, &frame_number, &hash, + &hash_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("SetDecryptHash"); + result = + OEMCrypto_SetDecryptHash(session, frame_number, hash, hash_length); + *response = OPK_Pack_SetDecryptHash_Response(result); + break; + } + case 89: /* OEMCrypto_GetHashErrorCode */ + { + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint32_t* failed_frame_number; + OPK_InitPointer((uint8_t**)&failed_frame_number); + OPK_Unpack_GetHashErrorCode_Request(request, &session, + &failed_frame_number); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GetHashErrorCode"); + result = OEMCrypto_GetHashErrorCode(session, failed_frame_number); + *response = + OPK_Pack_GetHashErrorCode_Response(result, failed_frame_number); + break; + } + case 109: /* OEMCrypto_AllocateSecureBuffer */ + { + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + size_t buffer_size; + OPK_Init_size_t((size_t*)&buffer_size); + OEMCrypto_DestBufferDesc* output_descriptor; + OPK_InitPointer((uint8_t**)&output_descriptor); + int* secure_fd; + OPK_InitPointer((uint8_t**)&secure_fd); + OPK_Unpack_AllocateSecureBuffer_Request(request, &session, &buffer_size, + &output_descriptor, &secure_fd); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("AllocateSecureBuffer"); + result = OEMCrypto_AllocateSecureBuffer(session, buffer_size, + output_descriptor, secure_fd); + *response = OPK_Pack_AllocateSecureBuffer_Response( + result, output_descriptor, secure_fd); + break; + } + case 110: /* OEMCrypto_FreeSecureBuffer */ + { + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + OEMCrypto_DestBufferDesc* output_descriptor; + OPK_InitPointer((uint8_t**)&output_descriptor); + int secure_fd; + OPK_Init_int((int*)&secure_fd); + OPK_Unpack_FreeSecureBuffer_Request(request, &session, &output_descriptor, + &secure_fd); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("FreeSecureBuffer"); + result = + OEMCrypto_FreeSecureBuffer(session, output_descriptor, secure_fd); + *response = OPK_Pack_FreeSecureBuffer_Response(result, output_descriptor); + break; + } + case 118: /* OEMCrypto_InstallOemPrivateKey */ + { + size_t wrapped_private_key_length; + OPK_Init_size_t((size_t*)&wrapped_private_key_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + OEMCrypto_PrivateKeyType key_type; + OPK_Init_uint32_t((uint32_t*)&key_type); + uint8_t* wrapped_private_key; + OPK_InitPointer((uint8_t**)&wrapped_private_key); + OPK_Unpack_InstallOemPrivateKey_Request(request, &session, &key_type, + &wrapped_private_key, + &wrapped_private_key_length); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("InstallOemPrivateKey"); + result = OEMCrypto_InstallOemPrivateKey( + session, key_type, wrapped_private_key, wrapped_private_key_length); + *response = OPK_Pack_InstallOemPrivateKey_Response(result); + break; + } + case 119: /* OEMCrypto_ReassociateEntitledKeySession */ + { + OEMCrypto_SESSION key_session; + OPK_Init_uint32_t((uint32_t*)&key_session); + OEMCrypto_SESSION oec_session; + OPK_Init_uint32_t((uint32_t*)&oec_session); + OPK_Unpack_ReassociateEntitledKeySession_Request(request, &key_session, + &oec_session); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("ReassociateEntitledKeySession"); + result = + OEMCrypto_ReassociateEntitledKeySession(key_session, oec_session); + *response = OPK_Pack_ReassociateEntitledKeySession_Response(result); + break; + } + case 120: /* OEMCrypto_LoadCasECMKeys */ + { + size_t message_length; + OPK_Init_size_t((size_t*)&message_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint8_t* message; + OPK_InitPointer((uint8_t**)&message); + OEMCrypto_EntitledContentKeyObject* even_key = + (OEMCrypto_EntitledContentKeyObject*)OPK_VarAlloc( + sizeof(OEMCrypto_EntitledContentKeyObject)); + OPK_Init_OEMCrypto_EntitledContentKeyObject( + (OEMCrypto_EntitledContentKeyObject*)even_key); + OEMCrypto_EntitledContentKeyObject* odd_key = + (OEMCrypto_EntitledContentKeyObject*)OPK_VarAlloc( + sizeof(OEMCrypto_EntitledContentKeyObject)); + OPK_Init_OEMCrypto_EntitledContentKeyObject( + (OEMCrypto_EntitledContentKeyObject*)odd_key); + OPK_Unpack_LoadCasECMKeys_Request(request, &session, &message, + &message_length, &even_key, &odd_key); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("LoadCasECMKeys"); + result = OEMCrypto_LoadCasECMKeys(session, message, message_length, + even_key, odd_key); + *response = OPK_Pack_LoadCasECMKeys_Response(result); + break; + } + case 115: /* OEMCrypto_OPK_SerializationVersion */ + { + uint32_t* ree_major = (uint32_t*)OPK_VarAlloc(sizeof(uint32_t)); + OPK_Init_uint32_t(ree_major); + uint32_t* ree_minor = (uint32_t*)OPK_VarAlloc(sizeof(uint32_t)); + OPK_Init_uint32_t(ree_minor); + uint32_t* tee_major; + OPK_InitPointer((uint8_t**)&tee_major); + uint32_t* tee_minor; + OPK_InitPointer((uint8_t**)&tee_minor); + OPK_Unpack_OPK_SerializationVersion_Request( + request, &ree_major, &ree_minor, &tee_major, &tee_minor); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("OPK_SerializationVersion"); + result = OEMCrypto_OPK_SerializationVersion(ree_major, ree_minor, + tee_major, tee_minor); + *response = OPK_Pack_OPK_SerializationVersion_Response( + result, ree_major, ree_minor, tee_major, tee_minor); + break; + } + case 113: /* OEMCrypto_GenerateOTARequest */ + { + size_t* buffer_length = (size_t*)OPK_VarAlloc(sizeof(size_t)); + OPK_Init_size_t(buffer_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint8_t* buffer; + OPK_InitPointer((uint8_t**)&buffer); + uint32_t use_test_key; + OPK_Init_uint32_t((uint32_t*)&use_test_key); + OPK_Unpack_GenerateOTARequest_Request(request, &session, &buffer, + &buffer_length, &use_test_key); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("GenerateOTARequest"); + result = OEMCrypto_GenerateOTARequest(session, buffer, buffer_length, + use_test_key); + *response = + OPK_Pack_GenerateOTARequest_Response(result, buffer, buffer_length); + break; + } + case 114: /* OEMCrypto_ProcessOTAKeybox */ + { + size_t buffer_length; + OPK_Init_size_t((size_t*)&buffer_length); + OEMCrypto_SESSION session; + OPK_Init_uint32_t((uint32_t*)&session); + uint8_t* buffer; + OPK_InitPointer((uint8_t**)&buffer); + uint32_t use_test_key; + OPK_Init_uint32_t((uint32_t*)&use_test_key); + OPK_Unpack_ProcessOTAKeybox_Request(request, &session, &buffer, + &buffer_length, &use_test_key); + if (!ODK_Message_IsValid(request)) goto handle_invalid_request; + OEMCryptoResult result; + OPK_Init_uint32_t((uint32_t*)&result); + LOGD("ProcessOTAKeybox"); + result = OEMCrypto_ProcessOTAKeybox(session, buffer, buffer_length, + use_test_key); + *response = OPK_Pack_ProcessOTAKeybox_Response(result); + break; + } + default: + return MESSAGE_STATUS_API_VALUE_ERROR; + } + return ODK_Message_GetStatus(response); + +handle_invalid_request: + LOGE("invalid request"); + *response = CreateEmptyMessage(); + return MESSAGE_STATUS_OK; +} diff --git a/oemcrypto/opk/serialization/tee/GEN_tee_serializer.c b/oemcrypto/opk/serialization/tee/GEN_tee_serializer.c new file mode 100644 index 0000000..d12769e --- /dev/null +++ b/oemcrypto/opk/serialization/tee/GEN_tee_serializer.c @@ -0,0 +1,2596 @@ +/* + * Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine License + * Agreement. + */ + +/* + * This code is auto-generated, do not edit + */ +#include "GEN_tee_serializer.h" + +#include +#include + +#include "GEN_common_serializer.h" +#include "bump_allocator.h" +#include "common_special_cases.h" +#include "log_macros.h" +#include "marshaller_base.h" +#include "odk_overflow.h" +#include "opk_serialization_base.h" +#include "shared_buffer_allocator.h" +#include "tee_special_cases.h" +#include "tos_transport_interface.h" + +void OPK_Unpack_SetSandbox_Request(ODK_Message* msg, uint8_t** sandbox_id, + size_t* sandbox_id_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 84) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, sandbox_id_length); + OPK_UnpackInPlace(msg, (uint8_t**)sandbox_id, + OPK_FromSizeTPtr(sandbox_id_length)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_SetSandbox_Response(OEMCryptoResult result) { + uint32_t api_value = 84; /* from _oecc84 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Initialize_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 1) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_Initialize_Response(OEMCryptoResult result) { + uint32_t api_value = 1; /* from _oecc1 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Terminate_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 2) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_Terminate_Response(OEMCryptoResult result) { + uint32_t api_value = 2; /* from _oecc2 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Idle_Request(ODK_Message* msg, OEMCrypto_IdleState* state, + uint32_t* os_specific_code) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 123) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_uint32_t(msg, state); + if (!Is_Valid_OEMCrypto_IdleState(*state)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_Unpack_uint32_t(msg, os_specific_code); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_Idle_Response(OEMCryptoResult result) { + uint32_t api_value = 123; /* from _oecc123 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Wake_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 124) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_Wake_Response(OEMCryptoResult result) { + uint32_t api_value = 124; /* from _oecc124 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_OpenSession_Request(ODK_Message* msg, + OEMCrypto_SESSION** session) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 9) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + *session = (uint32_t*)OPK_UnpackAlloc(msg, sizeof(OEMCrypto_SESSION)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_OpenSession_Response(OEMCryptoResult result, + const OEMCrypto_SESSION* session) { + uint32_t api_value = 9; /* from _oecc9 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_uint32_t(&msg, session); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_CloseSession_Request(ODK_Message* msg, + OEMCrypto_SESSION* session) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 10) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_CloseSession_Response(OEMCryptoResult result) { + uint32_t api_value = 10; /* from _oecc10 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_CreateEntitledKeySession_Request( + ODK_Message* msg, OEMCrypto_SESSION* oec_session, + OEMCrypto_SESSION** key_session) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 111) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_uint32_t(msg, oec_session); + *key_session = (uint32_t*)OPK_UnpackAlloc(msg, sizeof(OEMCrypto_SESSION)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_CreateEntitledKeySession_Response( + OEMCryptoResult result, const OEMCrypto_SESSION* key_session) { + uint32_t api_value = 111; /* from _oecc111 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_uint32_t(&msg, key_session); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_RemoveEntitledKeySession_Request( + ODK_Message* msg, OEMCrypto_SESSION* key_session) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 112) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_uint32_t(msg, key_session); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_RemoveEntitledKeySession_Response(OEMCryptoResult result) { + uint32_t api_value = 112; /* from _oecc112 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GenerateDerivedKeys_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, + OEMCrypto_SharedMemory** mac_key_context, size_t* mac_key_context_length, + OEMCrypto_SharedMemory** enc_key_context, size_t* enc_key_context_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 95) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, mac_key_context_length); + OPK_Unpack_size_t(msg, enc_key_context_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackSharedBuffer(msg, mac_key_context, + OPK_FromSizeTPtr(mac_key_context_length), + /* map */ true, /* is_output */ false); + OPK_UnpackSharedBuffer(msg, enc_key_context, + OPK_FromSizeTPtr(enc_key_context_length), + /* map */ true, /* is_output */ false); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GenerateDerivedKeys_Response(OEMCryptoResult result) { + uint32_t api_value = 95; /* from _oecc95 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_DeriveKeysFromSessionKey_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, uint8_t** derivation_key, + size_t* derivation_key_length, OEMCrypto_SharedMemory** mac_key_context, + size_t* mac_key_context_length, OEMCrypto_SharedMemory** enc_key_context, + size_t* enc_key_context_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 21) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, derivation_key_length); + OPK_Unpack_size_t(msg, mac_key_context_length); + OPK_Unpack_size_t(msg, enc_key_context_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackInPlace(msg, (uint8_t**)derivation_key, + OPK_FromSizeTPtr(derivation_key_length)); + OPK_UnpackSharedBuffer(msg, mac_key_context, + OPK_FromSizeTPtr(mac_key_context_length), + /* map */ true, /* is_output */ false); + OPK_UnpackSharedBuffer(msg, enc_key_context, + OPK_FromSizeTPtr(enc_key_context_length), + /* map */ true, /* is_output */ false); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_DeriveKeysFromSessionKey_Response(OEMCryptoResult result) { + uint32_t api_value = 21; /* from _oecc21 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GenerateNonce_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint32_t** nonce) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 14) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_uint32_t(msg, session); + *nonce = (uint32_t*)OPK_UnpackAlloc(msg, sizeof(uint32_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GenerateNonce_Response(OEMCryptoResult result, + const uint32_t* nonce) { + uint32_t api_value = 14; /* from _oecc14 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_uint32_t(&msg, nonce); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_PrepAndSignLicenseRequest_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, uint8_t** message, + size_t* message_length, size_t** core_message_size, uint8_t** signature, + size_t** signature_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 96) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, message_length); + OPK_UnpackNullable_size_t(msg, signature_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackInPlace(msg, (uint8_t**)message, OPK_FromSizeTPtr(message_length)); + OPK_UnpackNullable_size_t(msg, core_message_size); + *signature = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(signature_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_PrepAndSignLicenseRequest_Response( + OEMCryptoResult result, const uint8_t* message, size_t message_length, + const size_t* core_message_size, const uint8_t* signature, + const size_t* signature_length) { + uint32_t api_value = 96; /* from _oecc96 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_size_t(&msg, &message_length); + OPK_PackNullable_size_t(&msg, signature_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)message, + OPK_ToLengthType(message_length)); + } + OPK_PackNullable_size_t(&msg, core_message_size); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)signature, + OPK_FromSizeTPtr(signature_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_PrepAndSignRenewalRequest_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, uint8_t** message, + size_t* message_length, size_t** core_message_size, uint8_t** signature, + size_t** signature_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 97) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, message_length); + OPK_UnpackNullable_size_t(msg, signature_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackInPlace(msg, (uint8_t**)message, OPK_FromSizeTPtr(message_length)); + OPK_UnpackNullable_size_t(msg, core_message_size); + *signature = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(signature_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_PrepAndSignRenewalRequest_Response( + OEMCryptoResult result, const uint8_t* message, size_t message_length, + const size_t* core_message_size, const uint8_t* signature, + const size_t* signature_length) { + uint32_t api_value = 97; /* from _oecc97 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_size_t(&msg, &message_length); + OPK_PackNullable_size_t(&msg, signature_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)message, + OPK_ToLengthType(message_length)); + } + OPK_PackNullable_size_t(&msg, core_message_size); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)signature, + OPK_FromSizeTPtr(signature_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadKeys_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, uint8_t** message, + size_t* message_length, uint8_t** signature, size_t* signature_length, + OEMCrypto_Substring* enc_mac_keys_iv, OEMCrypto_Substring* enc_mac_keys, + size_t* key_array_length, OEMCrypto_KeyObject** key_array, + OEMCrypto_Substring* pst, OEMCrypto_Substring* srm_restriction_data, + OEMCrypto_LicenseType* license_type) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 83) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, message_length); + OPK_Unpack_size_t(msg, signature_length); + OPK_Unpack_size_t(msg, key_array_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackInPlace(msg, (uint8_t**)message, OPK_FromSizeTPtr(message_length)); + OPK_UnpackInPlace(msg, (uint8_t**)signature, + OPK_FromSizeTPtr(signature_length)); + OPK_Unpack_OEMCrypto_Substring(msg, enc_mac_keys_iv); + OPK_Unpack_OEMCrypto_Substring(msg, enc_mac_keys); + /* unpack object array with unpacker function OPK_Unpack_OEMCrypto_KeyObject + */ + ODK_Message* odk_message = msg; + void** address = (void**)key_array; + LengthType count = OPK_FromSizeTPtr(key_array_length); + size_t size = sizeof(OEMCrypto_KeyObject); + if (address) { + *address = NULL; + } + if (!OPK_UnpackIsNull(odk_message)) { + if (address && !OPK_LengthIsNull(count)) { + size_t bytes_to_unpack = 0; + if (odk_mul_overflow_ux(OPK_ToSizeT(count), size, &bytes_to_unpack)) { + ODK_MESSAGE_SETSTATUS(odk_message, MESSAGE_STATUS_PARSE_ERROR); + } else { + *address = OPK_BumpAllocate(bytes_to_unpack); + if (!*address) { + ODK_MESSAGE_SETSTATUS(odk_message, MESSAGE_STATUS_OUT_OF_MEMORY); + } else { + for (size_t i = 0; i < OPK_ToSizeT(count); i++) { + OPK_Unpack_OEMCrypto_KeyObject( + odk_message, (OEMCrypto_KeyObject*)((*address) + size * i)); + } + } + } + } + } + OPK_Unpack_OEMCrypto_Substring(msg, pst); + OPK_Unpack_OEMCrypto_Substring(msg, srm_restriction_data); + OPK_Unpack_uint32_t(msg, license_type); + if (!Is_Valid_OEMCrypto_LicenseType(*license_type)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_LoadKeys_Response(OEMCryptoResult result) { + uint32_t api_value = 83; /* from _oecc83 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadLicense_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint8_t** message, size_t* message_length, + size_t* core_message_length, + uint8_t** signature, + size_t* signature_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 99) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, message_length); + OPK_Unpack_size_t(msg, signature_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackInPlace(msg, (uint8_t**)message, OPK_FromSizeTPtr(message_length)); + OPK_Unpack_size_t(msg, core_message_length); + OPK_UnpackInPlace(msg, (uint8_t**)signature, + OPK_FromSizeTPtr(signature_length)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_LoadLicense_Response(OEMCryptoResult result) { + uint32_t api_value = 99; /* from _oecc99 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadEntitledContentKeys_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, uint8_t** message, + size_t* message_length, size_t* key_array_length, + OEMCrypto_EntitledContentKeyObject** key_array) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 121) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, message_length); + OPK_Unpack_size_t(msg, key_array_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackInPlace(msg, (uint8_t**)message, OPK_FromSizeTPtr(message_length)); + /* unpack object array with unpacker function + * OPK_Unpack_OEMCrypto_EntitledContentKeyObject */ + ODK_Message* odk_message = msg; + void** address = (void**)key_array; + LengthType count = OPK_FromSizeTPtr(key_array_length); + size_t size = sizeof(OEMCrypto_EntitledContentKeyObject); + if (address) { + *address = NULL; + } + if (!OPK_UnpackIsNull(odk_message)) { + if (address && !OPK_LengthIsNull(count)) { + size_t bytes_to_unpack = 0; + if (odk_mul_overflow_ux(OPK_ToSizeT(count), size, &bytes_to_unpack)) { + ODK_MESSAGE_SETSTATUS(odk_message, MESSAGE_STATUS_PARSE_ERROR); + } else { + *address = OPK_BumpAllocate(bytes_to_unpack); + if (!*address) { + ODK_MESSAGE_SETSTATUS(odk_message, MESSAGE_STATUS_OUT_OF_MEMORY); + } else { + for (size_t i = 0; i < OPK_ToSizeT(count); i++) { + OPK_Unpack_OEMCrypto_EntitledContentKeyObject( + odk_message, + (OEMCrypto_EntitledContentKeyObject*)((*address) + size * i)); + } + } + } + } + } + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_LoadEntitledContentKeys_Response(OEMCryptoResult result) { + uint32_t api_value = 121; /* from _oecc121 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_RefreshKeys_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint8_t** message, size_t* message_length, + uint8_t** signature, + size_t* signature_length, size_t* num_keys, + OEMCrypto_KeyRefreshObject** key_array) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 91) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, message_length); + OPK_Unpack_size_t(msg, signature_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackInPlace(msg, (uint8_t**)message, OPK_FromSizeTPtr(message_length)); + OPK_UnpackInPlace(msg, (uint8_t**)signature, + OPK_FromSizeTPtr(signature_length)); + OPK_Unpack_size_t(msg, num_keys); + OPK_UnpackNullable_OEMCrypto_KeyRefreshObject(msg, key_array); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_RefreshKeys_Response(OEMCryptoResult result) { + uint32_t api_value = 91; /* from _oecc91 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadRenewal_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint8_t** message, size_t* message_length, + size_t* core_message_length, + uint8_t** signature, + size_t* signature_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 101) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, message_length); + OPK_Unpack_size_t(msg, signature_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackInPlace(msg, (uint8_t**)message, OPK_FromSizeTPtr(message_length)); + OPK_Unpack_size_t(msg, core_message_length); + OPK_UnpackInPlace(msg, (uint8_t**)signature, + OPK_FromSizeTPtr(signature_length)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_LoadRenewal_Response(OEMCryptoResult result) { + uint32_t api_value = 101; /* from _oecc101 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_QueryKeyControl_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint8_t** content_key_id, + size_t* content_key_id_length, + uint8_t** key_control_block, + size_t** key_control_block_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 41) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, content_key_id_length); + OPK_UnpackNullable_size_t(msg, key_control_block_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackInPlace(msg, (uint8_t**)content_key_id, + OPK_FromSizeTPtr(content_key_id_length)); + *key_control_block = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(key_control_block_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_QueryKeyControl_Response( + OEMCryptoResult result, const uint8_t* key_control_block, + const size_t* key_control_block_length) { + uint32_t api_value = 41; /* from _oecc41 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, key_control_block_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)key_control_block, + OPK_FromSizeTPtr(key_control_block_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_SelectKey_Request(ODK_Message* msg, OEMCrypto_SESSION* session, + uint8_t** content_key_id, + size_t* content_key_id_length, + OEMCryptoCipherMode* cipher_mode) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 81) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, content_key_id_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackInPlace(msg, (uint8_t**)content_key_id, + OPK_FromSizeTPtr(content_key_id_length)); + OPK_Unpack_uint32_t(msg, cipher_mode); + if (!Is_Valid_OEMCryptoCipherMode(*cipher_mode)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_SelectKey_Response(OEMCryptoResult result) { + uint32_t api_value = 81; /* from _oecc81 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_DecryptCENC_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, + OEMCrypto_SampleDescription** samples, size_t* samples_length, + OEMCrypto_CENCEncryptPatternDesc** pattern) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 105) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, samples_length); + OPK_Unpack_uint32_t(msg, session); + /* unpack object array with unpacker function + * OPK_Unpack_OEMCrypto_SampleDescription */ + ODK_Message* odk_message = msg; + void** address = (void**)samples; + LengthType count = OPK_FromSizeTPtr(samples_length); + size_t size = sizeof(OEMCrypto_SampleDescription); + if (address) { + *address = NULL; + } + if (!OPK_UnpackIsNull(odk_message)) { + if (address && !OPK_LengthIsNull(count)) { + size_t bytes_to_unpack = 0; + if (odk_mul_overflow_ux(OPK_ToSizeT(count), size, &bytes_to_unpack)) { + ODK_MESSAGE_SETSTATUS(odk_message, MESSAGE_STATUS_PARSE_ERROR); + } else { + *address = OPK_BumpAllocate(bytes_to_unpack); + if (!*address) { + ODK_MESSAGE_SETSTATUS(odk_message, MESSAGE_STATUS_OUT_OF_MEMORY); + } else { + for (size_t i = 0; i < OPK_ToSizeT(count); i++) { + OPK_Unpack_OEMCrypto_SampleDescription( + odk_message, + (OEMCrypto_SampleDescription*)((*address) + size * i)); + } + } + } + } + } + OPK_UnpackNullable_OEMCrypto_CENCEncryptPatternDesc(msg, pattern); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_DecryptCENC_Response(OEMCryptoResult result) { + uint32_t api_value = 105; /* from _oecc105 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_CopyBuffer_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, + OEMCrypto_SharedMemory** data_addr, size_t* data_addr_length, + OEMCrypto_DestBufferDesc** out_buffer_descriptor, + uint8_t* subsample_flags) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 93) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, data_addr_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackSharedBuffer(msg, data_addr, OPK_FromSizeTPtr(data_addr_length), + /* map */ true, /* is_output */ false); + OPK_UnpackNullable_OEMCrypto_DestBufferDesc(msg, out_buffer_descriptor); + OPK_Unpack_uint8_t(msg, subsample_flags); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_CopyBuffer_Response(OEMCryptoResult result) { + uint32_t api_value = 93; /* from _oecc93 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Generic_Encrypt_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + OEMCrypto_SharedMemory** in_buffer, + size_t* in_buffer_length, uint8_t* iv, + OEMCrypto_Algorithm* algorithm, + OEMCrypto_SharedMemory** out_buffer) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 24) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, in_buffer_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackSharedBuffer(msg, in_buffer, OPK_FromSizeTPtr(in_buffer_length), + /* map */ true, /* is_output */ false); + OPK_UnpackArray(msg, &iv[0], 16); + OPK_Unpack_uint32_t(msg, algorithm); + if (!Is_Valid_OEMCrypto_Algorithm(*algorithm)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackSharedBuffer(msg, out_buffer, OPK_FromSizeTPtr(in_buffer_length), + /* map */ true, /* is_output */ true); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_Generic_Encrypt_Response( + OEMCryptoResult result, size_t in_buffer_length, + const OEMCrypto_SharedMemory* out_buffer) { + uint32_t api_value = 24; /* from _oecc24 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_size_t(&msg, &in_buffer_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackSharedBuffer(&msg, out_buffer, OPK_ToLengthType(in_buffer_length), + /* map */ false, /* copy_in */ false, + /* is_output */ true); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Generic_Decrypt_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + OEMCrypto_SharedMemory** in_buffer, + size_t* in_buffer_length, uint8_t* iv, + OEMCrypto_Algorithm* algorithm, + OEMCrypto_SharedMemory** out_buffer) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 25) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, in_buffer_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackSharedBuffer(msg, in_buffer, OPK_FromSizeTPtr(in_buffer_length), + /* map */ true, /* is_output */ false); + OPK_UnpackArray(msg, &iv[0], 16); + OPK_Unpack_uint32_t(msg, algorithm); + if (!Is_Valid_OEMCrypto_Algorithm(*algorithm)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackSharedBuffer(msg, out_buffer, OPK_FromSizeTPtr(in_buffer_length), + /* map */ true, /* is_output */ true); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_Generic_Decrypt_Response( + OEMCryptoResult result, size_t in_buffer_length, + const OEMCrypto_SharedMemory* out_buffer) { + uint32_t api_value = 25; /* from _oecc25 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_size_t(&msg, &in_buffer_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackSharedBuffer(&msg, out_buffer, OPK_ToLengthType(in_buffer_length), + /* map */ false, /* copy_in */ false, + /* is_output */ true); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Generic_Sign_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + OEMCrypto_SharedMemory** buffer, + size_t* buffer_length, + OEMCrypto_Algorithm* algorithm, + OEMCrypto_SharedMemory** signature, + size_t** signature_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 26) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, buffer_length); + OPK_UnpackNullable_size_t(msg, signature_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackSharedBuffer(msg, buffer, OPK_FromSizeTPtr(buffer_length), + /* map */ true, /* is_output */ false); + OPK_Unpack_uint32_t(msg, algorithm); + if (!Is_Valid_OEMCrypto_Algorithm(*algorithm)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackSharedBuffer(msg, signature, OPK_FromSizeTPtrPtr(signature_length), + /* map */ true, /* is_output */ true); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_Generic_Sign_Response( + OEMCryptoResult result, const OEMCrypto_SharedMemory* signature, + const size_t* signature_length) { + uint32_t api_value = 26; /* from _oecc26 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, signature_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackSharedBuffer(&msg, signature, OPK_FromSizeTPtr(signature_length), + /* map */ false, /* copy_in */ false, + /* is_output */ true); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Generic_Verify_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + OEMCrypto_SharedMemory** buffer, + size_t* buffer_length, + OEMCrypto_Algorithm* algorithm, + OEMCrypto_SharedMemory** signature, + size_t* signature_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 27) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, buffer_length); + OPK_Unpack_size_t(msg, signature_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackSharedBuffer(msg, buffer, OPK_FromSizeTPtr(buffer_length), + /* map */ true, /* is_output */ false); + OPK_Unpack_uint32_t(msg, algorithm); + if (!Is_Valid_OEMCrypto_Algorithm(*algorithm)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackSharedBuffer(msg, signature, OPK_FromSizeTPtr(signature_length), + /* map */ true, /* is_output */ false); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_Generic_Verify_Response(OEMCryptoResult result) { + uint32_t api_value = 27; /* from _oecc27 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_WrapKeyboxOrOEMCert_Request( + ODK_Message* msg, uint8_t** keybox_or_cert, size_t* keybox_or_cert_length, + uint8_t** wrapped_keybox_or_cert, size_t** wrapped_keybox_or_cert_length, + uint8_t** transport_key, size_t* transport_key_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 8) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, keybox_or_cert_length); + OPK_UnpackNullable_size_t(msg, wrapped_keybox_or_cert_length); + OPK_Unpack_size_t(msg, transport_key_length); + OPK_UnpackInPlace(msg, (uint8_t**)keybox_or_cert, + OPK_FromSizeTPtr(keybox_or_cert_length)); + *wrapped_keybox_or_cert = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(wrapped_keybox_or_cert_length), sizeof(uint8_t)); + OPK_UnpackInPlace(msg, (uint8_t**)transport_key, + OPK_FromSizeTPtr(transport_key_length)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_WrapKeyboxOrOEMCert_Response( + OEMCryptoResult result, const uint8_t* wrapped_keybox_or_cert, + const size_t* wrapped_keybox_or_cert_length) { + uint32_t api_value = 8; /* from _oecc8 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, wrapped_keybox_or_cert_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)wrapped_keybox_or_cert, + OPK_FromSizeTPtr(wrapped_keybox_or_cert_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_InstallKeyboxOrOEMCert_Request(ODK_Message* msg, + uint8_t** keybox_or_cert, + size_t* keybox_or_cert_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 3) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, keybox_or_cert_length); + OPK_UnpackInPlace(msg, (uint8_t**)keybox_or_cert, + OPK_FromSizeTPtr(keybox_or_cert_length)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_InstallKeyboxOrOEMCert_Response(OEMCryptoResult result) { + uint32_t api_value = 3; /* from _oecc3 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetProvisioningMethod_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 49) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GetProvisioningMethod_Response( + OEMCrypto_ProvisioningMethod result) { + uint32_t api_value = 49; /* from _oecc49 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_IsKeyboxOrOEMCertValid_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 5) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_IsKeyboxOrOEMCertValid_Response(OEMCryptoResult result) { + uint32_t api_value = 5; /* from _oecc5 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetDeviceID_Request(ODK_Message* msg, uint8_t** device_id, + size_t** device_id_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 7) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackNullable_size_t(msg, device_id_length); + *device_id = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(device_id_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GetDeviceID_Response(OEMCryptoResult result, + const uint8_t* device_id, + const size_t* device_id_length) { + uint32_t api_value = 7; /* from _oecc7 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, device_id_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)device_id, + OPK_FromSizeTPtr(device_id_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetKeyData_Request(ODK_Message* msg, uint8_t** key_data, + size_t** key_data_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 4) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackNullable_size_t(msg, key_data_length); + *key_data = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(key_data_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GetKeyData_Response(OEMCryptoResult result, + const uint8_t* key_data, + const size_t* key_data_length) { + uint32_t api_value = 4; /* from _oecc4 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, key_data_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)key_data, + OPK_FromSizeTPtr(key_data_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadTestKeybox_Request(ODK_Message* msg, uint8_t** buffer, + size_t* buffer_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 78) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, buffer_length); + OPK_UnpackInPlace(msg, (uint8_t**)buffer, OPK_FromSizeTPtr(buffer_length)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_LoadTestKeybox_Response(OEMCryptoResult result) { + uint32_t api_value = 78; /* from _oecc78 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadOEMPrivateKey_Request(ODK_Message* msg, + OEMCrypto_SESSION* session) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 103) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_LoadOEMPrivateKey_Response(OEMCryptoResult result) { + uint32_t api_value = 103; /* from _oecc103 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetOEMPublicCertificate_Request(ODK_Message* msg, + uint8_t** public_cert, + size_t** public_cert_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 104) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackNullable_size_t(msg, public_cert_length); + *public_cert = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(public_cert_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GetOEMPublicCertificate_Response( + OEMCryptoResult result, const uint8_t* public_cert, + const size_t* public_cert_length) { + uint32_t api_value = 104; /* from _oecc104 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, public_cert_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)public_cert, + OPK_FromSizeTPtr(public_cert_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetRandom_Request(ODK_Message* msg, uint8_t** random_data, + size_t* random_data_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 6) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, random_data_length); + *random_data = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtr(random_data_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GetRandom_Response(OEMCryptoResult result, + const uint8_t* random_data, + size_t random_data_length) { + uint32_t api_value = 6; /* from _oecc6 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_size_t(&msg, &random_data_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)random_data, + OPK_ToLengthType(random_data_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_APIVersion_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 22) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_APIVersion_Response(uint32_t result) { + uint32_t api_value = 22; /* from _oecc22 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_MinorAPIVersion_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 108) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_MinorAPIVersion_Response(uint32_t result) { + uint32_t api_value = 108; /* from _oecc108 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_BuildInformation_Request(ODK_Message* msg, char** buffer, + size_t** buffer_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 125) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackNullable_size_t(msg, buffer_length); + *buffer = (char*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(buffer_length), sizeof(char)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_BuildInformation_Response(OEMCryptoResult result, + const char* buffer, + const size_t* buffer_length) { + uint32_t api_value = 125; /* from _oecc125 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, buffer_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)buffer, + OPK_FromSizeTPtr(buffer_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_Security_Patch_Level_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 46) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_Security_Patch_Level_Response(uint8_t result) { + uint32_t api_value = 46; /* from _oecc46 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint8_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_SecurityLevel_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 126) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_SecurityLevel_Response(OEMCrypto_Security_Level result) { + uint32_t api_value = 126; /* from _oecc126 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetHDCPCapability_Request(ODK_Message* msg, + OEMCrypto_HDCP_Capability** current, + OEMCrypto_HDCP_Capability** maximum) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 44) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + *current = (uint32_t*)OPK_UnpackAlloc(msg, sizeof(OEMCrypto_HDCP_Capability)); + *maximum = (uint32_t*)OPK_UnpackAlloc(msg, sizeof(OEMCrypto_HDCP_Capability)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GetHDCPCapability_Response( + OEMCryptoResult result, const OEMCrypto_HDCP_Capability* current, + const OEMCrypto_HDCP_Capability* maximum) { + uint32_t api_value = 44; /* from _oecc44 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_uint32_t(&msg, current); + OPK_PackNullable_uint32_t(&msg, maximum); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetDTCP2Capability_Request( + ODK_Message* msg, OEMCrypto_DTCP2_Capability** capability) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 128) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + *capability = + (uint32_t*)OPK_UnpackAlloc(msg, sizeof(OEMCrypto_DTCP2_Capability)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GetDTCP2Capability_Response( + OEMCryptoResult result, const OEMCrypto_DTCP2_Capability* capability) { + uint32_t api_value = 128; /* from _oecc128 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_uint32_t(&msg, capability); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_SupportsUsageTable_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 29) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_SupportsUsageTable_Response(bool result) { + uint32_t api_value = 29; /* from _oecc29 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_bool(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_MaximumUsageTableHeaderSize_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 94) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_MaximumUsageTableHeaderSize_Response(size_t result) { + uint32_t api_value = 94; /* from _oecc94 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_size_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_IsAntiRollbackHwPresent_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 39) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_IsAntiRollbackHwPresent_Response(bool result) { + uint32_t api_value = 39; /* from _oecc39 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_bool(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetNumberOfOpenSessions_Request(ODK_Message* msg, + size_t** count) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 38) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + *count = (size_t*)OPK_UnpackAlloc(msg, sizeof(size_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GetNumberOfOpenSessions_Response(OEMCryptoResult result, + const size_t* count) { + uint32_t api_value = 38; /* from _oecc38 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_size_t(&msg, count); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetMaxNumberOfSessions_Request(ODK_Message* msg, size_t** max) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 37) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + *max = (size_t*)OPK_UnpackAlloc(msg, sizeof(size_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GetMaxNumberOfSessions_Response(OEMCryptoResult result, + const size_t* max) { + uint32_t api_value = 37; /* from _oecc37 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_size_t(&msg, max); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_SupportedCertificates_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 52) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_SupportedCertificates_Response(uint32_t result) { + uint32_t api_value = 52; /* from _oecc52 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetCurrentSRMVersion_Request(ODK_Message* msg, + uint16_t** version) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 54) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + *version = (uint16_t*)OPK_UnpackAlloc(msg, sizeof(uint16_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GetCurrentSRMVersion_Response(OEMCryptoResult result, + const uint16_t* version) { + uint32_t api_value = 54; /* from _oecc54 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_uint16_t(&msg, version); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetAnalogOutputFlags_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 71) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GetAnalogOutputFlags_Response(uint32_t result) { + uint32_t api_value = 71; /* from _oecc71 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_ResourceRatingTier_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 85) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_ResourceRatingTier_Response(uint32_t result) { + uint32_t api_value = 85; /* from _oecc85 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_ProductionReady_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 122) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_ProductionReady_Response(OEMCryptoResult result) { + uint32_t api_value = 122; /* from _oecc122 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetWatermarkingSupport_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 129) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GetWatermarkingSupport_Response( + OEMCrypto_WatermarkingSupport result) { + uint32_t api_value = 129; /* from _oecc129 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadProvisioning_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, uint8_t** message, + size_t* message_length, size_t* core_message_length, uint8_t** signature, + size_t* signature_length, uint8_t** wrapped_private_key, + size_t** wrapped_private_key_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 102) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, message_length); + OPK_Unpack_size_t(msg, signature_length); + OPK_UnpackNullable_size_t(msg, wrapped_private_key_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackInPlace(msg, (uint8_t**)message, OPK_FromSizeTPtr(message_length)); + OPK_Unpack_size_t(msg, core_message_length); + OPK_UnpackInPlace(msg, (uint8_t**)signature, + OPK_FromSizeTPtr(signature_length)); + *wrapped_private_key = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(wrapped_private_key_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_LoadProvisioning_Response( + OEMCryptoResult result, const uint8_t* wrapped_private_key, + const size_t* wrapped_private_key_length) { + uint32_t api_value = 102; /* from _oecc102 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, wrapped_private_key_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)wrapped_private_key, + OPK_FromSizeTPtr(wrapped_private_key_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadDRMPrivateKey_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + OEMCrypto_PrivateKeyType* key_type, + uint8_t** wrapped_private_key, + size_t* wrapped_private_key_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 107) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, wrapped_private_key_length); + OPK_Unpack_uint32_t(msg, session); + OPK_Unpack_uint32_t(msg, key_type); + if (!Is_Valid_OEMCrypto_PrivateKeyType(*key_type)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackInPlace(msg, (uint8_t**)wrapped_private_key, + OPK_FromSizeTPtr(wrapped_private_key_length)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_LoadDRMPrivateKey_Response(OEMCryptoResult result) { + uint32_t api_value = 107; /* from _oecc107 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadTestRSAKey_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 45) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_LoadTestRSAKey_Response(OEMCryptoResult result) { + uint32_t api_value = 45; /* from _oecc45 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GenerateRSASignature_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, uint8_t** message, + size_t* message_length, uint8_t** signature, size_t** signature_length, + RSA_Padding_Scheme* padding_scheme) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 36) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, message_length); + OPK_UnpackNullable_size_t(msg, signature_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackInPlace(msg, (uint8_t**)message, OPK_FromSizeTPtr(message_length)); + *signature = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(signature_length), sizeof(uint8_t)); + OPK_Unpack_uint8_t(msg, padding_scheme); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GenerateRSASignature_Response( + OEMCryptoResult result, const uint8_t* signature, + const size_t* signature_length) { + uint32_t api_value = 36; /* from _oecc36 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, signature_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)signature, + OPK_FromSizeTPtr(signature_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_PrepAndSignProvisioningRequest_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, uint8_t** message, + size_t* message_length, size_t** core_message_size, uint8_t** signature, + size_t** signature_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 98) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, message_length); + OPK_UnpackNullable_size_t(msg, signature_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackInPlace(msg, (uint8_t**)message, OPK_FromSizeTPtr(message_length)); + OPK_UnpackNullable_size_t(msg, core_message_size); + *signature = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(signature_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_PrepAndSignProvisioningRequest_Response( + OEMCryptoResult result, const uint8_t* message, size_t message_length, + const size_t* core_message_size, const uint8_t* signature, + const size_t* signature_length) { + uint32_t api_value = 98; /* from _oecc98 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_size_t(&msg, &message_length); + OPK_PackNullable_size_t(&msg, signature_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)message, + OPK_ToLengthType(message_length)); + } + OPK_PackNullable_size_t(&msg, core_message_size); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)signature, + OPK_FromSizeTPtr(signature_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_CreateUsageTableHeader_Request(ODK_Message* msg, + uint8_t** header_buffer, + size_t** header_buffer_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 61) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackNullable_size_t(msg, header_buffer_length); + *header_buffer = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(header_buffer_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_CreateUsageTableHeader_Response( + OEMCryptoResult result, const uint8_t* header_buffer, + const size_t* header_buffer_length) { + uint32_t api_value = 61; /* from _oecc61 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, header_buffer_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)header_buffer, + OPK_FromSizeTPtr(header_buffer_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadUsageTableHeader_Request(ODK_Message* msg, uint8_t** buffer, + size_t* buffer_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 62) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, buffer_length); + OPK_UnpackInPlace(msg, (uint8_t**)buffer, OPK_FromSizeTPtr(buffer_length)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_LoadUsageTableHeader_Response(OEMCryptoResult result) { + uint32_t api_value = 62; /* from _oecc62 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_CreateNewUsageEntry_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint32_t** usage_entry_number) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 63) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_uint32_t(msg, session); + *usage_entry_number = (uint32_t*)OPK_UnpackAlloc(msg, sizeof(uint32_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_CreateNewUsageEntry_Response( + OEMCryptoResult result, const uint32_t* usage_entry_number) { + uint32_t api_value = 63; /* from _oecc63 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_uint32_t(&msg, usage_entry_number); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_ReuseUsageEntry_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint32_t* usage_entry_number) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 127) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_uint32_t(msg, session); + OPK_Unpack_uint32_t(msg, usage_entry_number); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_ReuseUsageEntry_Response(OEMCryptoResult result) { + uint32_t api_value = 127; /* from _oecc127 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadUsageEntry_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint32_t* usage_entry_number, + uint8_t** buffer, + size_t* buffer_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 64) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, buffer_length); + OPK_Unpack_uint32_t(msg, session); + OPK_Unpack_uint32_t(msg, usage_entry_number); + OPK_UnpackInPlace(msg, (uint8_t**)buffer, OPK_FromSizeTPtr(buffer_length)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_LoadUsageEntry_Response(OEMCryptoResult result) { + uint32_t api_value = 64; /* from _oecc64 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_UpdateUsageEntry_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + OEMCrypto_SharedMemory** header_buffer, + size_t** header_buffer_length, + OEMCrypto_SharedMemory** entry_buffer, + size_t** entry_buffer_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 65) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackNullable_size_t(msg, header_buffer_length); + OPK_UnpackNullable_size_t(msg, entry_buffer_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackSharedBuffer(msg, header_buffer, + OPK_FromSizeTPtrPtr(header_buffer_length), + /* map */ true, /* is_output */ true); + OPK_UnpackSharedBuffer(msg, entry_buffer, + OPK_FromSizeTPtrPtr(entry_buffer_length), + /* map */ true, /* is_output */ true); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_UpdateUsageEntry_Response( + OEMCryptoResult result, const OEMCrypto_SharedMemory* header_buffer, + const size_t* header_buffer_length, + const OEMCrypto_SharedMemory* entry_buffer, + const size_t* entry_buffer_length) { + uint32_t api_value = 65; /* from _oecc65 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, header_buffer_length); + OPK_PackNullable_size_t(&msg, entry_buffer_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackSharedBuffer( + &msg, header_buffer, OPK_FromSizeTPtr(header_buffer_length), + /* map */ false, /* copy_in */ false, /* is_output */ true); + } + if (SuccessResult(result)) { + OPK_PackSharedBuffer(&msg, entry_buffer, + OPK_FromSizeTPtr(entry_buffer_length), /* map */ false, + /* copy_in */ false, /* is_output */ true); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_DeactivateUsageEntry_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint8_t** pst, + size_t* pst_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 66) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, pst_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackInPlace(msg, (uint8_t**)pst, OPK_FromSizeTPtr(pst_length)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_DeactivateUsageEntry_Response(OEMCryptoResult result) { + uint32_t api_value = 66; /* from _oecc66 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_ReportUsage_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, uint8_t** pst, + size_t* pst_length, uint8_t** buffer, + size_t** buffer_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 32) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, pst_length); + OPK_UnpackNullable_size_t(msg, buffer_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackInPlace(msg, (uint8_t**)pst, OPK_FromSizeTPtr(pst_length)); + *buffer = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(buffer_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_ReportUsage_Response(OEMCryptoResult result, + const uint8_t* buffer, + const size_t* buffer_length) { + uint32_t api_value = 32; /* from _oecc32 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, buffer_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)buffer, + OPK_FromSizeTPtr(buffer_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_MoveEntry_Request(ODK_Message* msg, OEMCrypto_SESSION* session, + uint32_t* new_index) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 68) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_uint32_t(msg, session); + OPK_Unpack_uint32_t(msg, new_index); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_MoveEntry_Response(OEMCryptoResult result) { + uint32_t api_value = 68; /* from _oecc68 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_ShrinkUsageTableHeader_Request(ODK_Message* msg, + uint32_t* new_entry_count, + uint8_t** header_buffer, + size_t** header_buffer_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 67) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackNullable_size_t(msg, header_buffer_length); + OPK_Unpack_uint32_t(msg, new_entry_count); + *header_buffer = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(header_buffer_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_ShrinkUsageTableHeader_Response( + OEMCryptoResult result, const uint8_t* header_buffer, + const size_t* header_buffer_length) { + uint32_t api_value = 67; /* from _oecc67 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, header_buffer_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)header_buffer, + OPK_FromSizeTPtr(header_buffer_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetBootCertificateChain_Request( + ODK_Message* msg, uint8_t** bcc, size_t** bcc_length, + uint8_t** additional_signature, size_t** additional_signature_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 116) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackNullable_size_t(msg, bcc_length); + OPK_UnpackNullable_size_t(msg, additional_signature_length); + *bcc = (uint8_t*)OPK_UnpackAllocBuffer(msg, OPK_FromSizeTPtrPtr(bcc_length), + sizeof(uint8_t)); + *additional_signature = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(additional_signature_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GetBootCertificateChain_Response( + OEMCryptoResult result, const uint8_t* bcc, const size_t* bcc_length, + const uint8_t* additional_signature, + const size_t* additional_signature_length) { + uint32_t api_value = 116; /* from _oecc116 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, bcc_length); + OPK_PackNullable_size_t(&msg, additional_signature_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)bcc, OPK_FromSizeTPtr(bcc_length)); + } + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)additional_signature, + OPK_FromSizeTPtr(additional_signature_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GenerateCertificateKeyPair_Request( + ODK_Message* msg, 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) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 117) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackNullable_size_t(msg, public_key_length); + OPK_UnpackNullable_size_t(msg, public_key_signature_length); + OPK_UnpackNullable_size_t(msg, wrapped_private_key_length); + OPK_Unpack_uint32_t(msg, session); + *public_key = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(public_key_length), sizeof(uint8_t)); + *public_key_signature = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(public_key_signature_length), sizeof(uint8_t)); + *wrapped_private_key = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(wrapped_private_key_length), sizeof(uint8_t)); + *key_type = (uint32_t*)OPK_UnpackAlloc(msg, sizeof(OEMCrypto_PrivateKeyType)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GenerateCertificateKeyPair_Response( + OEMCryptoResult result, const uint8_t* public_key, + const size_t* public_key_length, const uint8_t* public_key_signature, + const size_t* public_key_signature_length, + const uint8_t* wrapped_private_key, + const size_t* wrapped_private_key_length, + const OEMCrypto_PrivateKeyType* key_type) { + uint32_t api_value = 117; /* from _oecc117 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, public_key_length); + OPK_PackNullable_size_t(&msg, public_key_signature_length); + OPK_PackNullable_size_t(&msg, wrapped_private_key_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)public_key, + OPK_FromSizeTPtr(public_key_length)); + } + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)public_key_signature, + OPK_FromSizeTPtr(public_key_signature_length)); + } + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)wrapped_private_key, + OPK_FromSizeTPtr(wrapped_private_key_length)); + } + OPK_PackNullable_uint32_t(&msg, key_type); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_SupportsDecryptHash_Request(ODK_Message* msg) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 86) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_SupportsDecryptHash_Response(uint32_t result) { + uint32_t api_value = 86; /* from _oecc86 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_SetDecryptHash_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint32_t* frame_number, uint8_t** hash, + size_t* hash_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 88) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, hash_length); + OPK_Unpack_uint32_t(msg, session); + OPK_Unpack_uint32_t(msg, frame_number); + OPK_UnpackInPlace(msg, (uint8_t**)hash, OPK_FromSizeTPtr(hash_length)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_SetDecryptHash_Response(OEMCryptoResult result) { + uint32_t api_value = 88; /* from _oecc88 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GetHashErrorCode_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint32_t** failed_frame_number) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 89) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_uint32_t(msg, session); + *failed_frame_number = (uint32_t*)OPK_UnpackAlloc(msg, sizeof(uint32_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GetHashErrorCode_Response( + OEMCryptoResult result, const uint32_t* failed_frame_number) { + uint32_t api_value = 89; /* from _oecc89 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_uint32_t(&msg, failed_frame_number); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_AllocateSecureBuffer_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, size_t* buffer_size, + OEMCrypto_DestBufferDesc** output_descriptor, int** secure_fd) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 109) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_uint32_t(msg, session); + OPK_Unpack_size_t(msg, buffer_size); + *output_descriptor = (OEMCrypto_DestBufferDesc*)OPK_UnpackAlloc( + msg, sizeof(OEMCrypto_DestBufferDesc)); + *secure_fd = (int*)OPK_UnpackAlloc(msg, sizeof(int)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_AllocateSecureBuffer_Response( + OEMCryptoResult result, const OEMCrypto_DestBufferDesc* output_descriptor, + const int* secure_fd) { + uint32_t api_value = 109; /* from _oecc109 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_OEMCrypto_DestBufferDesc(&msg, output_descriptor); + OPK_PackNullable_int(&msg, secure_fd); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_FreeSecureBuffer_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, + OEMCrypto_DestBufferDesc** output_descriptor, int* secure_fd) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 110) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_uint32_t(msg, session); + *output_descriptor = (OEMCrypto_DestBufferDesc*)OPK_UnpackAlloc( + msg, sizeof(OEMCrypto_DestBufferDesc)); + OPK_Unpack_int(msg, secure_fd); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_FreeSecureBuffer_Response( + OEMCryptoResult result, const OEMCrypto_DestBufferDesc* output_descriptor) { + uint32_t api_value = 110; /* from _oecc110 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_OEMCrypto_DestBufferDesc(&msg, output_descriptor); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_InstallOemPrivateKey_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, + OEMCrypto_PrivateKeyType* key_type, uint8_t** wrapped_private_key, + size_t* wrapped_private_key_length) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 118) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, wrapped_private_key_length); + OPK_Unpack_uint32_t(msg, session); + OPK_Unpack_uint32_t(msg, key_type); + if (!Is_Valid_OEMCrypto_PrivateKeyType(*key_type)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_INVALID_ENUM_VALUE); + } + OPK_UnpackInPlace(msg, (uint8_t**)wrapped_private_key, + OPK_FromSizeTPtr(wrapped_private_key_length)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_InstallOemPrivateKey_Response(OEMCryptoResult result) { + uint32_t api_value = 118; /* from _oecc118 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_ReassociateEntitledKeySession_Request( + ODK_Message* msg, OEMCrypto_SESSION* key_session, + OEMCrypto_SESSION* oec_session) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 119) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_uint32_t(msg, key_session); + OPK_Unpack_uint32_t(msg, oec_session); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_ReassociateEntitledKeySession_Response( + OEMCryptoResult result) { + uint32_t api_value = 119; /* from _oecc119 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_LoadCasECMKeys_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, uint8_t** message, + size_t* message_length, OEMCrypto_EntitledContentKeyObject** even_key, + OEMCrypto_EntitledContentKeyObject** odd_key) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 120) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, message_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackInPlace(msg, (uint8_t**)message, OPK_FromSizeTPtr(message_length)); + OPK_UnpackNullable_OEMCrypto_EntitledContentKeyObject(msg, even_key); + OPK_UnpackNullable_OEMCrypto_EntitledContentKeyObject(msg, odd_key); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_LoadCasECMKeys_Response(OEMCryptoResult result) { + uint32_t api_value = 120; /* from _oecc120 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_OPK_SerializationVersion_Request(ODK_Message* msg, + uint32_t** ree_major, + uint32_t** ree_minor, + uint32_t** tee_major, + uint32_t** tee_minor) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 115) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackNullable_uint32_t(msg, ree_major); + OPK_UnpackNullable_uint32_t(msg, ree_minor); + *tee_major = (uint32_t*)OPK_UnpackAlloc(msg, sizeof(uint32_t)); + *tee_minor = (uint32_t*)OPK_UnpackAlloc(msg, sizeof(uint32_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_OPK_SerializationVersion_Response( + OEMCryptoResult result, const uint32_t* ree_major, + const uint32_t* ree_minor, const uint32_t* tee_major, + const uint32_t* tee_minor) { + uint32_t api_value = 115; /* from _oecc115 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackNullable_uint32_t(&msg, ree_major); + OPK_PackNullable_uint32_t(&msg, ree_minor); + OPK_PackNullable_uint32_t(&msg, tee_major); + OPK_PackNullable_uint32_t(&msg, tee_minor); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_GenerateOTARequest_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint8_t** buffer, + size_t** buffer_length, + uint32_t* use_test_key) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 113) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_UnpackNullable_size_t(msg, buffer_length); + OPK_Unpack_uint32_t(msg, session); + *buffer = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(buffer_length), sizeof(uint8_t)); + OPK_Unpack_uint32_t(msg, use_test_key); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_GenerateOTARequest_Response(OEMCryptoResult result, + const uint8_t* buffer, + const size_t* buffer_length) { + uint32_t api_value = 113; /* from _oecc113 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_PackNullable_size_t(&msg, buffer_length); + OPK_Pack_uint32_t(&msg, &result); + if (SuccessResult(result)) { + OPK_PackMemory(&msg, (const uint8_t*)buffer, + OPK_FromSizeTPtr(buffer_length)); + } + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} + +void OPK_Unpack_ProcessOTAKeybox_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint8_t** buffer, + size_t* buffer_length, + uint32_t* use_test_key) { + uint32_t api_value = UINT32_MAX; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 114) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, buffer_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackInPlace(msg, (uint8_t**)buffer, OPK_FromSizeTPtr(buffer_length)); + OPK_Unpack_uint32_t(msg, use_test_key); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} + +ODK_Message OPK_Pack_ProcessOTAKeybox_Response(OEMCryptoResult result) { + uint32_t api_value = 114; /* from _oecc114 */ + ODK_Message msg = TOS_Transport_GetResponse(); + OPK_Pack_uint32_t(&msg, &api_value); + OPK_Pack_uint32_t(&msg, &result); + OPK_PackEOM(&msg); + OPK_SharedBuffer_FinalizePacking(); + return msg; +} diff --git a/oemcrypto/opk/serialization/tee/GEN_tee_serializer.h b/oemcrypto/opk/serialization/tee/GEN_tee_serializer.h new file mode 100644 index 0000000..3bc2ccc --- /dev/null +++ b/oemcrypto/opk/serialization/tee/GEN_tee_serializer.h @@ -0,0 +1,437 @@ +/* + * Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine License + * Agreement. + */ + +/* + * This code is auto-generated, do not edit + */ +#ifndef OPK_TEE_SERIALIZER_H_ +#define OPK_TEE_SERIALIZER_H_ + +#include "log_macros.h" +#include "opk_serialization_base.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void OPK_Unpack_SetSandbox_Request(ODK_Message* msg, uint8_t** sandbox_id, + size_t* sandbox_id_length); +ODK_Message OPK_Pack_SetSandbox_Response(OEMCryptoResult result); +void OPK_Unpack_Initialize_Request(ODK_Message* msg); +ODK_Message OPK_Pack_Initialize_Response(OEMCryptoResult result); +void OPK_Unpack_Terminate_Request(ODK_Message* msg); +ODK_Message OPK_Pack_Terminate_Response(OEMCryptoResult result); +void OPK_Unpack_Idle_Request(ODK_Message* msg, OEMCrypto_IdleState* state, + uint32_t* os_specific_code); +ODK_Message OPK_Pack_Idle_Response(OEMCryptoResult result); +void OPK_Unpack_Wake_Request(ODK_Message* msg); +ODK_Message OPK_Pack_Wake_Response(OEMCryptoResult result); +void OPK_Unpack_OpenSession_Request(ODK_Message* msg, + OEMCrypto_SESSION** session); +ODK_Message OPK_Pack_OpenSession_Response(OEMCryptoResult result, + const OEMCrypto_SESSION* session); +void OPK_Unpack_CloseSession_Request(ODK_Message* msg, + OEMCrypto_SESSION* session); +ODK_Message OPK_Pack_CloseSession_Response(OEMCryptoResult result); +void OPK_Unpack_CreateEntitledKeySession_Request( + ODK_Message* msg, OEMCrypto_SESSION* oec_session, + OEMCrypto_SESSION** key_session); +ODK_Message OPK_Pack_CreateEntitledKeySession_Response( + OEMCryptoResult result, const OEMCrypto_SESSION* key_session); +void OPK_Unpack_RemoveEntitledKeySession_Request( + ODK_Message* msg, OEMCrypto_SESSION* key_session); +ODK_Message OPK_Pack_RemoveEntitledKeySession_Response(OEMCryptoResult result); +void OPK_Unpack_GenerateDerivedKeys_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, + OEMCrypto_SharedMemory** mac_key_context, size_t* mac_key_context_length, + OEMCrypto_SharedMemory** enc_key_context, size_t* enc_key_context_length); +ODK_Message OPK_Pack_GenerateDerivedKeys_Response(OEMCryptoResult result); +void OPK_Unpack_DeriveKeysFromSessionKey_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, uint8_t** derivation_key, + size_t* derivation_key_length, OEMCrypto_SharedMemory** mac_key_context, + size_t* mac_key_context_length, OEMCrypto_SharedMemory** enc_key_context, + size_t* enc_key_context_length); +ODK_Message OPK_Pack_DeriveKeysFromSessionKey_Response(OEMCryptoResult result); +void OPK_Unpack_GenerateNonce_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint32_t** nonce); +ODK_Message OPK_Pack_GenerateNonce_Response(OEMCryptoResult result, + const uint32_t* nonce); +void OPK_Unpack_PrepAndSignLicenseRequest_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, uint8_t** message, + size_t* message_length, size_t** core_message_size, uint8_t** signature, + size_t** signature_length); +ODK_Message OPK_Pack_PrepAndSignLicenseRequest_Response( + OEMCryptoResult result, const uint8_t* message, size_t message_length, + const size_t* core_message_size, const uint8_t* signature, + const size_t* signature_length); +void OPK_Unpack_PrepAndSignRenewalRequest_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, uint8_t** message, + size_t* message_length, size_t** core_message_size, uint8_t** signature, + size_t** signature_length); +ODK_Message OPK_Pack_PrepAndSignRenewalRequest_Response( + OEMCryptoResult result, const uint8_t* message, size_t message_length, + const size_t* core_message_size, const uint8_t* signature, + const size_t* signature_length); +void OPK_Unpack_LoadKeys_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, uint8_t** message, + size_t* message_length, uint8_t** signature, size_t* signature_length, + OEMCrypto_Substring* enc_mac_keys_iv, OEMCrypto_Substring* enc_mac_keys, + size_t* key_array_length, OEMCrypto_KeyObject** key_array, + OEMCrypto_Substring* pst, OEMCrypto_Substring* srm_restriction_data, + OEMCrypto_LicenseType* license_type); +ODK_Message OPK_Pack_LoadKeys_Response(OEMCryptoResult result); +void OPK_Unpack_LoadLicense_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint8_t** message, size_t* message_length, + size_t* core_message_length, + uint8_t** signature, + size_t* signature_length); +ODK_Message OPK_Pack_LoadLicense_Response(OEMCryptoResult result); +void OPK_Unpack_LoadEntitledContentKeys_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, uint8_t** message, + size_t* message_length, size_t* key_array_length, + OEMCrypto_EntitledContentKeyObject** key_array); +ODK_Message OPK_Pack_LoadEntitledContentKeys_Response(OEMCryptoResult result); +void OPK_Unpack_RefreshKeys_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint8_t** message, size_t* message_length, + uint8_t** signature, + size_t* signature_length, size_t* num_keys, + OEMCrypto_KeyRefreshObject** key_array); +ODK_Message OPK_Pack_RefreshKeys_Response(OEMCryptoResult result); +void OPK_Unpack_LoadRenewal_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint8_t** message, size_t* message_length, + size_t* core_message_length, + uint8_t** signature, + size_t* signature_length); +ODK_Message OPK_Pack_LoadRenewal_Response(OEMCryptoResult result); +void OPK_Unpack_QueryKeyControl_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint8_t** content_key_id, + size_t* content_key_id_length, + uint8_t** key_control_block, + size_t** key_control_block_length); +ODK_Message OPK_Pack_QueryKeyControl_Response( + OEMCryptoResult result, const uint8_t* key_control_block, + const size_t* key_control_block_length); +void OPK_Unpack_SelectKey_Request(ODK_Message* msg, OEMCrypto_SESSION* session, + uint8_t** content_key_id, + size_t* content_key_id_length, + OEMCryptoCipherMode* cipher_mode); +ODK_Message OPK_Pack_SelectKey_Response(OEMCryptoResult result); +void OPK_Unpack_DecryptCENC_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + OEMCrypto_SampleDescription** samples, + size_t* samples_length, + OEMCrypto_CENCEncryptPatternDesc** pattern); +ODK_Message OPK_Pack_DecryptCENC_Response(OEMCryptoResult result); +void OPK_Unpack_CopyBuffer_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, + OEMCrypto_SharedMemory** data_addr, size_t* data_addr_length, + OEMCrypto_DestBufferDesc** out_buffer_descriptor, uint8_t* subsample_flags); +ODK_Message OPK_Pack_CopyBuffer_Response(OEMCryptoResult result); +void OPK_Unpack_Generic_Encrypt_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + OEMCrypto_SharedMemory** in_buffer, + size_t* in_buffer_length, uint8_t* iv, + OEMCrypto_Algorithm* algorithm, + OEMCrypto_SharedMemory** out_buffer); +ODK_Message OPK_Pack_Generic_Encrypt_Response( + OEMCryptoResult result, size_t in_buffer_length, + const OEMCrypto_SharedMemory* out_buffer); +void OPK_Unpack_Generic_Decrypt_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + OEMCrypto_SharedMemory** in_buffer, + size_t* in_buffer_length, uint8_t* iv, + OEMCrypto_Algorithm* algorithm, + OEMCrypto_SharedMemory** out_buffer); +ODK_Message OPK_Pack_Generic_Decrypt_Response( + OEMCryptoResult result, size_t in_buffer_length, + const OEMCrypto_SharedMemory* out_buffer); +void OPK_Unpack_Generic_Sign_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + OEMCrypto_SharedMemory** buffer, + size_t* buffer_length, + OEMCrypto_Algorithm* algorithm, + OEMCrypto_SharedMemory** signature, + size_t** signature_length); +ODK_Message OPK_Pack_Generic_Sign_Response( + OEMCryptoResult result, const OEMCrypto_SharedMemory* signature, + const size_t* signature_length); +void OPK_Unpack_Generic_Verify_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + OEMCrypto_SharedMemory** buffer, + size_t* buffer_length, + OEMCrypto_Algorithm* algorithm, + OEMCrypto_SharedMemory** signature, + size_t* signature_length); +ODK_Message OPK_Pack_Generic_Verify_Response(OEMCryptoResult result); +void OPK_Unpack_WrapKeyboxOrOEMCert_Request( + ODK_Message* msg, uint8_t** keybox_or_cert, size_t* keybox_or_cert_length, + uint8_t** wrapped_keybox_or_cert, size_t** wrapped_keybox_or_cert_length, + uint8_t** transport_key, size_t* transport_key_length); +ODK_Message OPK_Pack_WrapKeyboxOrOEMCert_Response( + OEMCryptoResult result, const uint8_t* wrapped_keybox_or_cert, + const size_t* wrapped_keybox_or_cert_length); +void OPK_Unpack_InstallKeyboxOrOEMCert_Request(ODK_Message* msg, + uint8_t** keybox_or_cert, + size_t* keybox_or_cert_length); +ODK_Message OPK_Pack_InstallKeyboxOrOEMCert_Response(OEMCryptoResult result); +void OPK_Unpack_GetProvisioningMethod_Request(ODK_Message* msg); +ODK_Message OPK_Pack_GetProvisioningMethod_Response( + OEMCrypto_ProvisioningMethod result); +void OPK_Unpack_IsKeyboxOrOEMCertValid_Request(ODK_Message* msg); +ODK_Message OPK_Pack_IsKeyboxOrOEMCertValid_Response(OEMCryptoResult result); +void OPK_Unpack_GetDeviceID_Request(ODK_Message* msg, uint8_t** device_id, + size_t** device_id_length); +ODK_Message OPK_Pack_GetDeviceID_Response(OEMCryptoResult result, + const uint8_t* device_id, + const size_t* device_id_length); +void OPK_Unpack_GetKeyData_Request(ODK_Message* msg, uint8_t** key_data, + size_t** key_data_length); +ODK_Message OPK_Pack_GetKeyData_Response(OEMCryptoResult result, + const uint8_t* key_data, + const size_t* key_data_length); +void OPK_Unpack_LoadTestKeybox_Request(ODK_Message* msg, uint8_t** buffer, + size_t* buffer_length); +ODK_Message OPK_Pack_LoadTestKeybox_Response(OEMCryptoResult result); +void OPK_Unpack_LoadOEMPrivateKey_Request(ODK_Message* msg, + OEMCrypto_SESSION* session); +ODK_Message OPK_Pack_LoadOEMPrivateKey_Response(OEMCryptoResult result); +void OPK_Unpack_GetOEMPublicCertificate_Request(ODK_Message* msg, + uint8_t** public_cert, + size_t** public_cert_length); +ODK_Message OPK_Pack_GetOEMPublicCertificate_Response( + OEMCryptoResult result, const uint8_t* public_cert, + const size_t* public_cert_length); +void OPK_Unpack_GetRandom_Request(ODK_Message* msg, uint8_t** random_data, + size_t* random_data_length); +ODK_Message OPK_Pack_GetRandom_Response(OEMCryptoResult result, + const uint8_t* random_data, + size_t random_data_length); +void OPK_Unpack_APIVersion_Request(ODK_Message* msg); +ODK_Message OPK_Pack_APIVersion_Response(uint32_t result); +void OPK_Unpack_MinorAPIVersion_Request(ODK_Message* msg); +ODK_Message OPK_Pack_MinorAPIVersion_Response(uint32_t result); +void OPK_Unpack_BuildInformation_Request(ODK_Message* msg, char** buffer, + size_t** buffer_length); +ODK_Message OPK_Pack_BuildInformation_Response(OEMCryptoResult result, + const char* buffer, + const size_t* buffer_length); +void OPK_Unpack_Security_Patch_Level_Request(ODK_Message* msg); +ODK_Message OPK_Pack_Security_Patch_Level_Response(uint8_t result); +void OPK_Unpack_SecurityLevel_Request(ODK_Message* msg); +ODK_Message OPK_Pack_SecurityLevel_Response(OEMCrypto_Security_Level result); +void OPK_Unpack_GetHDCPCapability_Request(ODK_Message* msg, + OEMCrypto_HDCP_Capability** current, + OEMCrypto_HDCP_Capability** maximum); +ODK_Message OPK_Pack_GetHDCPCapability_Response( + OEMCryptoResult result, const OEMCrypto_HDCP_Capability* current, + const OEMCrypto_HDCP_Capability* maximum); +void OPK_Unpack_GetDTCP2Capability_Request( + ODK_Message* msg, OEMCrypto_DTCP2_Capability** capability); +ODK_Message OPK_Pack_GetDTCP2Capability_Response( + OEMCryptoResult result, const OEMCrypto_DTCP2_Capability* capability); +void OPK_Unpack_SupportsUsageTable_Request(ODK_Message* msg); +ODK_Message OPK_Pack_SupportsUsageTable_Response(bool result); +void OPK_Unpack_MaximumUsageTableHeaderSize_Request(ODK_Message* msg); +ODK_Message OPK_Pack_MaximumUsageTableHeaderSize_Response(size_t result); +void OPK_Unpack_IsAntiRollbackHwPresent_Request(ODK_Message* msg); +ODK_Message OPK_Pack_IsAntiRollbackHwPresent_Response(bool result); +void OPK_Unpack_GetNumberOfOpenSessions_Request(ODK_Message* msg, + size_t** count); +ODK_Message OPK_Pack_GetNumberOfOpenSessions_Response(OEMCryptoResult result, + const size_t* count); +void OPK_Unpack_GetMaxNumberOfSessions_Request(ODK_Message* msg, size_t** max); +ODK_Message OPK_Pack_GetMaxNumberOfSessions_Response(OEMCryptoResult result, + const size_t* max); +void OPK_Unpack_SupportedCertificates_Request(ODK_Message* msg); +ODK_Message OPK_Pack_SupportedCertificates_Response(uint32_t result); +void OPK_Unpack_GetCurrentSRMVersion_Request(ODK_Message* msg, + uint16_t** version); +ODK_Message OPK_Pack_GetCurrentSRMVersion_Response(OEMCryptoResult result, + const uint16_t* version); +void OPK_Unpack_GetAnalogOutputFlags_Request(ODK_Message* msg); +ODK_Message OPK_Pack_GetAnalogOutputFlags_Response(uint32_t result); +void OPK_Unpack_ResourceRatingTier_Request(ODK_Message* msg); +ODK_Message OPK_Pack_ResourceRatingTier_Response(uint32_t result); +void OPK_Unpack_ProductionReady_Request(ODK_Message* msg); +ODK_Message OPK_Pack_ProductionReady_Response(OEMCryptoResult result); +void OPK_Unpack_GetWatermarkingSupport_Request(ODK_Message* msg); +ODK_Message OPK_Pack_GetWatermarkingSupport_Response( + OEMCrypto_WatermarkingSupport result); +void OPK_Unpack_LoadProvisioning_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, uint8_t** message, + size_t* message_length, size_t* core_message_length, uint8_t** signature, + size_t* signature_length, uint8_t** wrapped_private_key, + size_t** wrapped_private_key_length); +ODK_Message OPK_Pack_LoadProvisioning_Response( + OEMCryptoResult result, const uint8_t* wrapped_private_key, + const size_t* wrapped_private_key_length); +void OPK_Unpack_LoadDRMPrivateKey_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + OEMCrypto_PrivateKeyType* key_type, + uint8_t** wrapped_private_key, + size_t* wrapped_private_key_length); +ODK_Message OPK_Pack_LoadDRMPrivateKey_Response(OEMCryptoResult result); +void OPK_Unpack_LoadTestRSAKey_Request(ODK_Message* msg); +ODK_Message OPK_Pack_LoadTestRSAKey_Response(OEMCryptoResult result); +void OPK_Unpack_GenerateRSASignature_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, uint8_t** message, + size_t* message_length, uint8_t** signature, size_t** signature_length, + RSA_Padding_Scheme* padding_scheme); +ODK_Message OPK_Pack_GenerateRSASignature_Response( + OEMCryptoResult result, const uint8_t* signature, + const size_t* signature_length); +void OPK_Unpack_PrepAndSignProvisioningRequest_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, uint8_t** message, + size_t* message_length, size_t** core_message_size, uint8_t** signature, + size_t** signature_length); +ODK_Message OPK_Pack_PrepAndSignProvisioningRequest_Response( + OEMCryptoResult result, const uint8_t* message, size_t message_length, + const size_t* core_message_size, const uint8_t* signature, + const size_t* signature_length); +void OPK_Unpack_CreateUsageTableHeader_Request(ODK_Message* msg, + uint8_t** header_buffer, + size_t** header_buffer_length); +ODK_Message OPK_Pack_CreateUsageTableHeader_Response( + OEMCryptoResult result, const uint8_t* header_buffer, + const size_t* header_buffer_length); +void OPK_Unpack_LoadUsageTableHeader_Request(ODK_Message* msg, uint8_t** buffer, + size_t* buffer_length); +ODK_Message OPK_Pack_LoadUsageTableHeader_Response(OEMCryptoResult result); +void OPK_Unpack_CreateNewUsageEntry_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint32_t** usage_entry_number); +ODK_Message OPK_Pack_CreateNewUsageEntry_Response( + OEMCryptoResult result, const uint32_t* usage_entry_number); +void OPK_Unpack_ReuseUsageEntry_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint32_t* usage_entry_number); +ODK_Message OPK_Pack_ReuseUsageEntry_Response(OEMCryptoResult result); +void OPK_Unpack_LoadUsageEntry_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint32_t* usage_entry_number, + uint8_t** buffer, size_t* buffer_length); +ODK_Message OPK_Pack_LoadUsageEntry_Response(OEMCryptoResult result); +void OPK_Unpack_UpdateUsageEntry_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + OEMCrypto_SharedMemory** header_buffer, + size_t** header_buffer_length, + OEMCrypto_SharedMemory** entry_buffer, + size_t** entry_buffer_length); +ODK_Message OPK_Pack_UpdateUsageEntry_Response( + OEMCryptoResult result, const OEMCrypto_SharedMemory* header_buffer, + const size_t* header_buffer_length, + const OEMCrypto_SharedMemory* entry_buffer, + const size_t* entry_buffer_length); +void OPK_Unpack_DeactivateUsageEntry_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint8_t** pst, size_t* pst_length); +ODK_Message OPK_Pack_DeactivateUsageEntry_Response(OEMCryptoResult result); +void OPK_Unpack_ReportUsage_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, uint8_t** pst, + size_t* pst_length, uint8_t** buffer, + size_t** buffer_length); +ODK_Message OPK_Pack_ReportUsage_Response(OEMCryptoResult result, + const uint8_t* buffer, + const size_t* buffer_length); +void OPK_Unpack_MoveEntry_Request(ODK_Message* msg, OEMCrypto_SESSION* session, + uint32_t* new_index); +ODK_Message OPK_Pack_MoveEntry_Response(OEMCryptoResult result); +void OPK_Unpack_ShrinkUsageTableHeader_Request(ODK_Message* msg, + uint32_t* new_entry_count, + uint8_t** header_buffer, + size_t** header_buffer_length); +ODK_Message OPK_Pack_ShrinkUsageTableHeader_Response( + OEMCryptoResult result, const uint8_t* header_buffer, + const size_t* header_buffer_length); +void OPK_Unpack_GetBootCertificateChain_Request( + ODK_Message* msg, uint8_t** bcc, size_t** bcc_length, + uint8_t** additional_signature, size_t** additional_signature_length); +ODK_Message OPK_Pack_GetBootCertificateChain_Response( + OEMCryptoResult result, const uint8_t* bcc, const size_t* bcc_length, + const uint8_t* additional_signature, + const size_t* additional_signature_length); +void OPK_Unpack_GenerateCertificateKeyPair_Request( + ODK_Message* msg, 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); +ODK_Message OPK_Pack_GenerateCertificateKeyPair_Response( + OEMCryptoResult result, const uint8_t* public_key, + const size_t* public_key_length, const uint8_t* public_key_signature, + const size_t* public_key_signature_length, + const uint8_t* wrapped_private_key, + const size_t* wrapped_private_key_length, + const OEMCrypto_PrivateKeyType* key_type); +void OPK_Unpack_SupportsDecryptHash_Request(ODK_Message* msg); +ODK_Message OPK_Pack_SupportsDecryptHash_Response(uint32_t result); +void OPK_Unpack_SetDecryptHash_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint32_t* frame_number, uint8_t** hash, + size_t* hash_length); +ODK_Message OPK_Pack_SetDecryptHash_Response(OEMCryptoResult result); +void OPK_Unpack_GetHashErrorCode_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint32_t** failed_frame_number); +ODK_Message OPK_Pack_GetHashErrorCode_Response( + OEMCryptoResult result, const uint32_t* failed_frame_number); +void OPK_Unpack_AllocateSecureBuffer_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, size_t* buffer_size, + OEMCrypto_DestBufferDesc** output_descriptor, int** secure_fd); +ODK_Message OPK_Pack_AllocateSecureBuffer_Response( + OEMCryptoResult result, const OEMCrypto_DestBufferDesc* output_descriptor, + const int* secure_fd); +void OPK_Unpack_FreeSecureBuffer_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, + OEMCrypto_DestBufferDesc** output_descriptor, int* secure_fd); +ODK_Message OPK_Pack_FreeSecureBuffer_Response( + OEMCryptoResult result, const OEMCrypto_DestBufferDesc* output_descriptor); +void OPK_Unpack_InstallOemPrivateKey_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, + OEMCrypto_PrivateKeyType* key_type, uint8_t** wrapped_private_key, + size_t* wrapped_private_key_length); +ODK_Message OPK_Pack_InstallOemPrivateKey_Response(OEMCryptoResult result); +void OPK_Unpack_ReassociateEntitledKeySession_Request( + ODK_Message* msg, OEMCrypto_SESSION* key_session, + OEMCrypto_SESSION* oec_session); +ODK_Message OPK_Pack_ReassociateEntitledKeySession_Response( + OEMCryptoResult result); +void OPK_Unpack_LoadCasECMKeys_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, uint8_t** message, + size_t* message_length, OEMCrypto_EntitledContentKeyObject** even_key, + OEMCrypto_EntitledContentKeyObject** odd_key); +ODK_Message OPK_Pack_LoadCasECMKeys_Response(OEMCryptoResult result); +void OPK_Unpack_OPK_SerializationVersion_Request(ODK_Message* msg, + uint32_t** ree_major, + uint32_t** ree_minor, + uint32_t** tee_major, + uint32_t** tee_minor); +ODK_Message OPK_Pack_OPK_SerializationVersion_Response( + OEMCryptoResult result, const uint32_t* ree_major, + const uint32_t* ree_minor, const uint32_t* tee_major, + const uint32_t* tee_minor); +void OPK_Unpack_GenerateOTARequest_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint8_t** buffer, + size_t** buffer_length, + uint32_t* use_test_key); +ODK_Message OPK_Pack_GenerateOTARequest_Response(OEMCryptoResult result, + const uint8_t* buffer, + const size_t* buffer_length); +void OPK_Unpack_ProcessOTAKeybox_Request(ODK_Message* msg, + OEMCrypto_SESSION* session, + uint8_t** buffer, + size_t* buffer_length, + uint32_t* use_test_key); +ODK_Message OPK_Pack_ProcessOTAKeybox_Response(OEMCryptoResult result); +#ifdef __cplusplus +} // extern "C" +#endif +#endif /* OPK_TEE_SERIALIZER_H_ */ diff --git a/oemcrypto/opk/serialization/tee/tee.gyp b/oemcrypto/opk/serialization/tee/tee.gyp new file mode 100644 index 0000000..bf07781 --- /dev/null +++ b/oemcrypto/opk/serialization/tee/tee.gyp @@ -0,0 +1,56 @@ +# +# Builds the static library opk_tee.a from the +# generated sources based on OEMCryptoCENC.h. To +# include it in your gyp file, use: +# +# 'dependencies: [ 'tee.gyp:opk_tee' ] +# +{ + 'includes' : [ + '../settings.gypi' + ], + 'targets' : [ + { + 'target_name' : 'opk_tee', + 'toolsets' : [ 'target' ], + 'type' : 'static_library', + 'standalone_static_library' : 1, + 'include_dirs' : [ + '<(tee_dir)/include', + ], + 'sources' : [ + 'GEN_dispatcher.c', + 'GEN_tee_serializer.c', + 'tee_special_cases.c', + 'tee_os_type.c', + 'tee_version.c', + 'tee_tos_stubs.c', + '<(common_dir)/bump_allocator.c', + '<(common_dir)/common_special_cases.c', + '<(common_dir)/GEN_common_serializer.c', + '<(common_dir)/length_types.c', + '<(common_dir)/log_macros.c', + '<(common_dir)/marshaller_base.c', + '<(common_dir)/message_debug.c', + '<(common_dir)/opk_init.c', + '<(common_dir)/opk_serialization_base.c', + '<(common_dir)/shared_buffer_allocator.c', + '<(odk_dir)/src/odk_message.c', + '<(odk_dir)/src/odk_overflow.c', + ], + 'conditions': [ + # If |enable_code_generator| is true, the GEN_* files will be + # updated if they are out of date. Disable the code generator + # when building the release distribution since the code + # generator isn't included. + ['enable_code_generator=="true"', { + 'dependencies' : [ + '<(generator_dir)/make_source.gyp:make_common_serializer_src', + '<(generator_dir)/make_source.gyp:make_dispatcher_src', + '<(generator_dir)/make_source.gyp:make_tee_serializer_src', + ], + }], + ], + } + ] +} diff --git a/oemcrypto/opk/serialization/tee/tee_os_type.c b/oemcrypto/opk/serialization/tee/tee_os_type.c new file mode 100644 index 0000000..8637c46 --- /dev/null +++ b/oemcrypto/opk/serialization/tee/tee_os_type.c @@ -0,0 +1,12 @@ +/* + * 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 "os_type.h" + +/* + * Indicate that the code is running in the TEE + */ +OPK_OSType OPK_Get_OSType(void) { return OPK_TRUSTED_OS; } diff --git a/oemcrypto/opk/serialization/tee/tee_special_cases.c b/oemcrypto/opk/serialization/tee/tee_special_cases.c new file mode 100644 index 0000000..0fa1631 --- /dev/null +++ b/oemcrypto/opk/serialization/tee/tee_special_cases.c @@ -0,0 +1,81 @@ +/* + * 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 "tee_special_cases.h" + +#include +#include +#include +#include + +#include "GEN_common_serializer.h" +#include "GEN_tee_serializer.h" +#include "OEMCryptoCENC.h" +#include "log_macros.h" +#include "opk_serialization_base.h" +#include "shared_buffer_allocator.h" +#include "tos_shared_memory_interface.h" + +/* + * Special cases due to parameters defined as having pointers into other + * parameters + */ +void OPK_Unpack_RewrapDeviceRSAKey_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, + OEMCrypto_SharedMemory** message, size_t* message_length, + OEMCrypto_SharedMemory** signature, size_t* signature_length, + uint32_t** unaligned_nonce, uint8_t** enc_rsa_key, + size_t* enc_rsa_key_length, uint8_t** enc_rsa_key_iv, + uint8_t** wrapped_rsa_key, size_t** wrapped_rsa_key_length) { + uint32_t api_value = ~(uint32_t)0; + OPK_Unpack_uint32_t(msg, &api_value); + if (api_value != 18) + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_API_VALUE_ERROR); + uint64_t timestamp; + OPK_Unpack_uint64_t(msg, ×tamp); + OPK_Unpack_size_t(msg, message_length); + OPK_Unpack_size_t(msg, signature_length); + OPK_Unpack_size_t(msg, enc_rsa_key_length); + OPK_UnpackNullable_size_t(msg, wrapped_rsa_key_length); + OPK_Unpack_uint32_t(msg, session); + OPK_UnpackSharedBuffer(msg, message, OPK_FromSizeTPtr(message_length), + /* map */ true, /* is_output */ false); + OPK_UnpackSharedBuffer(msg, signature, OPK_FromSizeTPtr(signature_length), + /* map */ true, /* is_output */ false); + size_t unaligned_nonce_offset = 0; + uintptr_t unaligned_nonce_ptr = 0; + OPK_Unpack_size_t(msg, &unaligned_nonce_offset); + if (__builtin_add_overflow((uintptr_t)*message, unaligned_nonce_offset, + &unaligned_nonce_ptr)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_PARSE_ERROR); + return; + } + *unaligned_nonce = (uint32_t*)unaligned_nonce_ptr; + + size_t enc_rsa_key_offset = 0; + uintptr_t enc_rsa_key_ptr = 0; + OPK_Unpack_size_t(msg, &enc_rsa_key_offset); + if (__builtin_add_overflow((uintptr_t)*message, enc_rsa_key_offset, + &enc_rsa_key_ptr)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_PARSE_ERROR); + return; + } + *enc_rsa_key = (uint8_t*)enc_rsa_key_ptr; + + size_t enc_rsa_key_iv_offset = 0; + uintptr_t enc_rsa_key_iv_ptr = 0; + OPK_Unpack_size_t(msg, &enc_rsa_key_iv_offset); + if (__builtin_add_overflow((uintptr_t)*message, enc_rsa_key_iv_offset, + &enc_rsa_key_iv_ptr)) { + ODK_MESSAGE_SETSTATUS(msg, MESSAGE_STATUS_PARSE_ERROR); + return; + } + *enc_rsa_key_iv = (uint8_t*)enc_rsa_key_iv_ptr; + *wrapped_rsa_key = (uint8_t*)OPK_UnpackAllocBuffer( + msg, OPK_FromSizeTPtrPtr(wrapped_rsa_key_length), sizeof(uint8_t)); + OPK_UnpackEOM(msg); + OPK_SharedBuffer_FinalizeUnpacking(); +} diff --git a/oemcrypto/opk/serialization/tee/tee_special_cases.h b/oemcrypto/opk/serialization/tee/tee_special_cases.h new file mode 100644 index 0000000..fac7f38 --- /dev/null +++ b/oemcrypto/opk/serialization/tee/tee_special_cases.h @@ -0,0 +1,36 @@ +/* + * 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 OPK_TEE_SPECIAL_CASES_H_ +#define OPK_TEE_SPECIAL_CASES_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "OEMCryptoCENC.h" +#include "odk_message.h" + +/* + * Special cases due to parameters defined as having pointers into other + * parameters + */ +void OPK_Unpack_RewrapDeviceRSAKey_Request( + ODK_Message* msg, OEMCrypto_SESSION* session, + OEMCrypto_SharedMemory** message, size_t* message_length, + OEMCrypto_SharedMemory** signature, size_t* signature_length, + uint32_t** unaligned_nonce, uint8_t** enc_rsa_key, + size_t* enc_rsa_key_length, uint8_t** enc_rsa_key_iv, + uint8_t** wrapped_rsa_key, size_t** wrapped_rsa_key_length); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPK_TEE_SPECIAL_CASES_H_ diff --git a/oemcrypto/opk/serialization/tee/tee_tos_stubs.c b/oemcrypto/opk/serialization/tee/tee_tos_stubs.c new file mode 100644 index 0000000..44e1e15 --- /dev/null +++ b/oemcrypto/opk/serialization/tee/tee_tos_stubs.c @@ -0,0 +1,35 @@ +/* + * Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine + * License Agreement. + */ + +/* + * This file provides stub implementations for the subset of TOS + * interface functions that are referenced by common code but not + * called in the TEE, so are not required to be implemented on the + * TEE side. + */ + +#include "tos_shared_memory_interface.h" + +#define UNUSED(x) (void)(x) + +bool TOS_SharedMemory_Allocate(size_t size) { + UNUSED(size); + return true; +} + +void TOS_SharedMemory_Release(void) {} + +void TOS_SharedMemory_Finalize(OPK_SharedMemory_CopyVector* in_vec, + size_t in_count, + OPK_SharedMemory_CopyVector* out_vec, + size_t out_count) { + UNUSED(in_vec); + UNUSED(in_count); + UNUSED(out_vec); + UNUSED(out_count); +} + +size_t TOS_SharedMemory_AvailableSize(void) { return 0; } diff --git a/oemcrypto/opk/serialization/tee/tee_version.c b/oemcrypto/opk/serialization/tee/tee_version.c new file mode 100644 index 0000000..43767f4 --- /dev/null +++ b/oemcrypto/opk/serialization/tee/tee_version.c @@ -0,0 +1,37 @@ +/* + * 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 + +#include "OEMCryptoCENC.h" +#include "log_macros.h" +#include "version.h" + +/* + * This function is called from the dispatcher as part of the serialization + * version request/response message handling, it needs to be declared here and + * implemented in the tee serialization layer since it is not implemented in the + * TA. + */ +OEMCryptoResult OEMCrypto_OPK_SerializationVersion(uint32_t* ree_major, + uint32_t* ree_minor, + uint32_t* tee_major, + uint32_t* tee_minor) { + if (tee_major) *tee_major = OPK_SERIALIZATION_VERSION_MAJOR; + if (tee_minor) *tee_minor = OPK_SERIALIZATION_VERSION_MINOR; + + if (tee_major && ree_major) { + if (*tee_major != *ree_major) { + LOGE("Rejected connection from REE version %" PRIu32 ".%" PRIu32 + ". TEE version is %" PRIu32 ".%" PRIu32, + *ree_major, *ree_minor, *tee_major, *tee_minor); + return OPK_ERROR_INCOMPATIBLE_VERSION; + } + LOGI("Accepted connection from REE version %" PRIu32 ".%" PRIu32 + ". TEE version is %" PRIu32 ".%" PRIu32, + *ree_major, *ree_minor, *tee_major, *tee_minor); + } + return OEMCrypto_SUCCESS; +} diff --git a/oemcrypto/opk/strict_compiler_flags.gypi b/oemcrypto/opk/strict_compiler_flags.gypi new file mode 100644 index 0000000..0c5318e --- /dev/null +++ b/oemcrypto/opk/strict_compiler_flags.gypi @@ -0,0 +1,27 @@ +# Copyright 2019 Google LLC.All Rights Reserved.This file and proprietary +# source code may only be used and distributed under the Widevine +# License Agreement. + +{ + 'target_defaults': { + # OPK is written in pure C99 aside from a few places where we use the + # preprocessor to include compiler-specific features only on supporting + # compilers. The core OPK code should compile on the most pure, pedantic C99 + # compiler, and to check this, we turn on flags to keep ourselves honest by + # by using the maximum compiler pedantry. + 'cflags': [ + '-pedantic', + '-pedantic-errors', + '-Werror=pedantic', + ], + 'cflags_c': [ + '-std=c99', + ], + # To make sure no other GYP file can override our C version, we filter out + # all other langauge standards here. + 'cflags_c/': [ + ['exclude', '-std=*'], + ['include', '-std=c99'], + ], + }, +} diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_copy_buffer_fuzz.cc b/oemcrypto/test/fuzz_tests/oemcrypto_copy_buffer_fuzz.cc index 46e3e19..26f48cd 100644 --- a/oemcrypto/test/fuzz_tests/oemcrypto_copy_buffer_fuzz.cc +++ b/oemcrypto/test/fuzz_tests/oemcrypto_copy_buffer_fuzz.cc @@ -14,7 +14,7 @@ void FreeOutputBuffers(OEMCrypto_SESSION session_id, int* secure_fd) { switch (output_descriptor.type) { case OEMCrypto_BufferType_Clear: { - delete[] output_descriptor.buffer.clear.address; + delete[] output_descriptor.buffer.clear.clear_buffer; break; } case OEMCrypto_BufferType_Secure: { @@ -32,7 +32,7 @@ bool InitializeOutputBuffers(OEMCrypto_SESSION session_id, int* secure_fd, size_t input_buffer_size) { switch (output_descriptor.type) { case OEMCrypto_BufferType_Clear: { - output_descriptor.buffer.clear.address = + output_descriptor.buffer.clear.clear_buffer = new OEMCrypto_SharedMemory[input_buffer_size]; return true; } diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gypi b/oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gypi index eaa88cc..686b56e 100644 --- a/oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gypi +++ b/oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gypi @@ -112,7 +112,7 @@ # Include oemcrypto opk implementation code for building opk # implementation fuzz binaries. 'dependencies': [ - '../../opk/oemcrypto_ta/wtpi_test_impl/wtpi_test_impl.gyp:oemcrypto_ta_test_impl_no_ipc', + '<(oemcrypto_dir)/opk/ports/linux/wtpi_test_impl/wtpi_test_impl.gyp:oemcrypto_ta_test_impl_no_ipc', ], }], ], # conditions diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_generic_verify_fuzz.cc b/oemcrypto/test/fuzz_tests/oemcrypto_generic_verify_fuzz.cc index 076d3ca..759286d 100644 --- a/oemcrypto/test/fuzz_tests/oemcrypto_generic_verify_fuzz.cc +++ b/oemcrypto/test/fuzz_tests/oemcrypto_generic_verify_fuzz.cc @@ -42,7 +42,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { license_api_fuzz.LoadLicense(); OEMCrypto_SelectKey(session->session_id(), session->license().keys[0].key_id, session->license().keys[0].key_id_length, - OEMCrypto_CipherMode_CTR); + OEMCrypto_CipherMode_CENC); // Calculate signature for in buffer. size_t signature_length = 0; OEMCrypto_Generic_Sign(session->session_id(), in_buffer.data(), diff --git a/oemcrypto/test/oec_key_deriver.cpp b/oemcrypto/test/oec_key_deriver.cpp index 7a24b35..21d5078 100644 --- a/oemcrypto/test/oec_key_deriver.cpp +++ b/oemcrypto/test/oec_key_deriver.cpp @@ -83,19 +83,23 @@ void Encryptor::PadAndEncryptProvisioningMessage( // This generates the data for deriving one key. If there are failures in // this function, then there is something wrong with the test program and its // dependency on BoringSSL. -void KeyDeriver::DeriveKey(const uint8_t* key, const vector& context, - int counter, vector* out) { +void KeyDeriver::DeriveKey(const uint8_t* key, size_t master_key_size, + const vector& context, int counter, + vector* out) { ASSERT_NE(key, nullptr); ASSERT_FALSE(context.empty()); ASSERT_GE(4, counter); ASSERT_LE(1, counter); ASSERT_NE(out, nullptr); + // For RSA, the master key is expected to be 16 bytes; for EC key, 32 bytes. + ASSERT_TRUE(master_key_size == KEY_SIZE || master_key_size == 2 * KEY_SIZE); - const EVP_CIPHER* cipher = EVP_aes_128_cbc(); + const EVP_CIPHER* cipher = + master_key_size == KEY_SIZE ? EVP_aes_128_cbc() : EVP_aes_256_cbc(); CMAC_CTX* cmac_ctx = CMAC_CTX_new(); ASSERT_NE(nullptr, cmac_ctx); - ASSERT_TRUE(CMAC_Init(cmac_ctx, key, KEY_SIZE, cipher, 0)); + ASSERT_TRUE(CMAC_Init(cmac_ctx, key, master_key_size, cipher, nullptr)); std::vector message; message.push_back(static_cast(counter)); @@ -114,24 +118,24 @@ void KeyDeriver::DeriveKey(const uint8_t* key, const vector& context, // This generates the data for deriving a set of keys. If there are failures in // this function, then there is something wrong with the test program and its // dependency on BoringSSL. -void KeyDeriver::DeriveKeys(const uint8_t* master_key, +void KeyDeriver::DeriveKeys(const uint8_t* master_key, size_t master_key_size, const vector& mac_key_context, const vector& enc_key_context) { // Generate derived key for mac key std::vector mac_key_part2; - DeriveKey(master_key, mac_key_context, 1, &mac_key_server_); - DeriveKey(master_key, mac_key_context, 2, &mac_key_part2); + DeriveKey(master_key, master_key_size, mac_key_context, 1, &mac_key_server_); + DeriveKey(master_key, master_key_size, mac_key_context, 2, &mac_key_part2); mac_key_server_.insert(mac_key_server_.end(), mac_key_part2.begin(), mac_key_part2.end()); - DeriveKey(master_key, mac_key_context, 3, &mac_key_client_); - DeriveKey(master_key, mac_key_context, 4, &mac_key_part2); + DeriveKey(master_key, master_key_size, mac_key_context, 3, &mac_key_client_); + DeriveKey(master_key, master_key_size, mac_key_context, 4, &mac_key_part2); mac_key_client_.insert(mac_key_client_.end(), mac_key_part2.begin(), mac_key_part2.end()); // Generate derived key for encryption key std::vector enc_key; - DeriveKey(master_key, enc_key_context, 1, &enc_key); + DeriveKey(master_key, master_key_size, enc_key_context, 1, &enc_key); set_enc_key(enc_key); } diff --git a/oemcrypto/test/oec_key_deriver.h b/oemcrypto/test/oec_key_deriver.h index 65b4ad7..4741da4 100644 --- a/oemcrypto/test/oec_key_deriver.h +++ b/oemcrypto/test/oec_key_deriver.h @@ -62,7 +62,7 @@ class KeyDeriver : public Encryptor { KeyDeriver& operator=(const KeyDeriver&) = default; // Generate mac and enc keys give the master key. - void DeriveKeys(const uint8_t* master_key, + void DeriveKeys(const uint8_t* master_key, size_t master_key_size, const std::vector& mac_key_context, const std::vector& enc_key_context); // Sign the buffer with server's mac key. @@ -80,9 +80,11 @@ class KeyDeriver : public Encryptor { void set_mac_keys(const uint8_t* mac_keys); private: - // Internal utility function to derive key using CMAC-128 - void DeriveKey(const uint8_t* key, const std::vector& context, - int counter, std::vector* out); + // Internal utility function to derive key using CMAC-128 or CMAC-256 based on + // master_key_size. + void DeriveKey(const uint8_t* key, size_t master_key_size, + const std::vector& context, int counter, + std::vector* out); std::vector mac_key_server_; std::vector mac_key_client_; diff --git a/oemcrypto/test/oec_session_util.cpp b/oemcrypto/test/oec_session_util.cpp index bd7dd21..b85d870 100644 --- a/oemcrypto/test/oec_session_util.cpp +++ b/oemcrypto/test/oec_session_util.cpp @@ -307,12 +307,12 @@ void ProvisioningRoundTrip::PrepareSession( OEMCrypto_BootCertificateChain) { // TODO(chelu): change this to CSR provisioning. session_->LoadOEMCert(true); - session_->GenerateRSASessionKey(&message_key_, &encrypted_message_key_); + session_->GenerateRsaSessionKey(&message_key_, &encrypted_message_key_); encryptor_.set_enc_key(message_key_); } else { EXPECT_EQ(global_features.provisioning_method, OEMCrypto_OEMCertificate); session_->LoadOEMCert(true); - session_->GenerateRSASessionKey(&message_key_, &encrypted_message_key_); + session_->GenerateRsaSessionKey(&message_key_, &encrypted_message_key_); encryptor_.set_enc_key(message_key_); } } @@ -321,7 +321,7 @@ void ProvisioningRoundTrip::VerifyRequestSignature( const vector& data, const vector& generated_signature, size_t /* core_message_length */) { if (global_features.provisioning_method == OEMCrypto_OEMCertificate) { - session()->VerifyRSASignature(data, generated_signature.data(), + session()->VerifyRsaSignature(data, generated_signature.data(), generated_signature.size(), kSign_RSASSA_PSS); } else { EXPECT_EQ(global_features.provisioning_method, OEMCrypto_Keybox); @@ -559,12 +559,12 @@ void LicenseRoundTrip::VerifyRequestSignature( if (global_features.api_version < 17) { const std::vector subdata(data.begin() + core_message_length, data.end()); - session()->VerifyRSASignature(subdata, generated_signature.data(), + session()->VerifyRsaSignature(subdata, generated_signature.data(), generated_signature.size(), kSign_RSASSA_PSS); SHA256(data.data(), core_message_length, request_hash_); } else { - session()->VerifyRSASignature(data, generated_signature.data(), - generated_signature.size(), kSign_RSASSA_PSS); + session()->VerifySignature(data, generated_signature.data(), + generated_signature.size(), kSign_RSASSA_PSS); SHA256(data.data(), core_message_length, request_hash_); } } @@ -749,9 +749,15 @@ void LicenseRoundTrip::FillCoreResponseSubstrings() { core_response_.key_array[i].key_data = FindSubstring(response_data_.keys[i].key_data, response_data_.keys[i].key_data_length); - core_response_.key_array[i].key_control_iv = - FindSubstring(response_data_.keys[i].control_iv, - sizeof(response_data_.keys[i].control_iv)); + if (core_request().api_major_version < kClearControlBlockAPIMajor || + (core_request().api_major_version == kClearControlBlockAPIMajor && + core_request().api_minor_version < kClearControlBlockAPIMinor)) { + core_response_.key_array[i].key_control_iv = + FindSubstring(response_data_.keys[i].control_iv, + sizeof(response_data_.keys[i].control_iv)); + } else { + core_response_.key_array[i].key_control_iv = FindSubstring(nullptr, 0); + } core_response_.key_array[i].key_control = FindSubstring(&response_data_.keys[i].control, sizeof(response_data_.keys[i].control)); @@ -1391,16 +1397,10 @@ OEMCryptoResult RenewalRoundTrip::LoadResponse(Session* session) { } } -Session::Session() - : open_(false), - forced_session_id_(false), - session_id_(0), - nonce_(0), - public_rsa_(0) {} +Session::Session() {} Session::~Session() { if (!forced_session_id_ && open_) close(); - if (public_rsa_) RSA_free(public_rsa_); } void Session::open() { @@ -1473,17 +1473,19 @@ void Session::GenerateDerivedKeysFromKeybox( OEMCrypto_GenerateDerivedKeys( session_id(), mac_context.data(), mac_context.size(), enc_context.data(), enc_context.size())); - key_deriver_.DeriveKeys(keybox.device_key_, mac_context, enc_context); + key_deriver_.DeriveKeys(keybox.device_key_, sizeof(keybox.device_key_), + mac_context, enc_context); } void Session::GenerateDerivedKeysFromSessionKey() { // Uses test certificate. vector session_key; vector enc_session_key; - ASSERT_NE(public_rsa_, nullptr) << "No public RSA key loaded in test code."; + ASSERT_TRUE(public_rsa_ || public_ec_) + << "No public RSA/ECC key loaded in test code"; // A failure here probably indicates that there is something wrong with the // test program and its dependency on BoringSSL. - ASSERT_TRUE(GenerateRSASessionKey(&session_key, &enc_session_key)); + ASSERT_TRUE(GenerateSessionKey(&session_key, &enc_session_key)); vector mac_context; vector enc_context; FillDefaultContext(&mac_context, &enc_context); @@ -1494,7 +1496,8 @@ void Session::GenerateDerivedKeysFromSessionKey() { mac_context.data(), mac_context.size(), enc_context.data(), enc_context.size())); - key_deriver_.DeriveKeys(session_key.data(), mac_context, enc_context); + key_deriver_.DeriveKeys(session_key.data(), session_key.size(), mac_context, + enc_context); } void Session::TestDecryptCTR(bool select_key_first, @@ -1593,7 +1596,7 @@ void Session::LoadOEMCert(bool verify_cert) { reinterpret_cast(public_cert.data()); long cert_size = static_cast(public_cert.size()); boringssl_ptr pkcs7( - d2i_PKCS7(NULL, &cert_data, cert_size)); + d2i_PKCS7(nullptr, &cert_data, cert_size)); ASSERT_TRUE(pkcs7.NotNull()) << "Error parsing PKCS7 message"; ASSERT_TRUE(PKCS7_type_is_signed(pkcs7.get())) << "Unexpected PKCS7 message type"; @@ -1607,12 +1610,11 @@ void Session::LoadOEMCert(bool verify_cert) { boringssl_ptr pubkey(X509_get_pubkey(x509_cert)); ASSERT_TRUE(pubkey.NotNull()); if (i == 0) { - public_rsa_ = EVP_PKEY_get1_RSA(pubkey.get()); - if (!public_rsa_) { - cerr << "d2i_RSAPrivateKey failed.\n"; - dump_boringssl_error(); - ASSERT_TRUE(nullptr != public_rsa_); - } + public_rsa_ = + util::RsaPublicKey::FromSslHandle(EVP_PKEY_get0_RSA(pubkey.get())); + ASSERT_TRUE(public_rsa_) + << "Failed to extract public RSA key from OEM certificate"; + return; } if (verify_cert) { vector buffer(80); @@ -1644,199 +1646,175 @@ void Session::LoadOEMCert(bool verify_cert) { } } -void Session::PreparePublicKey(const uint8_t* rsa_key, size_t rsa_key_length) { - if (rsa_key == nullptr) { - rsa_key = kTestRSAPKCS8PrivateKeyInfo2_2048; - rsa_key_length = sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048); - } - uint8_t* p = const_cast(rsa_key); - boringssl_ptr bio( - BIO_new_mem_buf(p, static_cast(rsa_key_length))); - ASSERT_TRUE(bio.NotNull()); - boringssl_ptr pkcs8_pki( - d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), nullptr)); - ASSERT_TRUE(pkcs8_pki.NotNull()); - boringssl_ptr evp(EVP_PKCS82PKEY(pkcs8_pki.get())); - ASSERT_TRUE(evp.NotNull()); - if (public_rsa_) RSA_free(public_rsa_); - public_rsa_ = EVP_PKEY_get1_RSA(evp.get()); - if (!public_rsa_) { - cerr << "d2i_RSAPrivateKey failed. "; - dump_boringssl_error(); - FAIL() << "Could not parse public RSA key."; - } - switch (RSA_check_key(public_rsa_)) { - case 1: // valid. +void Session::SetTestRsaPublicKey() { + public_ec_.reset(); + public_rsa_ = util::RsaPublicKey::LoadPrivateKeyInfo( + kTestRSAPKCS8PrivateKeyInfo2_2048, + sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048)); + ASSERT_TRUE(public_rsa_) << "Could not parse test RSA public key #2"; +} + +void Session::SetPublicKeyFromPrivateKeyInfo(OEMCrypto_PrivateKeyType key_type, + const uint8_t* buffer, + size_t length) { + switch (key_type) { + case OEMCrypto_RSA_Private_Key: + ASSERT_NO_FATAL_FAILURE( + SetRsaPublicKeyFromPrivateKeyInfo(buffer, length)); return; - case 0: // not valid. - dump_boringssl_error(); - FAIL() << "[rsa key not valid] "; - default: // -1 == check failed. - dump_boringssl_error(); - FAIL() << "[error checking rsa key] "; - } -} - -void Session::SetRsaPublicKey(const uint8_t* buffer, size_t length) { - if (public_rsa_) { - RSA_free(public_rsa_); - public_rsa_ = nullptr; - } - if (public_ec_) { - EC_KEY_free(public_ec_); - public_ec_ = nullptr; - } - public_rsa_ = d2i_RSA_PUBKEY(nullptr, &buffer, length); - if (!public_rsa_) { - cout << "d2i_RSAPrivateKey failed. "; - dump_boringssl_error(); - FAIL() << "Could not parse public RSA key."; - } - switch (RSA_check_key(public_rsa_)) { - case 1: // valid. + case OEMCrypto_ECC_Private_Key: + ASSERT_NO_FATAL_FAILURE( + SetEccPublicKeyFromPrivateKeyInfo(buffer, length)); return; - case 0: // not valid. - dump_boringssl_error(); - FAIL() << "[rsa key not valid] "; - default: // -1 == check failed. - dump_boringssl_error(); - FAIL() << "[error checking rsa key] "; } + FAIL() << "Unknown key type: " << static_cast(key_type); } -void Session::SetEcPublicKey(const uint8_t* buffer, size_t length) { - if (public_rsa_) { - RSA_free(public_rsa_); - public_rsa_ = nullptr; - } - if (public_ec_) { - EC_KEY_free(public_ec_); - public_ec_ = nullptr; - } - public_ec_ = d2i_EC_PUBKEY(nullptr, &buffer, length); - if (!public_ec_) { - cout << "d2i_RSAPrivateKey failed. "; - dump_boringssl_error(); - FAIL() << "Could not parse public RSA key."; - } - switch (EC_KEY_check_key(public_ec_)) { - case 1: // valid. +void Session::SetRsaPublicKeyFromPrivateKeyInfo(const uint8_t* buffer, + size_t length) { + public_ec_.reset(); + public_rsa_ = util::RsaPublicKey::LoadPrivateKeyInfo(buffer, length); + ASSERT_TRUE(public_rsa_) << "Could not parse RSA public key"; +} + +void Session::SetEccPublicKeyFromPrivateKeyInfo(const uint8_t* buffer, + size_t length) { + public_rsa_.reset(); + public_ec_ = util::EccPublicKey::LoadPrivateKeyInfo(buffer, length); + ASSERT_TRUE(public_ec_) << "Could not parse ECC public key"; +} + +void Session::SetPublicKeyFromSubjectPublicKey( + OEMCrypto_PrivateKeyType key_type, const uint8_t* buffer, size_t length) { + switch (key_type) { + case OEMCrypto_RSA_Private_Key: + ASSERT_NO_FATAL_FAILURE( + SetRsaPublicKeyFromSubjectPublicKey(buffer, length)); + return; + case OEMCrypto_ECC_Private_Key: + ASSERT_NO_FATAL_FAILURE( + SetEccPublicKeyFromSubjectPublicKey(buffer, length)); return; - case 0: // not valid. - default: - dump_boringssl_error(); - FAIL() << "[ec key not valid] "; } + FAIL() << "Unknown key type: " << static_cast(key_type); } -bool Session::VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message, - size_t message_length, - const uint8_t* signature, - size_t signature_length) { - boringssl_ptr md_ctx(EVP_MD_CTX_new()); - EVP_PKEY_CTX* pkey_ctx = nullptr; - - if (EVP_DigestVerifyInit(md_ctx.get(), &pkey_ctx, EVP_sha1(), - nullptr /* no ENGINE */, pkey) != 1) { - LOGE("EVP_DigestVerifyInit failed in VerifyPSSSignature"); - goto err; - } - - if (EVP_PKEY_CTX_set_signature_md(pkey_ctx, - const_cast(EVP_sha1())) != 1) { - LOGE("EVP_PKEY_CTX_set_signature_md failed in VerifyPSSSignature"); - goto err; - } - - if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) { - LOGE("EVP_PKEY_CTX_set_rsa_padding failed in VerifyPSSSignature"); - goto err; - } - - if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, SHA_DIGEST_LENGTH) != 1) { - LOGE("EVP_PKEY_CTX_set_rsa_pss_saltlen failed in VerifyPSSSignature"); - goto err; - } - - if (EVP_DigestVerifyUpdate(md_ctx.get(), message, message_length) != 1) { - LOGE("EVP_DigestVerifyUpdate failed in VerifyPSSSignature"); - goto err; - } - - if (EVP_DigestVerifyFinal(md_ctx.get(), const_cast(signature), - signature_length) != 1) { - LOGE( - "EVP_DigestVerifyFinal failed in VerifyPSSSignature. (Probably a bad " - "signature.)"); - goto err; - } - - return true; - -err: - dump_boringssl_error(); - return false; +void Session::SetRsaPublicKeyFromSubjectPublicKey(const uint8_t* buffer, + size_t length) { + public_ec_.reset(); + public_rsa_ = util::RsaPublicKey::Load(buffer, length); + ASSERT_TRUE(public_rsa_) << "Could not parse RSA public key"; } -void Session::VerifyRSASignature(const vector& message, +void Session::SetEccPublicKeyFromSubjectPublicKey(const uint8_t* buffer, + size_t length) { + public_rsa_.reset(); + public_ec_ = util::EccPublicKey::Load(buffer, length); + ASSERT_TRUE(public_ec_) << "Could not parse ECC public key"; +} + +void Session::VerifyRsaSignature(const vector& message, const uint8_t* signature, size_t signature_length, RSA_Padding_Scheme padding_scheme) { - ASSERT_NE(public_rsa_, nullptr) << "No public RSA key loaded in test code."; - - ASSERT_EQ(static_cast(RSA_size(public_rsa_)), signature_length) - << "Signature size is wrong. " << signature_length << ", should be " - << RSA_size(public_rsa_); - - if (padding_scheme == kSign_RSASSA_PSS) { - boringssl_ptr pkey(EVP_PKEY_new()); - ASSERT_EQ(1, EVP_PKEY_set1_RSA(pkey.get(), public_rsa_)); - - const bool ok = - VerifyPSSSignature(pkey.get(), message.data(), message.size(), - signature, signature_length); - EXPECT_TRUE(ok) << "PSS signature check failed."; - } else if (padding_scheme == kSign_PKCS1_Block1) { - vector padded_digest(signature_length); - int size; - // RSA_public_decrypt decrypts the signature, and then verifies that - // it was padded with RSA PKCS1 padding. - size = RSA_public_decrypt(static_cast(signature_length), signature, - padded_digest.data(), public_rsa_, - RSA_PKCS1_PADDING); - EXPECT_GT(size, 0); - padded_digest.resize(size); - EXPECT_EQ(message, padded_digest); - } else { - EXPECT_TRUE(false) << "Padding scheme not supported."; + ASSERT_TRUE(public_rsa_) << "No public RSA key loaded in test code"; + if (padding_scheme != kSign_RSASSA_PSS && + padding_scheme != kSign_PKCS1_Block1) { + FAIL() << "Padding scheme not supported: " << padding_scheme; + return; } + const util::RsaSignatureAlgorithm algorithm = + padding_scheme == kSign_RSASSA_PSS ? util::kRsaPssDefault + : util::kRsaPkcs1Cast; + const OEMCryptoResult result = public_rsa_->VerifySignature( + message.data(), message.size(), signature, signature_length, algorithm); + ASSERT_EQ(result, OEMCrypto_SUCCESS) << "RSA signature check failed"; } -bool Session::GenerateRSASessionKey(vector* session_key, +void Session::VerifyEccSignature(const vector& message, + const uint8_t* signature, + size_t signature_length) { + ASSERT_TRUE(public_ec_) << "No public ECC key loaded in test code"; + const OEMCryptoResult result = public_ec_->VerifySignature( + message.data(), message.size(), signature, signature_length); + ASSERT_EQ(result, OEMCrypto_SUCCESS) << "ECC signature check failed"; +} + +void Session::VerifySignature(const vector& message, + const uint8_t* signature, size_t signature_length, + RSA_Padding_Scheme padding_scheme) { + if (public_rsa_ != nullptr) { + return VerifyRsaSignature(message, signature, signature_length, + padding_scheme); + } else if (public_ec_ != nullptr) { + return VerifyEccSignature(message, signature, signature_length); + } + FAIL() << "No public RSA or ECC key loaded in test code"; +} + +bool Session::GenerateRsaSessionKey(vector* session_key, vector* enc_session_key) { if (!public_rsa_) { - cerr << "No public RSA key loaded in test code.\n"; + cerr << "No public RSA key loaded in test code\n"; return false; } *session_key = wvutil::a2b_hex("6fa479c731d2770b6a61a5d1420bb9d1"); - enc_session_key->assign(RSA_size(public_rsa_), 0); - int status = RSA_public_encrypt( - static_cast(session_key->size()), &(session_key->front()), - &(enc_session_key->front()), public_rsa_, RSA_PKCS1_OAEP_PADDING); - int size = static_cast(RSA_size(public_rsa_)); - if (status != size) { - cerr << "GenerateRSASessionKey error encrypting session key.\n"; - dump_boringssl_error(); + *enc_session_key = public_rsa_->EncryptSessionKey(*session_key); + if (enc_session_key->empty()) { return false; } return true; } -void Session::InstallRSASessionTestKey(const vector& wrapped_rsa_key) { +bool Session::GenerateEccSessionKey(vector* session_key, + vector* ecdh_public_key_data) { + if (!public_ec_) { + cerr << "No public ECC key loaded in test code\n"; + return false; + } + auto ephemeral_key = util::EccPrivateKey::New(public_ec_->curve()); + if (!ephemeral_key) { + return false; + } + *session_key = ephemeral_key->DeriveSessionKey(*public_ec_); + if (session_key->empty()) { + return false; + } + *ecdh_public_key_data = ephemeral_key->SerializeAsPublicKey(); + if (ecdh_public_key_data->empty()) { + session_key->clear(); + return false; + } + return true; +} + +bool Session::GenerateSessionKey(vector* session_key, + vector* key_material) { + if (public_rsa_ != nullptr) { + return GenerateRsaSessionKey(session_key, key_material); + } else if (public_ec_ != nullptr) { + return GenerateEccSessionKey(session_key, key_material); + } + cerr << "No public RSA or ECC key loaded in test code\n"; + return false; +} + +void Session::LoadWrappedDrmKey(OEMCrypto_PrivateKeyType key_type, + const vector& wrapped_drm_key) { ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_LoadDRMPrivateKey(session_id(), OEMCrypto_RSA_Private_Key, - wrapped_rsa_key.data(), - wrapped_rsa_key.size())); + OEMCrypto_LoadDRMPrivateKey(session_id(), key_type, + wrapped_drm_key.data(), + wrapped_drm_key.size())); +} + +void Session::LoadWrappedRsaDrmKey(const vector& wrapped_rsa_key) { + ASSERT_NO_FATAL_FAILURE( + LoadWrappedDrmKey(OEMCrypto_RSA_Private_Key, wrapped_rsa_key)); +} + +void Session::LoadWrappedEccDrmKey(const vector& wrapped_ecc_key) { + ASSERT_NO_FATAL_FAILURE( + LoadWrappedDrmKey(OEMCrypto_ECC_Private_Key, wrapped_ecc_key)); } void Session::CreateNewUsageEntry(OEMCryptoResult* status) { diff --git a/oemcrypto/test/oec_session_util.h b/oemcrypto/test/oec_session_util.h index 9587cde..ebe69ef 100644 --- a/oemcrypto/test/oec_session_util.h +++ b/oemcrypto/test/oec_session_util.h @@ -8,8 +8,6 @@ // OEMCrypto unit tests // #include -#include -#include #include #include @@ -20,7 +18,9 @@ #include "odk.h" #include "oec_device_features.h" #include "oec_key_deriver.h" +#include "oemcrypto_ecc_key.h" #include "oemcrypto_fuzz_structs.h" +#include "oemcrypto_rsa_key.h" #include "oemcrypto_types.h" #include "pst_report.h" @@ -213,7 +213,7 @@ class RoundTrip { } protected: - // Returns true if the a nonce should be generated before signing the request. + // Returns true if a nonce should be generated before signing the request. virtual bool RequestHasNonce() = 0; // ---------------------------------------------------------------------- @@ -558,38 +558,75 @@ class Session { void RewrapRSAKey(const struct RSAPrivateKeyMessage& encrypted, size_t message_size, const std::vector& signature, vector* wrapped_key, bool force); - // Loads the specified RSA public key into public_rsa_. If rsa_key is null, - // the default test key is loaded. - void PreparePublicKey(const uint8_t* rsa_key = nullptr, - size_t rsa_key_length = 0); + // Loads the default test RSA public key into public_rsa_. + void SetTestRsaPublicKey(); + // Loads the specified DRM public key into the appropriate key. + // The provided key is serialized as an ASN.1 DER encoded PrivateKeyInfo. + void SetPublicKeyFromPrivateKeyInfo(OEMCrypto_PrivateKeyType key_type, + const uint8_t* buffer, size_t length); // Loads the specified RSA public key into public_rsa_. - void SetRsaPublicKey(const uint8_t* buffer, size_t length); + // The provided key is serialized as an ASN.1 DER encoded PrivateKeyInfo. + void SetRsaPublicKeyFromPrivateKeyInfo(const uint8_t* buffer, size_t length); // Loads the specified EC public key into public_ec_. - void SetEcPublicKey(const uint8_t* buffer, size_t length); + // The provided key is serialized as an ASN.1 DER encoded PrivateKeyInfo. + void SetEccPublicKeyFromPrivateKeyInfo(const uint8_t* buffer, size_t length); + + // Loads the specified DRM public key into the appropriate key. + // The provided key is serialized as an ASN.1 DER encoded SubjectPublicKey. + void SetPublicKeyFromSubjectPublicKey(OEMCrypto_PrivateKeyType key_type, + const uint8_t* buffer, size_t length); + // Loads the specified RSA public key into public_rsa_. + // The provided key is serialized as an ASN.1 DER encoded SubjectPublicKey. + void SetRsaPublicKeyFromSubjectPublicKey(const uint8_t* buffer, + size_t length); + // Loads the specified EC public key into public_ec_. + // The provided key is serialized as an ASN.1 DER encoded SubjectPublicKey. + void SetEccPublicKeyFromSubjectPublicKey(const uint8_t* buffer, + size_t length); - // Verifies the given signature is from the given message and RSA key, pkey. - static bool VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message, - size_t message_length, - const uint8_t* signature, - size_t signature_length); // Verify that the message was signed by the private key associated with // |public_rsa_| using the specified padding scheme. - void VerifyRSASignature(const vector& message, + void VerifyRsaSignature(const vector& message, const uint8_t* signature, size_t signature_length, RSA_Padding_Scheme padding_scheme); + // Verify that the message was signed by the private key associated with + // |public_ecc_| using Widevine ECDSA. + void VerifyEccSignature(const vector& message, + const uint8_t* signature, size_t signature_length); + // Verify RSA or ECC signature based on the key type installed. The + // padding_scheme will be ignored in case of ECC key. + void VerifySignature(const vector& message, const uint8_t* signature, + size_t signature_length, + RSA_Padding_Scheme padding_scheme); + // Encrypts a known session key with public_rsa_ for use in future calls to // OEMCrypto_DeriveKeysFromSessionKey or OEMCrypto_RewrapDeviceRSAKey30. // The unencrypted session key is stored in session_key. - bool GenerateRSASessionKey(vector* session_key, + bool GenerateRsaSessionKey(vector* session_key, vector* enc_session_key); + // Derives a session key with public_ec_ and a ephemeral "server" ECC key + // for use in future calls to OEMCrypto_DeriveKeysFromSessionKey. + // The unencrypted session key is stored in session_key. + bool GenerateEccSessionKey(vector* session_key, + vector* ecdh_public_key_data); + // Based on the key type installed, call GenerateRsaSessionKey or + // GenerateEccSessionKey. + bool GenerateSessionKey(vector* session_key, + vector* key_material); + // Calls OEMCrypto_RewrapDeviceRSAKey30 with the given provisioning response // message. If force is true, we assert that the key loads successfully. void RewrapRSAKey30(const struct RSAPrivateKeyMessage& encrypted, const std::vector& encrypted_message_key, vector* wrapped_key, bool force); - // Loads the specified wrapped_rsa_key into OEMCrypto, and then runs - // GenerateDerivedKeysFromSessionKey to install known encryption and mac keys. - void InstallRSASessionTestKey(const vector& wrapped_rsa_key); + + void LoadWrappedDrmKey(OEMCrypto_PrivateKeyType key_type, + const vector& wrapped_drm_key); + // Loads the specified wrapped_rsa_key into OEMCrypto. + void LoadWrappedRsaDrmKey(const vector& wrapped_rsa_key); + // Loads the specified wrapped_ecc_key into OEMCrypto. + void LoadWrappedEccDrmKey(const vector& wrapped_ecc_key); + // Creates a new usage entry, and keeps track of the index. // If status is null, we expect success, otherwise status is set to the // return value. @@ -619,7 +656,7 @@ class Session { // order to verify signatures. void GenerateReport(const std::string& pst, OEMCryptoResult expected_result = OEMCrypto_SUCCESS, - Session* other = 0); + Session* other = nullptr); // Move this usage entry to a new index. void MoveUsageEntry(uint32_t new_index, std::vector* header_buffer, OEMCryptoResult expect_result = OEMCrypto_SUCCESS); @@ -663,21 +700,21 @@ class Session { OEMCryptoResult actual_select_result, OEMCryptoResult actual_decryt_result); - bool open_; - bool forced_session_id_; - OEMCrypto_SESSION session_id_; + bool open_ = false; + bool forced_session_id_ = false; + OEMCrypto_SESSION session_id_ = 0; KeyDeriver key_deriver_; - uint32_t nonce_; + uint32_t nonce_ = 0; // Only one of RSA or EC should be set. - RSA* public_rsa_ = nullptr; - EC_KEY* public_ec_ = nullptr; + std::unique_ptr public_rsa_; + std::unique_ptr public_ec_; vector pst_report_buffer_; - MessageData license_; + MessageData license_ = {}; vector encrypted_usage_entry_; - uint32_t usage_entry_number_; + uint32_t usage_entry_number_ = 0; string pst_; -}; +}; // class Session // Used for OEMCrypto Fuzzing: Convert byte to a valid boolean to avoid errors // generated by msan. diff --git a/oemcrypto/test/oec_test_data.h b/oemcrypto/test/oec_test_data.h index 453847b..d279217 100644 --- a/oemcrypto/test/oec_test_data.h +++ b/oemcrypto/test/oec_test_data.h @@ -12,7 +12,6 @@ #include "OEMCryptoCENC.h" #include "oemcrypto_types.h" - namespace wvoec { // This is a test keybox. It will not be accepted by production systems. By @@ -91,313 +90,262 @@ static const WidevineKeybox kQATestKeybox = { // A 2048 bit RSA key in PKCS#8 PrivateKeyInfo format // Used to verify the functions that manipulate RSA keys. static const uint8_t kTestRSAPKCS8PrivateKeyInfo2_2048[] = { - 0x30, 0x82, 0x04, 0xbc, 0x02, 0x01, 0x00, 0x30, - 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, - 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, - 0x04, 0xa6, 0x30, 0x82, 0x04, 0xa2, 0x02, 0x01, - 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, - 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, - 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, - 0x94, 0x58, 0xdd, 0xde, 0xa7, 0x1f, 0x3c, 0x2c, - 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, - 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, - 0x2a, 0xaa, 0x9d, 0xb4, 0x4e, 0xfa, 0xa7, 0x6a, - 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, - 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, - 0x28, 0xda, 0x3f, 0xce, 0x31, 0x7b, 0xfd, 0x06, - 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, - 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, - 0x29, 0xf2, 0x73, 0x9e, 0x39, 0xd8, 0xf6, 0x6f, - 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, - 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, - 0xcd, 0x9a, 0x13, 0x8b, 0x54, 0x73, 0x54, 0x25, - 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, - 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, - 0x98, 0x56, 0x57, 0x54, 0x71, 0xcd, 0x12, 0x7f, - 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, - 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, - 0xc9, 0x83, 0x06, 0x51, 0x5a, 0x88, 0x65, 0x13, - 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, - 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, - 0x2d, 0x5f, 0xf8, 0x12, 0x7f, 0xa2, 0xfc, 0xbb, - 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, - 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, - 0x82, 0x46, 0x0b, 0x3a, 0x77, 0x8f, 0xc0, 0x72, - 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, - 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, - 0xd3, 0x5b, 0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, - 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, - 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, - 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x00, 0x5e, - 0x79, 0x65, 0x49, 0xa5, 0x76, 0x79, 0xf9, 0x05, - 0x45, 0x0f, 0xf4, 0x03, 0xbd, 0xa4, 0x7d, 0x29, - 0xd5, 0xde, 0x33, 0x63, 0xd8, 0xb8, 0xac, 0x97, - 0xeb, 0x3f, 0x5e, 0x55, 0xe8, 0x7d, 0xf3, 0xe7, - 0x3b, 0x5c, 0x2d, 0x54, 0x67, 0x36, 0xd6, 0x1d, - 0x46, 0xf5, 0xca, 0x2d, 0x8b, 0x3a, 0x7e, 0xdc, - 0x45, 0x38, 0x79, 0x7e, 0x65, 0x71, 0x5f, 0x1c, - 0x5e, 0x79, 0xb1, 0x40, 0xcd, 0xfe, 0xc5, 0xe1, - 0xc1, 0x6b, 0x78, 0x04, 0x4e, 0x8e, 0x79, 0xf9, - 0x0a, 0xfc, 0x79, 0xb1, 0x5e, 0xb3, 0x60, 0xe3, - 0x68, 0x7b, 0xc6, 0xef, 0xcb, 0x71, 0x4c, 0xba, - 0xa7, 0x79, 0x5c, 0x7a, 0x81, 0xd1, 0x71, 0xe7, - 0x00, 0x21, 0x13, 0xe2, 0x55, 0x69, 0x0e, 0x75, - 0xbe, 0x09, 0xc3, 0x4f, 0xa9, 0xc9, 0x68, 0x22, - 0x0e, 0x97, 0x8d, 0x89, 0x6e, 0xf1, 0xe8, 0x88, - 0x7a, 0xd1, 0xd9, 0x09, 0x5d, 0xd3, 0x28, 0x78, - 0x25, 0x0b, 0x1c, 0x47, 0x73, 0x25, 0xcc, 0x21, - 0xb6, 0xda, 0xc6, 0x24, 0x5a, 0xd0, 0x37, 0x14, - 0x46, 0xc7, 0x94, 0x69, 0xe4, 0x43, 0x6f, 0x47, - 0xde, 0x00, 0x33, 0x4d, 0x8f, 0x95, 0x72, 0xfa, - 0x68, 0x71, 0x17, 0x66, 0x12, 0x1a, 0x87, 0x27, - 0xf7, 0xef, 0x7e, 0xe0, 0x35, 0x58, 0xf2, 0x4d, - 0x6f, 0x35, 0x01, 0xaa, 0x96, 0xe2, 0x3d, 0x51, - 0x13, 0x86, 0x9c, 0x79, 0xd0, 0xb7, 0xb6, 0x64, - 0xe8, 0x86, 0x65, 0x50, 0xbf, 0xcc, 0x27, 0x53, - 0x1f, 0x51, 0xd4, 0xca, 0xbe, 0xf5, 0xdd, 0x77, - 0x70, 0x98, 0x0f, 0xee, 0xa8, 0x96, 0x07, 0x5f, - 0x45, 0x6a, 0x7a, 0x0d, 0x03, 0x9c, 0x4f, 0x29, - 0xf6, 0x06, 0xf3, 0x5d, 0x58, 0x6c, 0x47, 0xd0, - 0x96, 0xa9, 0x03, 0x17, 0xbb, 0x4e, 0xc9, 0x21, - 0xe0, 0xac, 0xcd, 0x78, 0x78, 0xb2, 0xfe, 0x81, - 0xb2, 0x51, 0x53, 0xa6, 0x1f, 0x98, 0x45, 0x02, - 0x81, 0x81, 0x00, 0xcf, 0x73, 0x8c, 0xbe, 0x6d, - 0x45, 0x2d, 0x0c, 0x0b, 0x5d, 0x5c, 0x6c, 0x75, - 0x78, 0xcc, 0x35, 0x48, 0xb6, 0x98, 0xf1, 0xb9, - 0x64, 0x60, 0x8c, 0x43, 0xeb, 0x85, 0xab, 0x04, - 0xb6, 0x7d, 0x1b, 0x71, 0x75, 0x06, 0xe2, 0xda, - 0x84, 0x68, 0x2e, 0x7f, 0x4c, 0xe3, 0x73, 0xb4, - 0xde, 0x51, 0x4b, 0xb6, 0x51, 0x86, 0x7b, 0xd0, - 0xe6, 0x4d, 0xf3, 0xd1, 0xcf, 0x1a, 0xfe, 0x7f, - 0x3a, 0x83, 0xba, 0xb3, 0xe1, 0xff, 0x54, 0x13, - 0x93, 0xd7, 0x9c, 0x27, 0x80, 0xb7, 0x1e, 0x64, - 0x9e, 0xf7, 0x32, 0x2b, 0x46, 0x29, 0xf7, 0xf8, - 0x18, 0x6c, 0xf7, 0x4a, 0xbe, 0x4b, 0xee, 0x96, - 0x90, 0x8f, 0xa2, 0x16, 0x22, 0x6a, 0xcc, 0x48, - 0x06, 0x74, 0x63, 0x43, 0x7f, 0x27, 0x22, 0x44, - 0x3c, 0x2d, 0x3b, 0x62, 0xf1, 0x1c, 0xb4, 0x27, - 0x33, 0x85, 0x26, 0x60, 0x48, 0x16, 0xcb, 0xef, - 0xf8, 0xcd, 0x37, 0x02, 0x81, 0x81, 0x00, 0xce, - 0x15, 0x43, 0x6e, 0x4b, 0x0f, 0xf9, 0x3f, 0x87, - 0xc3, 0x41, 0x45, 0x97, 0xb1, 0x49, 0xc2, 0x19, - 0x23, 0x87, 0xe4, 0x24, 0x1c, 0x64, 0xe5, 0x28, - 0xcb, 0x43, 0x10, 0x14, 0x14, 0x0e, 0x19, 0xcb, - 0xbb, 0xdb, 0xfd, 0x11, 0x9d, 0x17, 0x68, 0x78, - 0x6d, 0x61, 0x70, 0x63, 0x3a, 0xa1, 0xb3, 0xf3, - 0xa7, 0x5b, 0x0e, 0xff, 0xb7, 0x61, 0x11, 0x54, - 0x91, 0x99, 0xe5, 0x91, 0x32, 0x2d, 0xeb, 0x3f, - 0xd8, 0x3e, 0xf7, 0xd4, 0xcb, 0xd2, 0xa3, 0x41, - 0xc1, 0xee, 0xc6, 0x92, 0x13, 0xeb, 0x7f, 0x42, - 0x58, 0xf4, 0xd0, 0xb2, 0x74, 0x1d, 0x8e, 0x87, - 0x46, 0xcd, 0x14, 0xb8, 0x16, 0xad, 0xb5, 0xbd, - 0x0d, 0x6c, 0x95, 0x5a, 0x16, 0xbf, 0xe9, 0x53, - 0xda, 0xfb, 0xed, 0x83, 0x51, 0x67, 0xa9, 0x55, - 0xab, 0x54, 0x02, 0x95, 0x20, 0xa6, 0x68, 0x17, - 0x53, 0xa8, 0xea, 0x43, 0xe5, 0xb0, 0xa3, 0x02, - 0x81, 0x80, 0x67, 0x9c, 0x32, 0x83, 0x39, 0x57, - 0xff, 0x73, 0xb0, 0x89, 0x64, 0x8b, 0xd6, 0xf0, - 0x0a, 0x2d, 0xe2, 0xaf, 0x30, 0x1c, 0x2a, 0x97, - 0xf3, 0x90, 0x9a, 0xab, 0x9b, 0x0b, 0x1b, 0x43, - 0x79, 0xa0, 0xa7, 0x3d, 0xe7, 0xbe, 0x8d, 0x9c, - 0xeb, 0xdb, 0xad, 0x40, 0xdd, 0xa9, 0x00, 0x80, - 0xb8, 0xe1, 0xb3, 0xa1, 0x6c, 0x25, 0x92, 0xe4, - 0x33, 0xb2, 0xbe, 0xeb, 0x4d, 0x74, 0x26, 0x5f, - 0x37, 0x43, 0x9c, 0x6c, 0x17, 0x76, 0x0a, 0x81, - 0x20, 0x82, 0xa1, 0x48, 0x2c, 0x2d, 0x45, 0xdc, - 0x0f, 0x62, 0x43, 0x32, 0xbb, 0xeb, 0x59, 0x41, - 0xf9, 0xca, 0x58, 0xce, 0x4a, 0x66, 0x53, 0x54, - 0xc8, 0x28, 0x10, 0x1e, 0x08, 0x71, 0x16, 0xd8, - 0x02, 0x71, 0x41, 0x58, 0xd4, 0x56, 0xcc, 0xf5, - 0xb1, 0x31, 0xa3, 0xed, 0x00, 0x85, 0x09, 0xbf, - 0x35, 0x95, 0x41, 0x29, 0x40, 0x19, 0x83, 0x35, - 0x24, 0x69, 0x02, 0x81, 0x80, 0x55, 0x10, 0x0b, - 0xcc, 0x3b, 0xa9, 0x75, 0x3d, 0x16, 0xe1, 0xae, - 0x50, 0x76, 0x63, 0x94, 0x49, 0x4c, 0xad, 0x10, - 0xcb, 0x47, 0x68, 0x7c, 0xf0, 0xe5, 0xdc, 0xb8, - 0x6a, 0xab, 0x8e, 0xf7, 0x9f, 0x08, 0x2c, 0x1b, - 0x8a, 0xa2, 0xb9, 0x8f, 0xce, 0xec, 0x5e, 0x61, - 0xa8, 0xcd, 0x1c, 0x87, 0x60, 0x4a, 0xc3, 0x1a, - 0x5f, 0xdf, 0x87, 0x26, 0xc6, 0xcb, 0x7c, 0x69, - 0xe4, 0x8b, 0x01, 0x06, 0x59, 0x22, 0xfa, 0x34, - 0x4b, 0x81, 0x87, 0x3c, 0x03, 0x6d, 0x02, 0x0a, - 0x77, 0xe6, 0x15, 0xd8, 0xcf, 0xa7, 0x68, 0x26, - 0x6c, 0xfa, 0x2b, 0xd9, 0x83, 0x5a, 0x2d, 0x0c, - 0x3b, 0x70, 0x1c, 0xd4, 0x48, 0xbe, 0xa7, 0x0a, - 0xd9, 0xbe, 0xdc, 0xc3, 0x0c, 0x21, 0x33, 0xb3, - 0x66, 0xff, 0x1c, 0x1b, 0xc8, 0x96, 0x76, 0xe8, - 0x6f, 0x44, 0x74, 0xbc, 0x9b, 0x1c, 0x7d, 0xc8, - 0xac, 0x21, 0xa8, 0x6e, 0x37, 0x02, 0x81, 0x80, - 0x2c, 0x7c, 0xad, 0x1e, 0x75, 0xf6, 0x69, 0x1d, - 0xe7, 0xa6, 0xca, 0x74, 0x7d, 0x67, 0xc8, 0x65, - 0x28, 0x66, 0xc4, 0x43, 0xa6, 0xbd, 0x40, 0x57, - 0xae, 0xb7, 0x65, 0x2c, 0x52, 0xf9, 0xe4, 0xc7, - 0x81, 0x7b, 0x56, 0xa3, 0xd2, 0x0d, 0xe8, 0x33, - 0x70, 0xcf, 0x06, 0x84, 0xb3, 0x4e, 0x44, 0x50, - 0x75, 0x61, 0x96, 0x86, 0x4b, 0xb6, 0x2b, 0xad, - 0xf0, 0xad, 0x57, 0xd0, 0x37, 0x0d, 0x1d, 0x35, - 0x50, 0xcb, 0x69, 0x22, 0x39, 0x29, 0xb9, 0x3a, - 0xd3, 0x29, 0x23, 0x02, 0x60, 0xf7, 0xab, 0x30, - 0x40, 0xda, 0x8e, 0x4d, 0x45, 0x70, 0x26, 0xf4, - 0xa2, 0x0d, 0xd0, 0x64, 0x5d, 0x47, 0x3c, 0x18, - 0xf4, 0xd4, 0x52, 0x95, 0x00, 0xae, 0x84, 0x6b, - 0x47, 0xb2, 0x3c, 0x82, 0xd3, 0x72, 0x53, 0xde, - 0x72, 0x2c, 0xf7, 0xc1, 0x22, 0x36, 0xd9, 0x18, - 0x56, 0xfe, 0x39, 0x28, 0x33, 0xe0, 0xdb, 0x03 }; + 0x30, 0x82, 0x04, 0xbc, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x04, 0xa6, 0x30, 0x82, 0x04, 0xa2, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, + 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, + 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd, 0xde, + 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, + 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d, 0xb4, + 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, + 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f, 0xce, + 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, + 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73, 0x9e, + 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, + 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13, 0x8b, + 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, + 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57, 0x54, + 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, + 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06, 0x51, + 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, + 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8, 0x12, + 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, + 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b, 0x3a, + 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, + 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d, 0xdb, + 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, + 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, + 0x82, 0x01, 0x00, 0x5e, 0x79, 0x65, 0x49, 0xa5, 0x76, 0x79, 0xf9, 0x05, + 0x45, 0x0f, 0xf4, 0x03, 0xbd, 0xa4, 0x7d, 0x29, 0xd5, 0xde, 0x33, 0x63, + 0xd8, 0xb8, 0xac, 0x97, 0xeb, 0x3f, 0x5e, 0x55, 0xe8, 0x7d, 0xf3, 0xe7, + 0x3b, 0x5c, 0x2d, 0x54, 0x67, 0x36, 0xd6, 0x1d, 0x46, 0xf5, 0xca, 0x2d, + 0x8b, 0x3a, 0x7e, 0xdc, 0x45, 0x38, 0x79, 0x7e, 0x65, 0x71, 0x5f, 0x1c, + 0x5e, 0x79, 0xb1, 0x40, 0xcd, 0xfe, 0xc5, 0xe1, 0xc1, 0x6b, 0x78, 0x04, + 0x4e, 0x8e, 0x79, 0xf9, 0x0a, 0xfc, 0x79, 0xb1, 0x5e, 0xb3, 0x60, 0xe3, + 0x68, 0x7b, 0xc6, 0xef, 0xcb, 0x71, 0x4c, 0xba, 0xa7, 0x79, 0x5c, 0x7a, + 0x81, 0xd1, 0x71, 0xe7, 0x00, 0x21, 0x13, 0xe2, 0x55, 0x69, 0x0e, 0x75, + 0xbe, 0x09, 0xc3, 0x4f, 0xa9, 0xc9, 0x68, 0x22, 0x0e, 0x97, 0x8d, 0x89, + 0x6e, 0xf1, 0xe8, 0x88, 0x7a, 0xd1, 0xd9, 0x09, 0x5d, 0xd3, 0x28, 0x78, + 0x25, 0x0b, 0x1c, 0x47, 0x73, 0x25, 0xcc, 0x21, 0xb6, 0xda, 0xc6, 0x24, + 0x5a, 0xd0, 0x37, 0x14, 0x46, 0xc7, 0x94, 0x69, 0xe4, 0x43, 0x6f, 0x47, + 0xde, 0x00, 0x33, 0x4d, 0x8f, 0x95, 0x72, 0xfa, 0x68, 0x71, 0x17, 0x66, + 0x12, 0x1a, 0x87, 0x27, 0xf7, 0xef, 0x7e, 0xe0, 0x35, 0x58, 0xf2, 0x4d, + 0x6f, 0x35, 0x01, 0xaa, 0x96, 0xe2, 0x3d, 0x51, 0x13, 0x86, 0x9c, 0x79, + 0xd0, 0xb7, 0xb6, 0x64, 0xe8, 0x86, 0x65, 0x50, 0xbf, 0xcc, 0x27, 0x53, + 0x1f, 0x51, 0xd4, 0xca, 0xbe, 0xf5, 0xdd, 0x77, 0x70, 0x98, 0x0f, 0xee, + 0xa8, 0x96, 0x07, 0x5f, 0x45, 0x6a, 0x7a, 0x0d, 0x03, 0x9c, 0x4f, 0x29, + 0xf6, 0x06, 0xf3, 0x5d, 0x58, 0x6c, 0x47, 0xd0, 0x96, 0xa9, 0x03, 0x17, + 0xbb, 0x4e, 0xc9, 0x21, 0xe0, 0xac, 0xcd, 0x78, 0x78, 0xb2, 0xfe, 0x81, + 0xb2, 0x51, 0x53, 0xa6, 0x1f, 0x98, 0x45, 0x02, 0x81, 0x81, 0x00, 0xcf, + 0x73, 0x8c, 0xbe, 0x6d, 0x45, 0x2d, 0x0c, 0x0b, 0x5d, 0x5c, 0x6c, 0x75, + 0x78, 0xcc, 0x35, 0x48, 0xb6, 0x98, 0xf1, 0xb9, 0x64, 0x60, 0x8c, 0x43, + 0xeb, 0x85, 0xab, 0x04, 0xb6, 0x7d, 0x1b, 0x71, 0x75, 0x06, 0xe2, 0xda, + 0x84, 0x68, 0x2e, 0x7f, 0x4c, 0xe3, 0x73, 0xb4, 0xde, 0x51, 0x4b, 0xb6, + 0x51, 0x86, 0x7b, 0xd0, 0xe6, 0x4d, 0xf3, 0xd1, 0xcf, 0x1a, 0xfe, 0x7f, + 0x3a, 0x83, 0xba, 0xb3, 0xe1, 0xff, 0x54, 0x13, 0x93, 0xd7, 0x9c, 0x27, + 0x80, 0xb7, 0x1e, 0x64, 0x9e, 0xf7, 0x32, 0x2b, 0x46, 0x29, 0xf7, 0xf8, + 0x18, 0x6c, 0xf7, 0x4a, 0xbe, 0x4b, 0xee, 0x96, 0x90, 0x8f, 0xa2, 0x16, + 0x22, 0x6a, 0xcc, 0x48, 0x06, 0x74, 0x63, 0x43, 0x7f, 0x27, 0x22, 0x44, + 0x3c, 0x2d, 0x3b, 0x62, 0xf1, 0x1c, 0xb4, 0x27, 0x33, 0x85, 0x26, 0x60, + 0x48, 0x16, 0xcb, 0xef, 0xf8, 0xcd, 0x37, 0x02, 0x81, 0x81, 0x00, 0xce, + 0x15, 0x43, 0x6e, 0x4b, 0x0f, 0xf9, 0x3f, 0x87, 0xc3, 0x41, 0x45, 0x97, + 0xb1, 0x49, 0xc2, 0x19, 0x23, 0x87, 0xe4, 0x24, 0x1c, 0x64, 0xe5, 0x28, + 0xcb, 0x43, 0x10, 0x14, 0x14, 0x0e, 0x19, 0xcb, 0xbb, 0xdb, 0xfd, 0x11, + 0x9d, 0x17, 0x68, 0x78, 0x6d, 0x61, 0x70, 0x63, 0x3a, 0xa1, 0xb3, 0xf3, + 0xa7, 0x5b, 0x0e, 0xff, 0xb7, 0x61, 0x11, 0x54, 0x91, 0x99, 0xe5, 0x91, + 0x32, 0x2d, 0xeb, 0x3f, 0xd8, 0x3e, 0xf7, 0xd4, 0xcb, 0xd2, 0xa3, 0x41, + 0xc1, 0xee, 0xc6, 0x92, 0x13, 0xeb, 0x7f, 0x42, 0x58, 0xf4, 0xd0, 0xb2, + 0x74, 0x1d, 0x8e, 0x87, 0x46, 0xcd, 0x14, 0xb8, 0x16, 0xad, 0xb5, 0xbd, + 0x0d, 0x6c, 0x95, 0x5a, 0x16, 0xbf, 0xe9, 0x53, 0xda, 0xfb, 0xed, 0x83, + 0x51, 0x67, 0xa9, 0x55, 0xab, 0x54, 0x02, 0x95, 0x20, 0xa6, 0x68, 0x17, + 0x53, 0xa8, 0xea, 0x43, 0xe5, 0xb0, 0xa3, 0x02, 0x81, 0x80, 0x67, 0x9c, + 0x32, 0x83, 0x39, 0x57, 0xff, 0x73, 0xb0, 0x89, 0x64, 0x8b, 0xd6, 0xf0, + 0x0a, 0x2d, 0xe2, 0xaf, 0x30, 0x1c, 0x2a, 0x97, 0xf3, 0x90, 0x9a, 0xab, + 0x9b, 0x0b, 0x1b, 0x43, 0x79, 0xa0, 0xa7, 0x3d, 0xe7, 0xbe, 0x8d, 0x9c, + 0xeb, 0xdb, 0xad, 0x40, 0xdd, 0xa9, 0x00, 0x80, 0xb8, 0xe1, 0xb3, 0xa1, + 0x6c, 0x25, 0x92, 0xe4, 0x33, 0xb2, 0xbe, 0xeb, 0x4d, 0x74, 0x26, 0x5f, + 0x37, 0x43, 0x9c, 0x6c, 0x17, 0x76, 0x0a, 0x81, 0x20, 0x82, 0xa1, 0x48, + 0x2c, 0x2d, 0x45, 0xdc, 0x0f, 0x62, 0x43, 0x32, 0xbb, 0xeb, 0x59, 0x41, + 0xf9, 0xca, 0x58, 0xce, 0x4a, 0x66, 0x53, 0x54, 0xc8, 0x28, 0x10, 0x1e, + 0x08, 0x71, 0x16, 0xd8, 0x02, 0x71, 0x41, 0x58, 0xd4, 0x56, 0xcc, 0xf5, + 0xb1, 0x31, 0xa3, 0xed, 0x00, 0x85, 0x09, 0xbf, 0x35, 0x95, 0x41, 0x29, + 0x40, 0x19, 0x83, 0x35, 0x24, 0x69, 0x02, 0x81, 0x80, 0x55, 0x10, 0x0b, + 0xcc, 0x3b, 0xa9, 0x75, 0x3d, 0x16, 0xe1, 0xae, 0x50, 0x76, 0x63, 0x94, + 0x49, 0x4c, 0xad, 0x10, 0xcb, 0x47, 0x68, 0x7c, 0xf0, 0xe5, 0xdc, 0xb8, + 0x6a, 0xab, 0x8e, 0xf7, 0x9f, 0x08, 0x2c, 0x1b, 0x8a, 0xa2, 0xb9, 0x8f, + 0xce, 0xec, 0x5e, 0x61, 0xa8, 0xcd, 0x1c, 0x87, 0x60, 0x4a, 0xc3, 0x1a, + 0x5f, 0xdf, 0x87, 0x26, 0xc6, 0xcb, 0x7c, 0x69, 0xe4, 0x8b, 0x01, 0x06, + 0x59, 0x22, 0xfa, 0x34, 0x4b, 0x81, 0x87, 0x3c, 0x03, 0x6d, 0x02, 0x0a, + 0x77, 0xe6, 0x15, 0xd8, 0xcf, 0xa7, 0x68, 0x26, 0x6c, 0xfa, 0x2b, 0xd9, + 0x83, 0x5a, 0x2d, 0x0c, 0x3b, 0x70, 0x1c, 0xd4, 0x48, 0xbe, 0xa7, 0x0a, + 0xd9, 0xbe, 0xdc, 0xc3, 0x0c, 0x21, 0x33, 0xb3, 0x66, 0xff, 0x1c, 0x1b, + 0xc8, 0x96, 0x76, 0xe8, 0x6f, 0x44, 0x74, 0xbc, 0x9b, 0x1c, 0x7d, 0xc8, + 0xac, 0x21, 0xa8, 0x6e, 0x37, 0x02, 0x81, 0x80, 0x2c, 0x7c, 0xad, 0x1e, + 0x75, 0xf6, 0x69, 0x1d, 0xe7, 0xa6, 0xca, 0x74, 0x7d, 0x67, 0xc8, 0x65, + 0x28, 0x66, 0xc4, 0x43, 0xa6, 0xbd, 0x40, 0x57, 0xae, 0xb7, 0x65, 0x2c, + 0x52, 0xf9, 0xe4, 0xc7, 0x81, 0x7b, 0x56, 0xa3, 0xd2, 0x0d, 0xe8, 0x33, + 0x70, 0xcf, 0x06, 0x84, 0xb3, 0x4e, 0x44, 0x50, 0x75, 0x61, 0x96, 0x86, + 0x4b, 0xb6, 0x2b, 0xad, 0xf0, 0xad, 0x57, 0xd0, 0x37, 0x0d, 0x1d, 0x35, + 0x50, 0xcb, 0x69, 0x22, 0x39, 0x29, 0xb9, 0x3a, 0xd3, 0x29, 0x23, 0x02, + 0x60, 0xf7, 0xab, 0x30, 0x40, 0xda, 0x8e, 0x4d, 0x45, 0x70, 0x26, 0xf4, + 0xa2, 0x0d, 0xd0, 0x64, 0x5d, 0x47, 0x3c, 0x18, 0xf4, 0xd4, 0x52, 0x95, + 0x00, 0xae, 0x84, 0x6b, 0x47, 0xb2, 0x3c, 0x82, 0xd3, 0x72, 0x53, 0xde, + 0x72, 0x2c, 0xf7, 0xc1, 0x22, 0x36, 0xd9, 0x18, 0x56, 0xfe, 0x39, 0x28, + 0x33, 0xe0, 0xdb, 0x03}; // A 3072 bit RSA key in PKCS#8 PrivateKeyInfo format // Used to verify the functions that manipulate RSA keys. static const uint8_t kTestRSAPKCS8PrivateKeyInfo3_3072[] = { - 0x30, 0x82, 0x06, 0xfe, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, - 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, - 0x06, 0xe8, 0x30, 0x82, 0x06, 0xe4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, - 0x81, 0x00, 0xe3, 0x32, 0x2f, 0x0a, 0x94, 0x06, 0x46, 0x99, 0x0a, 0x58, - 0xda, 0xd0, 0x9b, 0x2b, 0xe4, 0x2a, 0x35, 0xdf, 0xb7, 0x9b, 0x5e, 0xbf, - 0xfb, 0xe5, 0x24, 0x47, 0x5a, 0x94, 0x06, 0x04, 0xe5, 0x43, 0xed, 0x37, - 0x33, 0x94, 0x09, 0xd0, 0xae, 0xad, 0x86, 0xb4, 0xc7, 0xc3, 0x56, 0x6f, - 0x88, 0x74, 0xfb, 0xab, 0xf7, 0xcf, 0xcb, 0xa6, 0x89, 0x48, 0x4a, 0x02, - 0x89, 0xcd, 0xfd, 0x83, 0x3d, 0x2a, 0x27, 0xc1, 0xa2, 0x99, 0x8e, 0xef, - 0xcf, 0x91, 0xd3, 0xb2, 0x96, 0xe7, 0x5f, 0x0c, 0xb3, 0x44, 0x6a, 0xcf, - 0xc1, 0x22, 0xb9, 0xe4, 0xd4, 0xc0, 0xf2, 0xc3, 0x8d, 0xe1, 0x43, 0x38, - 0x31, 0x9c, 0x56, 0x04, 0xd4, 0x9d, 0x41, 0x02, 0x31, 0xce, 0x7e, 0xc0, - 0x11, 0x24, 0x54, 0xb1, 0xa2, 0x99, 0x0e, 0xe2, 0x0c, 0x5b, 0x24, 0x94, - 0x85, 0xe8, 0x8c, 0x30, 0xbb, 0x12, 0x94, 0x74, 0x0f, 0x67, 0xe5, 0x69, - 0xa4, 0xc4, 0x59, 0xd6, 0x77, 0x96, 0xae, 0xc6, 0x00, 0xbe, 0xf5, 0xe6, - 0x1f, 0x71, 0x90, 0x6d, 0xdd, 0xfb, 0x7b, 0x42, 0xd0, 0xdf, 0x4b, 0x58, - 0xaf, 0x9c, 0xba, 0xcb, 0x35, 0x4b, 0xf3, 0x06, 0x3a, 0x20, 0x42, 0x97, - 0x96, 0x95, 0x47, 0xbe, 0x2d, 0xeb, 0x9a, 0xb6, 0xea, 0xe0, 0xc1, 0x1d, - 0x80, 0x61, 0x3e, 0x8e, 0x18, 0x66, 0xf4, 0x26, 0x77, 0xcf, 0x56, 0x27, - 0x8b, 0xde, 0x93, 0x94, 0x3e, 0x1d, 0xe4, 0x5f, 0x6d, 0xf2, 0x39, 0x03, - 0x15, 0x4f, 0x2e, 0x58, 0x59, 0x75, 0x19, 0xb9, 0x24, 0x87, 0xd4, 0xff, - 0x64, 0x82, 0x11, 0x10, 0x34, 0x30, 0x09, 0x39, 0x43, 0x9c, 0xd2, 0x3b, - 0x45, 0xdc, 0x85, 0x4f, 0x6d, 0xb7, 0xbb, 0x49, 0xda, 0x3b, 0x07, 0xa2, - 0x76, 0x56, 0xa0, 0xee, 0xa9, 0xa9, 0x52, 0xb7, 0xf1, 0xfd, 0xde, 0xa1, - 0x6f, 0x0e, 0x7f, 0x82, 0x3f, 0x9e, 0x3d, 0x46, 0xcd, 0x48, 0x55, 0xe8, - 0x59, 0x65, 0xd8, 0xc7, 0xe4, 0x6b, 0xe6, 0xc0, 0xdd, 0x6e, 0x5c, 0xb7, - 0x0c, 0xdb, 0x29, 0xad, 0x8e, 0xa4, 0x86, 0xe9, 0x4d, 0xad, 0x54, 0xf9, - 0x56, 0x06, 0x0e, 0xc4, 0x2b, 0x01, 0xd9, 0x86, 0x1f, 0x65, 0xbe, 0x0d, - 0x77, 0x8d, 0x9d, 0xff, 0x37, 0x97, 0x57, 0xc3, 0x06, 0x8a, 0x05, 0x80, - 0x78, 0xd3, 0xbd, 0x91, 0xa5, 0xc1, 0x11, 0x4d, 0x99, 0x1a, 0x83, 0xd7, - 0x30, 0x1c, 0x24, 0xac, 0xdf, 0x6c, 0xc1, 0x23, 0x60, 0x76, 0x54, 0xbf, - 0x2b, 0xac, 0x34, 0xf0, 0x35, 0x92, 0x0d, 0x36, 0x29, 0x09, 0x24, 0xd5, - 0x54, 0xe9, 0x68, 0x9c, 0x90, 0x07, 0x16, 0x86, 0xb1, 0xd0, 0x9b, 0xa5, - 0x86, 0x4e, 0xce, 0xbf, 0x30, 0x9d, 0x91, 0xd7, 0xd2, 0xa6, 0x4f, 0xbb, - 0xbb, 0x9d, 0x7c, 0x0f, 0x58, 0xaa, 0xf1, 0xd0, 0x90, 0x66, 0x20, 0x48, - 0x8f, 0x29, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x81, 0x00, - 0x88, 0x41, 0x28, 0x85, 0x27, 0x91, 0x3b, 0xf5, 0xbc, 0x86, 0xdd, 0x74, - 0x0e, 0x1b, 0x9c, 0x92, 0xd4, 0x0c, 0x7f, 0x88, 0xe6, 0xa2, 0x2c, 0xe7, - 0x97, 0x82, 0x53, 0x88, 0x42, 0xb3, 0xdc, 0xeb, 0x87, 0xf0, 0x7b, 0x36, - 0x65, 0x4c, 0x89, 0xf5, 0xf7, 0xbb, 0xa3, 0xb2, 0x3a, 0xbc, 0x59, 0x12, - 0x0f, 0x7d, 0x6a, 0xf9, 0x6c, 0x21, 0x4c, 0x63, 0xd6, 0x3e, 0xff, 0x76, - 0x52, 0x7b, 0xca, 0xca, 0xe5, 0x5c, 0xf3, 0xaf, 0x34, 0x52, 0x0e, 0x22, - 0x5e, 0xdb, 0xd4, 0x34, 0x9e, 0x84, 0x77, 0x5e, 0xa8, 0xd0, 0x3f, 0xfc, - 0x1b, 0x90, 0x69, 0x27, 0xee, 0x6f, 0xe9, 0x3f, 0x17, 0x99, 0x33, 0xe7, - 0x96, 0x8e, 0xff, 0x13, 0xf0, 0x50, 0xe8, 0x9d, 0xf6, 0xd6, 0x29, 0x71, - 0xa8, 0x79, 0x80, 0x12, 0x5b, 0x22, 0xa6, 0x56, 0x62, 0xf1, 0xcf, 0xfd, - 0x4f, 0x56, 0x4a, 0x5b, 0x32, 0x3d, 0x08, 0xa0, 0x3e, 0xad, 0xc4, 0xeb, - 0x1d, 0x15, 0xca, 0x52, 0xcc, 0x2e, 0x63, 0x74, 0x22, 0xf5, 0x08, 0x16, - 0x8b, 0x8f, 0xd8, 0x79, 0x61, 0xcb, 0x08, 0x89, 0x62, 0x1e, 0xa5, 0xf3, - 0x50, 0xf3, 0x5d, 0xdb, 0x56, 0xbc, 0x7d, 0x4f, 0xab, 0xa0, 0x4d, 0xe6, - 0xe9, 0x47, 0xdd, 0x32, 0x57, 0x6f, 0x2c, 0x1d, 0xee, 0xb3, 0x4a, 0xb3, - 0x07, 0x59, 0x20, 0xb9, 0x5d, 0xe3, 0x54, 0x27, 0x3c, 0x7c, 0x2b, 0x1d, - 0x07, 0xff, 0x49, 0x93, 0xe2, 0xe3, 0xb2, 0x65, 0xf3, 0x69, 0xc1, 0x1c, - 0x2a, 0x75, 0x80, 0x16, 0x37, 0xe6, 0x00, 0x5b, 0xd3, 0x1b, 0xac, 0xca, - 0x8b, 0x8b, 0x98, 0x77, 0x81, 0x67, 0xe3, 0xdc, 0xbb, 0xc4, 0x3a, 0x45, - 0x15, 0xec, 0xd9, 0xad, 0xdb, 0x60, 0xcf, 0xe5, 0xd8, 0xd9, 0xfc, 0xcf, - 0xbe, 0x76, 0x2f, 0x5b, 0x60, 0xdb, 0x06, 0x62, 0x5b, 0x80, 0x7e, 0x53, - 0xde, 0x74, 0xb1, 0xa3, 0xb6, 0x9b, 0x14, 0xd7, 0x09, 0x65, 0x21, 0x1d, - 0xd5, 0xd3, 0x34, 0xca, 0x89, 0xe7, 0xbc, 0xf4, 0x48, 0x81, 0x6a, 0xcf, - 0x28, 0xbe, 0x74, 0x8b, 0x40, 0xad, 0x86, 0xcd, 0xa5, 0xd6, 0xfa, 0x64, - 0x9b, 0xd2, 0xd4, 0x17, 0x20, 0xd6, 0x0d, 0xbe, 0x95, 0xd4, 0xaf, 0xa5, - 0xde, 0x31, 0x0d, 0x6a, 0x90, 0xc6, 0xd0, 0x59, 0xd4, 0x8c, 0x81, 0x2d, - 0x9d, 0x09, 0xf1, 0x22, 0xf5, 0x30, 0x2d, 0xdf, 0x85, 0x54, 0x34, 0x8a, - 0xde, 0x3c, 0xce, 0xdb, 0x36, 0x9f, 0xcf, 0x12, 0x61, 0x0e, 0x99, 0x87, - 0x70, 0x51, 0x04, 0x91, 0x74, 0xc6, 0x88, 0x22, 0x75, 0x02, 0x8f, 0x7e, - 0xb5, 0x79, 0x48, 0x2f, 0xf3, 0x3b, 0xb8, 0x82, 0x3e, 0x7e, 0x45, 0xe5, - 0xb2, 0xc8, 0x4c, 0x12, 0x73, 0xb8, 0x92, 0x04, 0xd1, 0x9a, 0xae, 0xaa, - 0x08, 0xd9, 0x23, 0x54, 0x19, 0x46, 0xc8, 0x56, 0x5f, 0x5e, 0x10, 0xa1, - 0x02, 0x81, 0xc1, 0x00, 0xf6, 0x38, 0x88, 0x31, 0x06, 0x85, 0xd9, 0x00, - 0xf0, 0x6b, 0xd8, 0x7d, 0x76, 0x08, 0xc0, 0x69, 0x6a, 0xfb, 0xa4, 0xc8, - 0xdc, 0x6b, 0x00, 0xaf, 0xae, 0x52, 0x82, 0xe6, 0xba, 0xc9, 0x5e, 0xc9, - 0xb7, 0x7f, 0xa1, 0xc4, 0xcb, 0xa0, 0xbc, 0x66, 0x3c, 0x55, 0x6a, 0xea, - 0x6e, 0x42, 0xf1, 0x6b, 0xbd, 0xc4, 0xf2, 0x6b, 0x91, 0x11, 0x82, 0x20, - 0xc2, 0xe6, 0x9e, 0x96, 0x5c, 0x9a, 0x7e, 0xb3, 0x57, 0x45, 0x9c, 0x42, - 0x60, 0x4c, 0x04, 0x4f, 0x47, 0xfb, 0xa7, 0x68, 0x4e, 0x15, 0x43, 0x5a, - 0x97, 0xb3, 0xfc, 0xd2, 0x91, 0x3c, 0x11, 0x5e, 0xaf, 0x57, 0x2a, 0xa1, - 0x45, 0xa5, 0x60, 0xf0, 0xbe, 0x31, 0xe8, 0xc4, 0x0b, 0x35, 0xe3, 0x42, - 0x9b, 0x22, 0x6b, 0xa3, 0x6c, 0x49, 0x71, 0x20, 0x34, 0x3f, 0x46, 0x0b, - 0x79, 0xc9, 0xb8, 0xb4, 0xbd, 0x9c, 0xad, 0xd3, 0xd8, 0x7e, 0x95, 0x9f, - 0x9a, 0xd4, 0x03, 0xe9, 0x5a, 0x54, 0x46, 0x94, 0x39, 0x55, 0xf1, 0x28, - 0x0d, 0xd1, 0xaa, 0xc9, 0xf8, 0x28, 0x58, 0xef, 0xb0, 0x62, 0xb6, 0x2d, - 0xc7, 0xd2, 0x09, 0x3a, 0x21, 0x0f, 0x7d, 0xa1, 0xb9, 0x59, 0xd5, 0xa7, - 0x43, 0xa9, 0x51, 0xb7, 0xbf, 0x9d, 0xf3, 0x85, 0xec, 0xb3, 0xfb, 0x51, - 0x61, 0xca, 0x81, 0x4d, 0xfa, 0xf1, 0xc3, 0x94, 0x37, 0x45, 0x91, 0xf0, - 0x4b, 0xfc, 0x8e, 0xff, 0x02, 0x81, 0xc1, 0x00, 0xec, 0x38, 0x37, 0x3b, - 0xba, 0x1b, 0x83, 0xaf, 0x3a, 0x00, 0xb9, 0x5e, 0x1f, 0xc8, 0xad, 0x57, - 0xcf, 0x7c, 0xe2, 0x94, 0x95, 0xf1, 0xec, 0x0a, 0x4b, 0x40, 0xc4, 0x48, - 0xfb, 0x47, 0x5f, 0x66, 0xc6, 0xf0, 0x70, 0x14, 0xe9, 0x08, 0xe4, 0x50, - 0x29, 0x0a, 0x24, 0x57, 0x93, 0x97, 0x21, 0xd9, 0xfb, 0xc5, 0x52, 0x0a, - 0x38, 0xb9, 0x68, 0xa3, 0x4f, 0x4b, 0xf8, 0xb8, 0x24, 0xef, 0x0c, 0x42, - 0xda, 0x57, 0x32, 0x77, 0xed, 0x9c, 0x78, 0xeb, 0x10, 0x3e, 0x70, 0x67, - 0xe9, 0x01, 0x03, 0x19, 0x19, 0xdb, 0x48, 0x9e, 0x1e, 0x52, 0x23, 0x88, - 0xb6, 0x87, 0xb8, 0x0d, 0x2d, 0x0c, 0xfc, 0x90, 0x31, 0x9f, 0xa6, 0x96, - 0x0a, 0xe1, 0x34, 0x72, 0x86, 0x0e, 0x49, 0x7c, 0xfe, 0x21, 0xaa, 0x25, - 0xdd, 0x36, 0xbb, 0x1f, 0x85, 0xfe, 0x34, 0x18, 0xc2, 0x36, 0xa2, 0x7d, - 0xee, 0xd9, 0x4f, 0x8e, 0xcb, 0x49, 0x8e, 0x7a, 0x43, 0x3c, 0x52, 0x73, - 0x18, 0x60, 0xf6, 0xb7, 0x7a, 0xc4, 0x7a, 0x8a, 0x1c, 0xf0, 0xc9, 0x2e, - 0xad, 0x54, 0xb1, 0x7b, 0x8e, 0xcb, 0x4d, 0xc2, 0xbc, 0x2a, 0x72, 0xfe, - 0x61, 0x01, 0xd8, 0xff, 0x0a, 0x22, 0x6c, 0x51, 0x7e, 0x06, 0x9e, 0x9e, - 0x3c, 0xe8, 0x31, 0x98, 0xf5, 0x08, 0x34, 0x7e, 0xfa, 0x08, 0xd1, 0x14, - 0xdf, 0xfd, 0x26, 0x2f, 0x1f, 0x5a, 0x89, 0xd7, 0x02, 0x81, 0xc0, 0x76, - 0xdd, 0xed, 0xe9, 0xf5, 0x23, 0x33, 0x13, 0x3f, 0xfe, 0x60, 0xa2, 0x99, - 0x14, 0x3a, 0x87, 0xea, 0x0d, 0x18, 0x8d, 0x9b, 0xd3, 0xd0, 0x9d, 0xff, - 0xc3, 0x77, 0xcc, 0x9a, 0x0a, 0x53, 0x47, 0x80, 0xde, 0x0e, 0x23, 0xea, - 0xc6, 0x6b, 0x8d, 0xd3, 0xbc, 0xcd, 0x03, 0xe6, 0x3d, 0x4d, 0x3d, 0xdd, - 0x7c, 0xb2, 0x27, 0xf9, 0xfe, 0x00, 0xdb, 0x7e, 0x1c, 0x46, 0x1d, 0x83, - 0x11, 0x56, 0xef, 0x8f, 0xc7, 0x5c, 0x5b, 0xb3, 0x0f, 0x9f, 0xd9, 0x02, - 0x80, 0x5c, 0x5e, 0x7f, 0xab, 0xc6, 0x3b, 0x7b, 0x17, 0x7a, 0x8b, 0xd1, - 0x6f, 0xb5, 0x57, 0x07, 0xc1, 0x46, 0x24, 0x5b, 0x72, 0x2e, 0xad, 0xaa, - 0xb4, 0x7f, 0x91, 0xfd, 0x73, 0x83, 0x86, 0x89, 0x4c, 0x81, 0xb8, 0x80, - 0xb3, 0xa7, 0xf8, 0x8b, 0x20, 0xac, 0xd9, 0x27, 0x6f, 0x9a, 0x4b, 0x2f, - 0x6a, 0xef, 0x84, 0x61, 0x75, 0x23, 0x18, 0xcd, 0x6f, 0x63, 0x80, 0x09, - 0x8a, 0xbc, 0x14, 0x1c, 0xe5, 0xff, 0xa9, 0x7d, 0x9a, 0x66, 0x20, 0x61, - 0x3c, 0x61, 0x4b, 0x3d, 0xd5, 0x39, 0xec, 0x3a, 0x16, 0x8d, 0x3b, 0xd1, - 0xf0, 0x1f, 0x8f, 0xae, 0xe2, 0xce, 0xc1, 0x94, 0x69, 0xae, 0xb8, 0xcd, - 0xba, 0x1c, 0x71, 0xe0, 0x47, 0x37, 0xa2, 0x1f, 0x5a, 0xdb, 0x37, 0xe1, - 0x59, 0x4c, 0x39, 0x46, 0xc1, 0xc0, 0x65, 0xc8, 0xd9, 0x61, 0xd3, 0x02, - 0x81, 0xc0, 0x2f, 0x63, 0xe7, 0xd0, 0xd7, 0xb9, 0x85, 0x65, 0xb6, 0x21, - 0x47, 0x0f, 0x17, 0x19, 0x4f, 0x8d, 0x7a, 0x56, 0xf7, 0xae, 0x0f, 0x97, - 0x05, 0x5f, 0xdb, 0x51, 0x17, 0x0f, 0xfd, 0x39, 0x88, 0x6e, 0x3a, 0x23, - 0x2a, 0x99, 0x47, 0x57, 0x3d, 0x56, 0xc7, 0xa4, 0xfd, 0x3d, 0x84, 0xa2, - 0xa1, 0x6b, 0xf6, 0x12, 0xd4, 0x2e, 0xb0, 0xca, 0xa1, 0xaf, 0x81, 0xcd, - 0x20, 0x0c, 0xf1, 0x7b, 0xf3, 0xdd, 0xc5, 0xa8, 0x10, 0xbb, 0xf6, 0xb3, - 0x99, 0x9e, 0xaf, 0x17, 0x97, 0xbd, 0x81, 0x05, 0x6e, 0xf5, 0xae, 0x36, - 0x4c, 0x0f, 0x4c, 0xcd, 0xf5, 0xcb, 0x0b, 0xb3, 0x96, 0xbd, 0x2d, 0xf8, - 0x99, 0x02, 0xe4, 0xb1, 0xbe, 0xde, 0x03, 0x38, 0xc3, 0x28, 0xe6, 0xb4, - 0x1f, 0x12, 0x30, 0x79, 0xd8, 0x84, 0xd8, 0x28, 0x8e, 0xc9, 0xf8, 0x3b, - 0xd3, 0x7f, 0xd4, 0x16, 0xd9, 0xea, 0xa1, 0xec, 0x7f, 0x05, 0x8a, 0xcb, - 0x2b, 0x06, 0x64, 0x4e, 0xc9, 0xcb, 0xc5, 0x6c, 0x4e, 0x92, 0xe8, 0xd2, - 0x5a, 0x33, 0x33, 0x33, 0x2b, 0x69, 0x6d, 0xe4, 0xbb, 0xe6, 0xa9, 0xf3, - 0x27, 0x9a, 0x95, 0xdd, 0x7e, 0x4c, 0x82, 0x71, 0xb8, 0x73, 0x12, 0x39, - 0x6d, 0xb9, 0xbb, 0xaa, 0xe0, 0x4f, 0xa6, 0xb0, 0x7e, 0xa2, 0xcd, 0x25, - 0xe4, 0x42, 0x45, 0x2f, 0x57, 0xa2, 0xf4, 0x7c, 0xf9, 0x18, 0x23, 0x16, - 0x2a, 0xe9, 0x02, 0x81, 0xc1, 0x00, 0xab, 0x35, 0x0d, 0x35, 0x94, 0x9d, - 0x96, 0xb2, 0xb7, 0x45, 0x16, 0xef, 0xb7, 0xea, 0xba, 0xa4, 0x32, 0xec, - 0x43, 0x05, 0xb0, 0x14, 0xbd, 0x9e, 0xd2, 0xbe, 0x0a, 0x0c, 0x4f, 0xca, - 0x4f, 0xf3, 0x11, 0xb3, 0x1f, 0xdc, 0x04, 0x18, 0x38, 0x9d, 0xb0, 0x09, - 0xb8, 0xf1, 0xcf, 0x7a, 0x89, 0x03, 0xd8, 0xed, 0x28, 0x30, 0xe8, 0xe6, - 0xbc, 0x7c, 0x1c, 0x59, 0x12, 0xf8, 0x95, 0x9b, 0x36, 0xad, 0xf2, 0xea, - 0x4a, 0x34, 0x00, 0xcf, 0x94, 0x3e, 0xeb, 0xff, 0xe2, 0x5b, 0x6c, 0x72, - 0xe3, 0x04, 0xd1, 0x10, 0x2e, 0xdd, 0x18, 0x8d, 0x9a, 0x84, 0x93, 0x55, - 0x4a, 0x80, 0x6c, 0xb5, 0x82, 0xc4, 0x16, 0x19, 0xc4, 0xba, 0xad, 0x2e, - 0x40, 0x76, 0xb3, 0xc9, 0xd4, 0x26, 0x5d, 0xc9, 0xb1, 0x05, 0x0f, 0x1f, - 0x7d, 0x59, 0x8c, 0x7b, 0xbe, 0x34, 0x09, 0x3e, 0x71, 0x0b, 0xc8, 0xf9, - 0xb3, 0x77, 0x4e, 0x4b, 0xfb, 0xbf, 0x81, 0x55, 0xa4, 0x5e, 0xc6, 0xe9, - 0xa1, 0xc3, 0x16, 0xff, 0xc8, 0x37, 0x88, 0xd5, 0x2d, 0xfb, 0x06, 0x98, - 0xe9, 0x82, 0x1b, 0x5e, 0x1e, 0xdd, 0x48, 0x5d, 0x6c, 0x59, 0xee, 0x7a, - 0xa6, 0xa4, 0x29, 0x41, 0x20, 0xb4, 0xcd, 0xf4, 0x58, 0x95, 0xfd, 0x7d, - 0xbf, 0xfc, 0x83, 0xf5, 0xe1, 0x5a, 0x5d, 0xa8, 0x08, 0x66, 0xd8, 0xa0, - 0x7f, 0xad, 0x7d, 0xcd, 0x22, 0x06 -}; + 0x30, 0x82, 0x06, 0xfe, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x06, 0xe8, 0x30, 0x82, 0x06, 0xe4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, + 0x81, 0x00, 0xe3, 0x32, 0x2f, 0x0a, 0x94, 0x06, 0x46, 0x99, 0x0a, 0x58, + 0xda, 0xd0, 0x9b, 0x2b, 0xe4, 0x2a, 0x35, 0xdf, 0xb7, 0x9b, 0x5e, 0xbf, + 0xfb, 0xe5, 0x24, 0x47, 0x5a, 0x94, 0x06, 0x04, 0xe5, 0x43, 0xed, 0x37, + 0x33, 0x94, 0x09, 0xd0, 0xae, 0xad, 0x86, 0xb4, 0xc7, 0xc3, 0x56, 0x6f, + 0x88, 0x74, 0xfb, 0xab, 0xf7, 0xcf, 0xcb, 0xa6, 0x89, 0x48, 0x4a, 0x02, + 0x89, 0xcd, 0xfd, 0x83, 0x3d, 0x2a, 0x27, 0xc1, 0xa2, 0x99, 0x8e, 0xef, + 0xcf, 0x91, 0xd3, 0xb2, 0x96, 0xe7, 0x5f, 0x0c, 0xb3, 0x44, 0x6a, 0xcf, + 0xc1, 0x22, 0xb9, 0xe4, 0xd4, 0xc0, 0xf2, 0xc3, 0x8d, 0xe1, 0x43, 0x38, + 0x31, 0x9c, 0x56, 0x04, 0xd4, 0x9d, 0x41, 0x02, 0x31, 0xce, 0x7e, 0xc0, + 0x11, 0x24, 0x54, 0xb1, 0xa2, 0x99, 0x0e, 0xe2, 0x0c, 0x5b, 0x24, 0x94, + 0x85, 0xe8, 0x8c, 0x30, 0xbb, 0x12, 0x94, 0x74, 0x0f, 0x67, 0xe5, 0x69, + 0xa4, 0xc4, 0x59, 0xd6, 0x77, 0x96, 0xae, 0xc6, 0x00, 0xbe, 0xf5, 0xe6, + 0x1f, 0x71, 0x90, 0x6d, 0xdd, 0xfb, 0x7b, 0x42, 0xd0, 0xdf, 0x4b, 0x58, + 0xaf, 0x9c, 0xba, 0xcb, 0x35, 0x4b, 0xf3, 0x06, 0x3a, 0x20, 0x42, 0x97, + 0x96, 0x95, 0x47, 0xbe, 0x2d, 0xeb, 0x9a, 0xb6, 0xea, 0xe0, 0xc1, 0x1d, + 0x80, 0x61, 0x3e, 0x8e, 0x18, 0x66, 0xf4, 0x26, 0x77, 0xcf, 0x56, 0x27, + 0x8b, 0xde, 0x93, 0x94, 0x3e, 0x1d, 0xe4, 0x5f, 0x6d, 0xf2, 0x39, 0x03, + 0x15, 0x4f, 0x2e, 0x58, 0x59, 0x75, 0x19, 0xb9, 0x24, 0x87, 0xd4, 0xff, + 0x64, 0x82, 0x11, 0x10, 0x34, 0x30, 0x09, 0x39, 0x43, 0x9c, 0xd2, 0x3b, + 0x45, 0xdc, 0x85, 0x4f, 0x6d, 0xb7, 0xbb, 0x49, 0xda, 0x3b, 0x07, 0xa2, + 0x76, 0x56, 0xa0, 0xee, 0xa9, 0xa9, 0x52, 0xb7, 0xf1, 0xfd, 0xde, 0xa1, + 0x6f, 0x0e, 0x7f, 0x82, 0x3f, 0x9e, 0x3d, 0x46, 0xcd, 0x48, 0x55, 0xe8, + 0x59, 0x65, 0xd8, 0xc7, 0xe4, 0x6b, 0xe6, 0xc0, 0xdd, 0x6e, 0x5c, 0xb7, + 0x0c, 0xdb, 0x29, 0xad, 0x8e, 0xa4, 0x86, 0xe9, 0x4d, 0xad, 0x54, 0xf9, + 0x56, 0x06, 0x0e, 0xc4, 0x2b, 0x01, 0xd9, 0x86, 0x1f, 0x65, 0xbe, 0x0d, + 0x77, 0x8d, 0x9d, 0xff, 0x37, 0x97, 0x57, 0xc3, 0x06, 0x8a, 0x05, 0x80, + 0x78, 0xd3, 0xbd, 0x91, 0xa5, 0xc1, 0x11, 0x4d, 0x99, 0x1a, 0x83, 0xd7, + 0x30, 0x1c, 0x24, 0xac, 0xdf, 0x6c, 0xc1, 0x23, 0x60, 0x76, 0x54, 0xbf, + 0x2b, 0xac, 0x34, 0xf0, 0x35, 0x92, 0x0d, 0x36, 0x29, 0x09, 0x24, 0xd5, + 0x54, 0xe9, 0x68, 0x9c, 0x90, 0x07, 0x16, 0x86, 0xb1, 0xd0, 0x9b, 0xa5, + 0x86, 0x4e, 0xce, 0xbf, 0x30, 0x9d, 0x91, 0xd7, 0xd2, 0xa6, 0x4f, 0xbb, + 0xbb, 0x9d, 0x7c, 0x0f, 0x58, 0xaa, 0xf1, 0xd0, 0x90, 0x66, 0x20, 0x48, + 0x8f, 0x29, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x81, 0x00, + 0x88, 0x41, 0x28, 0x85, 0x27, 0x91, 0x3b, 0xf5, 0xbc, 0x86, 0xdd, 0x74, + 0x0e, 0x1b, 0x9c, 0x92, 0xd4, 0x0c, 0x7f, 0x88, 0xe6, 0xa2, 0x2c, 0xe7, + 0x97, 0x82, 0x53, 0x88, 0x42, 0xb3, 0xdc, 0xeb, 0x87, 0xf0, 0x7b, 0x36, + 0x65, 0x4c, 0x89, 0xf5, 0xf7, 0xbb, 0xa3, 0xb2, 0x3a, 0xbc, 0x59, 0x12, + 0x0f, 0x7d, 0x6a, 0xf9, 0x6c, 0x21, 0x4c, 0x63, 0xd6, 0x3e, 0xff, 0x76, + 0x52, 0x7b, 0xca, 0xca, 0xe5, 0x5c, 0xf3, 0xaf, 0x34, 0x52, 0x0e, 0x22, + 0x5e, 0xdb, 0xd4, 0x34, 0x9e, 0x84, 0x77, 0x5e, 0xa8, 0xd0, 0x3f, 0xfc, + 0x1b, 0x90, 0x69, 0x27, 0xee, 0x6f, 0xe9, 0x3f, 0x17, 0x99, 0x33, 0xe7, + 0x96, 0x8e, 0xff, 0x13, 0xf0, 0x50, 0xe8, 0x9d, 0xf6, 0xd6, 0x29, 0x71, + 0xa8, 0x79, 0x80, 0x12, 0x5b, 0x22, 0xa6, 0x56, 0x62, 0xf1, 0xcf, 0xfd, + 0x4f, 0x56, 0x4a, 0x5b, 0x32, 0x3d, 0x08, 0xa0, 0x3e, 0xad, 0xc4, 0xeb, + 0x1d, 0x15, 0xca, 0x52, 0xcc, 0x2e, 0x63, 0x74, 0x22, 0xf5, 0x08, 0x16, + 0x8b, 0x8f, 0xd8, 0x79, 0x61, 0xcb, 0x08, 0x89, 0x62, 0x1e, 0xa5, 0xf3, + 0x50, 0xf3, 0x5d, 0xdb, 0x56, 0xbc, 0x7d, 0x4f, 0xab, 0xa0, 0x4d, 0xe6, + 0xe9, 0x47, 0xdd, 0x32, 0x57, 0x6f, 0x2c, 0x1d, 0xee, 0xb3, 0x4a, 0xb3, + 0x07, 0x59, 0x20, 0xb9, 0x5d, 0xe3, 0x54, 0x27, 0x3c, 0x7c, 0x2b, 0x1d, + 0x07, 0xff, 0x49, 0x93, 0xe2, 0xe3, 0xb2, 0x65, 0xf3, 0x69, 0xc1, 0x1c, + 0x2a, 0x75, 0x80, 0x16, 0x37, 0xe6, 0x00, 0x5b, 0xd3, 0x1b, 0xac, 0xca, + 0x8b, 0x8b, 0x98, 0x77, 0x81, 0x67, 0xe3, 0xdc, 0xbb, 0xc4, 0x3a, 0x45, + 0x15, 0xec, 0xd9, 0xad, 0xdb, 0x60, 0xcf, 0xe5, 0xd8, 0xd9, 0xfc, 0xcf, + 0xbe, 0x76, 0x2f, 0x5b, 0x60, 0xdb, 0x06, 0x62, 0x5b, 0x80, 0x7e, 0x53, + 0xde, 0x74, 0xb1, 0xa3, 0xb6, 0x9b, 0x14, 0xd7, 0x09, 0x65, 0x21, 0x1d, + 0xd5, 0xd3, 0x34, 0xca, 0x89, 0xe7, 0xbc, 0xf4, 0x48, 0x81, 0x6a, 0xcf, + 0x28, 0xbe, 0x74, 0x8b, 0x40, 0xad, 0x86, 0xcd, 0xa5, 0xd6, 0xfa, 0x64, + 0x9b, 0xd2, 0xd4, 0x17, 0x20, 0xd6, 0x0d, 0xbe, 0x95, 0xd4, 0xaf, 0xa5, + 0xde, 0x31, 0x0d, 0x6a, 0x90, 0xc6, 0xd0, 0x59, 0xd4, 0x8c, 0x81, 0x2d, + 0x9d, 0x09, 0xf1, 0x22, 0xf5, 0x30, 0x2d, 0xdf, 0x85, 0x54, 0x34, 0x8a, + 0xde, 0x3c, 0xce, 0xdb, 0x36, 0x9f, 0xcf, 0x12, 0x61, 0x0e, 0x99, 0x87, + 0x70, 0x51, 0x04, 0x91, 0x74, 0xc6, 0x88, 0x22, 0x75, 0x02, 0x8f, 0x7e, + 0xb5, 0x79, 0x48, 0x2f, 0xf3, 0x3b, 0xb8, 0x82, 0x3e, 0x7e, 0x45, 0xe5, + 0xb2, 0xc8, 0x4c, 0x12, 0x73, 0xb8, 0x92, 0x04, 0xd1, 0x9a, 0xae, 0xaa, + 0x08, 0xd9, 0x23, 0x54, 0x19, 0x46, 0xc8, 0x56, 0x5f, 0x5e, 0x10, 0xa1, + 0x02, 0x81, 0xc1, 0x00, 0xf6, 0x38, 0x88, 0x31, 0x06, 0x85, 0xd9, 0x00, + 0xf0, 0x6b, 0xd8, 0x7d, 0x76, 0x08, 0xc0, 0x69, 0x6a, 0xfb, 0xa4, 0xc8, + 0xdc, 0x6b, 0x00, 0xaf, 0xae, 0x52, 0x82, 0xe6, 0xba, 0xc9, 0x5e, 0xc9, + 0xb7, 0x7f, 0xa1, 0xc4, 0xcb, 0xa0, 0xbc, 0x66, 0x3c, 0x55, 0x6a, 0xea, + 0x6e, 0x42, 0xf1, 0x6b, 0xbd, 0xc4, 0xf2, 0x6b, 0x91, 0x11, 0x82, 0x20, + 0xc2, 0xe6, 0x9e, 0x96, 0x5c, 0x9a, 0x7e, 0xb3, 0x57, 0x45, 0x9c, 0x42, + 0x60, 0x4c, 0x04, 0x4f, 0x47, 0xfb, 0xa7, 0x68, 0x4e, 0x15, 0x43, 0x5a, + 0x97, 0xb3, 0xfc, 0xd2, 0x91, 0x3c, 0x11, 0x5e, 0xaf, 0x57, 0x2a, 0xa1, + 0x45, 0xa5, 0x60, 0xf0, 0xbe, 0x31, 0xe8, 0xc4, 0x0b, 0x35, 0xe3, 0x42, + 0x9b, 0x22, 0x6b, 0xa3, 0x6c, 0x49, 0x71, 0x20, 0x34, 0x3f, 0x46, 0x0b, + 0x79, 0xc9, 0xb8, 0xb4, 0xbd, 0x9c, 0xad, 0xd3, 0xd8, 0x7e, 0x95, 0x9f, + 0x9a, 0xd4, 0x03, 0xe9, 0x5a, 0x54, 0x46, 0x94, 0x39, 0x55, 0xf1, 0x28, + 0x0d, 0xd1, 0xaa, 0xc9, 0xf8, 0x28, 0x58, 0xef, 0xb0, 0x62, 0xb6, 0x2d, + 0xc7, 0xd2, 0x09, 0x3a, 0x21, 0x0f, 0x7d, 0xa1, 0xb9, 0x59, 0xd5, 0xa7, + 0x43, 0xa9, 0x51, 0xb7, 0xbf, 0x9d, 0xf3, 0x85, 0xec, 0xb3, 0xfb, 0x51, + 0x61, 0xca, 0x81, 0x4d, 0xfa, 0xf1, 0xc3, 0x94, 0x37, 0x45, 0x91, 0xf0, + 0x4b, 0xfc, 0x8e, 0xff, 0x02, 0x81, 0xc1, 0x00, 0xec, 0x38, 0x37, 0x3b, + 0xba, 0x1b, 0x83, 0xaf, 0x3a, 0x00, 0xb9, 0x5e, 0x1f, 0xc8, 0xad, 0x57, + 0xcf, 0x7c, 0xe2, 0x94, 0x95, 0xf1, 0xec, 0x0a, 0x4b, 0x40, 0xc4, 0x48, + 0xfb, 0x47, 0x5f, 0x66, 0xc6, 0xf0, 0x70, 0x14, 0xe9, 0x08, 0xe4, 0x50, + 0x29, 0x0a, 0x24, 0x57, 0x93, 0x97, 0x21, 0xd9, 0xfb, 0xc5, 0x52, 0x0a, + 0x38, 0xb9, 0x68, 0xa3, 0x4f, 0x4b, 0xf8, 0xb8, 0x24, 0xef, 0x0c, 0x42, + 0xda, 0x57, 0x32, 0x77, 0xed, 0x9c, 0x78, 0xeb, 0x10, 0x3e, 0x70, 0x67, + 0xe9, 0x01, 0x03, 0x19, 0x19, 0xdb, 0x48, 0x9e, 0x1e, 0x52, 0x23, 0x88, + 0xb6, 0x87, 0xb8, 0x0d, 0x2d, 0x0c, 0xfc, 0x90, 0x31, 0x9f, 0xa6, 0x96, + 0x0a, 0xe1, 0x34, 0x72, 0x86, 0x0e, 0x49, 0x7c, 0xfe, 0x21, 0xaa, 0x25, + 0xdd, 0x36, 0xbb, 0x1f, 0x85, 0xfe, 0x34, 0x18, 0xc2, 0x36, 0xa2, 0x7d, + 0xee, 0xd9, 0x4f, 0x8e, 0xcb, 0x49, 0x8e, 0x7a, 0x43, 0x3c, 0x52, 0x73, + 0x18, 0x60, 0xf6, 0xb7, 0x7a, 0xc4, 0x7a, 0x8a, 0x1c, 0xf0, 0xc9, 0x2e, + 0xad, 0x54, 0xb1, 0x7b, 0x8e, 0xcb, 0x4d, 0xc2, 0xbc, 0x2a, 0x72, 0xfe, + 0x61, 0x01, 0xd8, 0xff, 0x0a, 0x22, 0x6c, 0x51, 0x7e, 0x06, 0x9e, 0x9e, + 0x3c, 0xe8, 0x31, 0x98, 0xf5, 0x08, 0x34, 0x7e, 0xfa, 0x08, 0xd1, 0x14, + 0xdf, 0xfd, 0x26, 0x2f, 0x1f, 0x5a, 0x89, 0xd7, 0x02, 0x81, 0xc0, 0x76, + 0xdd, 0xed, 0xe9, 0xf5, 0x23, 0x33, 0x13, 0x3f, 0xfe, 0x60, 0xa2, 0x99, + 0x14, 0x3a, 0x87, 0xea, 0x0d, 0x18, 0x8d, 0x9b, 0xd3, 0xd0, 0x9d, 0xff, + 0xc3, 0x77, 0xcc, 0x9a, 0x0a, 0x53, 0x47, 0x80, 0xde, 0x0e, 0x23, 0xea, + 0xc6, 0x6b, 0x8d, 0xd3, 0xbc, 0xcd, 0x03, 0xe6, 0x3d, 0x4d, 0x3d, 0xdd, + 0x7c, 0xb2, 0x27, 0xf9, 0xfe, 0x00, 0xdb, 0x7e, 0x1c, 0x46, 0x1d, 0x83, + 0x11, 0x56, 0xef, 0x8f, 0xc7, 0x5c, 0x5b, 0xb3, 0x0f, 0x9f, 0xd9, 0x02, + 0x80, 0x5c, 0x5e, 0x7f, 0xab, 0xc6, 0x3b, 0x7b, 0x17, 0x7a, 0x8b, 0xd1, + 0x6f, 0xb5, 0x57, 0x07, 0xc1, 0x46, 0x24, 0x5b, 0x72, 0x2e, 0xad, 0xaa, + 0xb4, 0x7f, 0x91, 0xfd, 0x73, 0x83, 0x86, 0x89, 0x4c, 0x81, 0xb8, 0x80, + 0xb3, 0xa7, 0xf8, 0x8b, 0x20, 0xac, 0xd9, 0x27, 0x6f, 0x9a, 0x4b, 0x2f, + 0x6a, 0xef, 0x84, 0x61, 0x75, 0x23, 0x18, 0xcd, 0x6f, 0x63, 0x80, 0x09, + 0x8a, 0xbc, 0x14, 0x1c, 0xe5, 0xff, 0xa9, 0x7d, 0x9a, 0x66, 0x20, 0x61, + 0x3c, 0x61, 0x4b, 0x3d, 0xd5, 0x39, 0xec, 0x3a, 0x16, 0x8d, 0x3b, 0xd1, + 0xf0, 0x1f, 0x8f, 0xae, 0xe2, 0xce, 0xc1, 0x94, 0x69, 0xae, 0xb8, 0xcd, + 0xba, 0x1c, 0x71, 0xe0, 0x47, 0x37, 0xa2, 0x1f, 0x5a, 0xdb, 0x37, 0xe1, + 0x59, 0x4c, 0x39, 0x46, 0xc1, 0xc0, 0x65, 0xc8, 0xd9, 0x61, 0xd3, 0x02, + 0x81, 0xc0, 0x2f, 0x63, 0xe7, 0xd0, 0xd7, 0xb9, 0x85, 0x65, 0xb6, 0x21, + 0x47, 0x0f, 0x17, 0x19, 0x4f, 0x8d, 0x7a, 0x56, 0xf7, 0xae, 0x0f, 0x97, + 0x05, 0x5f, 0xdb, 0x51, 0x17, 0x0f, 0xfd, 0x39, 0x88, 0x6e, 0x3a, 0x23, + 0x2a, 0x99, 0x47, 0x57, 0x3d, 0x56, 0xc7, 0xa4, 0xfd, 0x3d, 0x84, 0xa2, + 0xa1, 0x6b, 0xf6, 0x12, 0xd4, 0x2e, 0xb0, 0xca, 0xa1, 0xaf, 0x81, 0xcd, + 0x20, 0x0c, 0xf1, 0x7b, 0xf3, 0xdd, 0xc5, 0xa8, 0x10, 0xbb, 0xf6, 0xb3, + 0x99, 0x9e, 0xaf, 0x17, 0x97, 0xbd, 0x81, 0x05, 0x6e, 0xf5, 0xae, 0x36, + 0x4c, 0x0f, 0x4c, 0xcd, 0xf5, 0xcb, 0x0b, 0xb3, 0x96, 0xbd, 0x2d, 0xf8, + 0x99, 0x02, 0xe4, 0xb1, 0xbe, 0xde, 0x03, 0x38, 0xc3, 0x28, 0xe6, 0xb4, + 0x1f, 0x12, 0x30, 0x79, 0xd8, 0x84, 0xd8, 0x28, 0x8e, 0xc9, 0xf8, 0x3b, + 0xd3, 0x7f, 0xd4, 0x16, 0xd9, 0xea, 0xa1, 0xec, 0x7f, 0x05, 0x8a, 0xcb, + 0x2b, 0x06, 0x64, 0x4e, 0xc9, 0xcb, 0xc5, 0x6c, 0x4e, 0x92, 0xe8, 0xd2, + 0x5a, 0x33, 0x33, 0x33, 0x2b, 0x69, 0x6d, 0xe4, 0xbb, 0xe6, 0xa9, 0xf3, + 0x27, 0x9a, 0x95, 0xdd, 0x7e, 0x4c, 0x82, 0x71, 0xb8, 0x73, 0x12, 0x39, + 0x6d, 0xb9, 0xbb, 0xaa, 0xe0, 0x4f, 0xa6, 0xb0, 0x7e, 0xa2, 0xcd, 0x25, + 0xe4, 0x42, 0x45, 0x2f, 0x57, 0xa2, 0xf4, 0x7c, 0xf9, 0x18, 0x23, 0x16, + 0x2a, 0xe9, 0x02, 0x81, 0xc1, 0x00, 0xab, 0x35, 0x0d, 0x35, 0x94, 0x9d, + 0x96, 0xb2, 0xb7, 0x45, 0x16, 0xef, 0xb7, 0xea, 0xba, 0xa4, 0x32, 0xec, + 0x43, 0x05, 0xb0, 0x14, 0xbd, 0x9e, 0xd2, 0xbe, 0x0a, 0x0c, 0x4f, 0xca, + 0x4f, 0xf3, 0x11, 0xb3, 0x1f, 0xdc, 0x04, 0x18, 0x38, 0x9d, 0xb0, 0x09, + 0xb8, 0xf1, 0xcf, 0x7a, 0x89, 0x03, 0xd8, 0xed, 0x28, 0x30, 0xe8, 0xe6, + 0xbc, 0x7c, 0x1c, 0x59, 0x12, 0xf8, 0x95, 0x9b, 0x36, 0xad, 0xf2, 0xea, + 0x4a, 0x34, 0x00, 0xcf, 0x94, 0x3e, 0xeb, 0xff, 0xe2, 0x5b, 0x6c, 0x72, + 0xe3, 0x04, 0xd1, 0x10, 0x2e, 0xdd, 0x18, 0x8d, 0x9a, 0x84, 0x93, 0x55, + 0x4a, 0x80, 0x6c, 0xb5, 0x82, 0xc4, 0x16, 0x19, 0xc4, 0xba, 0xad, 0x2e, + 0x40, 0x76, 0xb3, 0xc9, 0xd4, 0x26, 0x5d, 0xc9, 0xb1, 0x05, 0x0f, 0x1f, + 0x7d, 0x59, 0x8c, 0x7b, 0xbe, 0x34, 0x09, 0x3e, 0x71, 0x0b, 0xc8, 0xf9, + 0xb3, 0x77, 0x4e, 0x4b, 0xfb, 0xbf, 0x81, 0x55, 0xa4, 0x5e, 0xc6, 0xe9, + 0xa1, 0xc3, 0x16, 0xff, 0xc8, 0x37, 0x88, 0xd5, 0x2d, 0xfb, 0x06, 0x98, + 0xe9, 0x82, 0x1b, 0x5e, 0x1e, 0xdd, 0x48, 0x5d, 0x6c, 0x59, 0xee, 0x7a, + 0xa6, 0xa4, 0x29, 0x41, 0x20, 0xb4, 0xcd, 0xf4, 0x58, 0x95, 0xfd, 0x7d, + 0xbf, 0xfc, 0x83, 0xf5, 0xe1, 0x5a, 0x5d, 0xa8, 0x08, 0x66, 0xd8, 0xa0, + 0x7f, 0xad, 0x7d, 0xcd, 0x22, 0x06}; // A 2048 bit RSA key in PKCS#8 PrivateKeyInfo format. // This is a different key from the one above. @@ -719,7 +667,844 @@ static const uint8_t kTestKeyRSAEuler_2048[] = { 0x33, 0xe0, 0xdb, 0x03, }; -static const uint8_t kTestSandbox[] = { 0x01, 0x02, 0x03 }; +// 8 randomly generated RSA private keys for testing the max number of DRM keys +// that can be loaded. +static const std::vector> kTestRSAPKCS8PrivateKeys_2048 = { + { + 0x30, 0x82, 0x04, 0xbd, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x04, 0xa7, 0x30, 0x82, 0x04, 0xa3, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, + 0x01, 0x00, 0xe3, 0x1a, 0x35, 0xd2, 0x41, 0x27, 0x76, 0xbe, 0x6a, 0xbb, + 0x87, 0xb1, 0x63, 0x4a, 0x8f, 0x83, 0x00, 0x37, 0x62, 0x8d, 0x21, 0xef, + 0xb2, 0x6d, 0xab, 0x94, 0xa4, 0x91, 0xc1, 0x16, 0x88, 0x73, 0x76, 0x4c, + 0x49, 0x7e, 0x60, 0xd7, 0xb4, 0xd5, 0xcc, 0xf1, 0x8f, 0xf1, 0x43, 0x78, + 0x52, 0xbb, 0x75, 0xf8, 0x90, 0x1f, 0xa0, 0x46, 0x3e, 0xba, 0xf1, 0x7a, + 0x0e, 0x80, 0xfa, 0xa0, 0x5a, 0x14, 0xd7, 0xbd, 0xe3, 0x8b, 0x08, 0xaf, + 0x43, 0xfb, 0x25, 0xdd, 0x6f, 0x1a, 0x14, 0x22, 0x08, 0xdb, 0xaf, 0x08, + 0xd9, 0xd3, 0x63, 0x1e, 0xb0, 0x3c, 0xe2, 0x15, 0xcb, 0xff, 0xf5, 0x54, + 0x87, 0x98, 0x66, 0xf0, 0x2b, 0x4b, 0x55, 0x61, 0xa0, 0xa3, 0xc8, 0x58, + 0xf2, 0x27, 0x9c, 0xce, 0x9d, 0x9c, 0x32, 0x2e, 0x8f, 0x5a, 0x18, 0xd2, + 0x66, 0x98, 0x5b, 0xc3, 0x33, 0x55, 0xdd, 0x5f, 0xe7, 0x7c, 0x9f, 0x0f, + 0xaa, 0xee, 0x39, 0xb9, 0xef, 0xa1, 0x14, 0x62, 0x41, 0xd4, 0xd3, 0x54, + 0x83, 0xdc, 0x63, 0xa1, 0x46, 0xba, 0x60, 0xb3, 0xe1, 0x72, 0xec, 0xc2, + 0x7a, 0xf6, 0x0c, 0x5f, 0xf6, 0xd0, 0x7e, 0x25, 0xc2, 0x84, 0xdc, 0x62, + 0xb0, 0x17, 0xa6, 0xb5, 0x12, 0xd0, 0x7f, 0x30, 0xbd, 0x94, 0x55, 0x8d, + 0x89, 0xe3, 0x37, 0xd1, 0xc2, 0xe0, 0x80, 0x59, 0xfe, 0xb1, 0x86, 0xe8, + 0xb2, 0xce, 0x30, 0x4e, 0xd7, 0xd8, 0xec, 0x5b, 0x54, 0x83, 0x93, 0x00, + 0xf0, 0x0a, 0x3a, 0x4e, 0x54, 0x12, 0x55, 0x07, 0xf9, 0x8d, 0x41, 0xda, + 0xe5, 0x16, 0xfc, 0xa8, 0x38, 0x29, 0x11, 0x94, 0xbd, 0xce, 0x40, 0x62, + 0xb4, 0x63, 0xbb, 0xde, 0x35, 0xf2, 0xa3, 0x06, 0xcb, 0x5e, 0x9e, 0x59, + 0x71, 0x1b, 0xd2, 0x77, 0xb7, 0xa4, 0x05, 0x4b, 0x70, 0xca, 0x77, 0x18, + 0x18, 0x65, 0xfe, 0x8f, 0x65, 0xe3, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, + 0x82, 0x01, 0x00, 0x78, 0x13, 0x14, 0x9f, 0xed, 0x79, 0x0b, 0x2c, 0x94, + 0xcc, 0x40, 0x6b, 0x86, 0xff, 0x16, 0x30, 0x02, 0x7e, 0x46, 0x2a, 0x2e, + 0xe6, 0x48, 0x65, 0xfa, 0x11, 0x66, 0x8d, 0xf6, 0x14, 0x8e, 0xe2, 0xd5, + 0x8a, 0x72, 0x55, 0x52, 0xa8, 0xda, 0x61, 0x2c, 0xb2, 0x10, 0x14, 0x55, + 0x43, 0x1a, 0x23, 0x29, 0xb5, 0xd9, 0xa6, 0x0f, 0x1c, 0xf4, 0xef, 0xec, + 0xdb, 0x47, 0x2d, 0xe4, 0xd5, 0xc1, 0xab, 0x41, 0x82, 0x91, 0xfb, 0xa2, + 0x04, 0xe1, 0x21, 0xba, 0x1c, 0x81, 0x30, 0xbd, 0xb2, 0x5d, 0x12, 0xa2, + 0x52, 0x40, 0xb1, 0xa6, 0xb7, 0xc4, 0x20, 0xfc, 0xbc, 0xdf, 0x96, 0x9e, + 0xc5, 0x29, 0xc8, 0x67, 0xac, 0xd6, 0x87, 0x47, 0x46, 0x8e, 0x23, 0xea, + 0xbb, 0x34, 0x95, 0xc3, 0x2f, 0xef, 0x5f, 0x75, 0x9c, 0xee, 0x88, 0x89, + 0x95, 0x3d, 0x4f, 0x93, 0x3c, 0xfb, 0x38, 0x8c, 0x0a, 0xb8, 0x1e, 0x49, + 0xc5, 0x04, 0xfe, 0xb8, 0xfb, 0xac, 0x27, 0x03, 0x76, 0xdd, 0x7a, 0xaf, + 0x03, 0xe7, 0x08, 0x73, 0x78, 0xb9, 0x1a, 0xb0, 0xa4, 0x1f, 0x7c, 0xcd, + 0xc8, 0x97, 0xf0, 0x80, 0x44, 0xb4, 0x52, 0xa7, 0xc4, 0xf2, 0x10, 0xe3, + 0x19, 0xbc, 0xdc, 0xd4, 0x64, 0xb3, 0xf4, 0xe4, 0x64, 0x46, 0xf2, 0xf8, + 0xc5, 0xc1, 0x8c, 0xcd, 0xa1, 0xf9, 0x0b, 0x80, 0xe2, 0x9f, 0x0f, 0xcf, + 0x61, 0x40, 0x87, 0x59, 0xbe, 0xc3, 0xf1, 0x21, 0x0d, 0xfc, 0xce, 0xd5, + 0xd7, 0xdc, 0x30, 0xc8, 0x5f, 0x9e, 0x4b, 0x26, 0xac, 0xdd, 0x86, 0x5c, + 0xfd, 0xd7, 0x4c, 0xb9, 0x3b, 0x02, 0xf8, 0xef, 0x70, 0xb8, 0x7e, 0x8e, + 0x7e, 0x38, 0x1f, 0xaf, 0xb1, 0x3c, 0x3e, 0x93, 0xd4, 0xdb, 0x72, 0x4b, + 0x13, 0xd7, 0xbf, 0xac, 0x4f, 0x30, 0x95, 0x6f, 0x09, 0x03, 0xd5, 0x58, + 0x41, 0x41, 0x94, 0x32, 0x75, 0x8c, 0xa9, 0x02, 0x81, 0x81, 0x00, 0xfd, + 0x6f, 0xc2, 0x10, 0x20, 0x42, 0x2a, 0xb0, 0xf9, 0x52, 0x00, 0xe2, 0xec, + 0x03, 0x4c, 0x34, 0x42, 0xf8, 0x1c, 0x96, 0xe7, 0xe0, 0x4b, 0x7a, 0x33, + 0x51, 0x4c, 0x6d, 0xa9, 0xf5, 0xf2, 0x84, 0x23, 0x70, 0x8f, 0x48, 0x9c, + 0xcb, 0x0d, 0x92, 0x89, 0x55, 0x2d, 0x0c, 0x14, 0xc4, 0xa6, 0xa8, 0x93, + 0x3a, 0x44, 0xbe, 0x22, 0xac, 0x5b, 0x40, 0x9d, 0xbd, 0x0a, 0x76, 0xfa, + 0xd1, 0x71, 0xc0, 0x9a, 0x17, 0xa4, 0x15, 0x93, 0x84, 0xcb, 0x84, 0x20, + 0xeb, 0x69, 0x12, 0x30, 0x0a, 0x22, 0x55, 0x2d, 0x36, 0x9e, 0xa1, 0xbc, + 0x9c, 0xe3, 0x44, 0xef, 0xb6, 0xa0, 0x33, 0x59, 0x88, 0x99, 0x3f, 0xb8, + 0x93, 0x2a, 0x79, 0xbd, 0x0b, 0x53, 0xb6, 0x02, 0x78, 0x41, 0xfb, 0xcb, + 0x03, 0x43, 0xf5, 0x3d, 0x2c, 0x21, 0xb3, 0xd8, 0x75, 0xfe, 0x1a, 0x85, + 0xc2, 0x1b, 0x32, 0x4c, 0x68, 0x03, 0xf5, 0x02, 0x81, 0x81, 0x00, 0xe5, + 0x66, 0x43, 0x5f, 0x36, 0x27, 0x00, 0xcf, 0xed, 0x36, 0xf9, 0x70, 0xb4, + 0xef, 0x39, 0x25, 0x81, 0x3c, 0x1f, 0x1c, 0xd8, 0xcc, 0x23, 0x48, 0x11, + 0xaa, 0x87, 0x74, 0x16, 0x93, 0x44, 0x4a, 0x41, 0x66, 0x4e, 0x2e, 0x31, + 0xfe, 0x6e, 0x5d, 0x3d, 0x74, 0xe0, 0x71, 0x8a, 0xde, 0x6e, 0x2d, 0x9f, + 0xef, 0xb5, 0x28, 0x29, 0x7a, 0xbb, 0x0b, 0xba, 0x49, 0xe9, 0xef, 0x20, + 0xb2, 0xf6, 0x9b, 0x8b, 0x56, 0xf7, 0x1f, 0x9f, 0x62, 0x7e, 0x5a, 0x64, + 0xd8, 0xdd, 0x4e, 0xa3, 0x35, 0x77, 0x15, 0x7c, 0x74, 0x3e, 0x48, 0x58, + 0xa7, 0x86, 0xf0, 0x9c, 0x50, 0xfa, 0x5c, 0x42, 0xbc, 0x95, 0x18, 0x6d, + 0x40, 0xae, 0x41, 0x8f, 0x0d, 0x4e, 0xe2, 0xad, 0xa3, 0x3c, 0x54, 0x1c, + 0xbc, 0x25, 0x6d, 0x2e, 0x9a, 0x8c, 0x87, 0x59, 0xaa, 0xff, 0x6e, 0xba, + 0x85, 0x2d, 0x5a, 0x70, 0x24, 0xf3, 0x77, 0x02, 0x81, 0x81, 0x00, 0xa2, + 0xba, 0xfc, 0xef, 0xdf, 0xd6, 0xbc, 0x94, 0xd0, 0x32, 0x30, 0x71, 0x84, + 0xbc, 0x05, 0x55, 0x55, 0x5e, 0xfc, 0xf4, 0x84, 0xc5, 0x07, 0x91, 0xda, + 0x9b, 0xac, 0x28, 0x8a, 0x46, 0x6b, 0x47, 0x1f, 0x7e, 0x69, 0xf2, 0x65, + 0x03, 0x18, 0x9e, 0xeb, 0xe1, 0xae, 0x40, 0x20, 0xa1, 0xb8, 0xa5, 0x6b, + 0x8b, 0xb1, 0x92, 0x28, 0xaf, 0x65, 0x14, 0x93, 0x74, 0x4b, 0xe0, 0xe3, + 0xe2, 0x52, 0x59, 0x2a, 0x58, 0x5d, 0xba, 0xa0, 0xf3, 0x56, 0xbe, 0x6b, + 0x68, 0xdd, 0x19, 0x99, 0x91, 0x18, 0x44, 0x7a, 0xf8, 0xa4, 0x59, 0x13, + 0xc3, 0xe5, 0x46, 0x61, 0x69, 0x71, 0x6a, 0x27, 0x66, 0x47, 0x88, 0x9b, + 0xda, 0x9b, 0x1e, 0x7a, 0x1d, 0x68, 0xcd, 0xe8, 0xbe, 0x11, 0x18, 0x3d, + 0x5d, 0xc6, 0x68, 0x14, 0xb4, 0x5e, 0x9b, 0x9d, 0x34, 0xfe, 0x49, 0x47, + 0x73, 0xc0, 0x84, 0xc6, 0x7c, 0xcf, 0x75, 0x02, 0x81, 0x80, 0x0b, 0xf2, + 0x50, 0x6d, 0xe3, 0xef, 0x12, 0x0e, 0xc2, 0x06, 0xcb, 0xe4, 0xbf, 0xb7, + 0x79, 0x9d, 0x67, 0x66, 0x8f, 0x24, 0x3c, 0x72, 0x1d, 0x20, 0x42, 0xb3, + 0x43, 0xcc, 0x9a, 0xe4, 0x5e, 0x79, 0x13, 0x80, 0xd5, 0x6f, 0x78, 0x71, + 0x0c, 0xa4, 0x54, 0x70, 0x02, 0xc4, 0x46, 0xfc, 0x20, 0x76, 0xc5, 0xce, + 0x80, 0xfb, 0xaf, 0x75, 0x51, 0xca, 0x61, 0x90, 0x39, 0xdb, 0x97, 0x33, + 0x5a, 0xbe, 0x6c, 0xfa, 0x41, 0x72, 0x67, 0x25, 0x84, 0xf9, 0x06, 0x29, + 0x79, 0x15, 0x00, 0x50, 0xe7, 0x7b, 0xb9, 0xf8, 0x7f, 0xc9, 0xda, 0xbc, + 0x16, 0xbd, 0xef, 0x16, 0x14, 0x83, 0xbb, 0x82, 0x00, 0xad, 0xbd, 0xfa, + 0xd1, 0x58, 0x5e, 0xaf, 0xcd, 0xbc, 0xe6, 0x58, 0x0a, 0xeb, 0x42, 0x77, + 0xd5, 0xe7, 0x22, 0xb7, 0x2f, 0xbc, 0x58, 0xc2, 0x1b, 0xfd, 0x53, 0x3f, + 0x5e, 0x46, 0x48, 0x72, 0xd4, 0x2f, 0x02, 0x81, 0x80, 0x6a, 0xbf, 0xcf, + 0x58, 0x14, 0x66, 0xc3, 0x76, 0x93, 0xc7, 0x03, 0xae, 0xb9, 0xb6, 0x60, + 0x61, 0xdd, 0x1e, 0x08, 0x2c, 0xe0, 0x2d, 0xd7, 0xde, 0x2c, 0x13, 0x04, + 0x26, 0x52, 0xf5, 0x25, 0x56, 0x3a, 0x30, 0xcc, 0xf1, 0x14, 0x7b, 0x82, + 0xa8, 0xce, 0x75, 0xf7, 0xfd, 0x01, 0x5c, 0x28, 0xd2, 0x78, 0x67, 0x56, + 0x42, 0xe3, 0x69, 0x1c, 0x69, 0xda, 0xf5, 0x31, 0xcf, 0xc8, 0x04, 0x00, + 0x76, 0x3c, 0x1b, 0x4c, 0x6d, 0xcc, 0x79, 0xcd, 0xd8, 0x4d, 0x29, 0x03, + 0xbb, 0xec, 0xfc, 0xf1, 0x02, 0x59, 0x98, 0x6d, 0xfa, 0xed, 0xc2, 0xac, + 0x8d, 0x1c, 0xb5, 0xa5, 0x4a, 0xe4, 0x04, 0x98, 0x47, 0xd3, 0x15, 0xc4, + 0xb9, 0xa1, 0xa6, 0xc8, 0xbd, 0xff, 0xef, 0x52, 0xac, 0x5b, 0xdc, 0x9f, + 0x46, 0x5c, 0x98, 0x46, 0xb3, 0x1a, 0x50, 0x75, 0x6a, 0x6d, 0x58, 0x53, + 0xcd, 0xed, 0x10, 0x4d, 0xde, + }, + { + 0x30, 0x82, 0x04, 0xbd, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x04, 0xa7, 0x30, 0x82, 0x04, 0xa3, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, + 0x01, 0x00, 0xaf, 0x87, 0xf1, 0xcb, 0x79, 0x9d, 0x91, 0x9f, 0xc0, 0x51, + 0x7b, 0x5f, 0x2b, 0x67, 0x1a, 0x50, 0xbc, 0x02, 0x56, 0x35, 0x62, 0x34, + 0xc5, 0xa7, 0x85, 0x31, 0xc6, 0xa5, 0x37, 0xc2, 0x2e, 0x6b, 0x30, 0x9d, + 0xc9, 0x50, 0x04, 0x84, 0x1e, 0x28, 0x3d, 0x26, 0x60, 0xd2, 0x12, 0x24, + 0xea, 0xd2, 0x66, 0x20, 0x5c, 0x4f, 0x42, 0xf8, 0x7c, 0x4f, 0x64, 0xec, + 0xb3, 0xf5, 0x07, 0x02, 0xca, 0x8f, 0x71, 0xc6, 0x50, 0xb2, 0x5c, 0x66, + 0xdd, 0x1e, 0x80, 0xdc, 0xe7, 0xb5, 0xeb, 0xe7, 0x88, 0x05, 0xd3, 0x35, + 0x9f, 0x36, 0xf4, 0x9a, 0xff, 0xaa, 0xc5, 0x32, 0x08, 0x44, 0x42, 0xba, + 0x34, 0xc1, 0xf0, 0x88, 0x0f, 0x0e, 0xb5, 0x5a, 0xb9, 0xd4, 0x35, 0xd5, + 0x62, 0xb4, 0xef, 0x08, 0x5f, 0xf4, 0x50, 0x86, 0x8b, 0x30, 0x66, 0x73, + 0x79, 0xe2, 0xf7, 0x79, 0x32, 0xcb, 0x80, 0xd9, 0xc9, 0x61, 0xe7, 0xca, + 0x41, 0xc8, 0xeb, 0xfb, 0xcf, 0xbb, 0x43, 0xc6, 0xe8, 0x72, 0x08, 0x24, + 0xc3, 0x03, 0xcd, 0xd1, 0xdc, 0xcd, 0x3d, 0x2a, 0xbc, 0x5b, 0x7d, 0xce, + 0x62, 0x5e, 0xbb, 0x0f, 0xb8, 0x46, 0x36, 0xd8, 0xaf, 0xaf, 0x2e, 0x89, + 0x21, 0x56, 0xf1, 0x02, 0xde, 0x6f, 0xd6, 0xce, 0x31, 0xc6, 0x2e, 0x89, + 0x87, 0x55, 0x16, 0x35, 0xf2, 0x99, 0x1f, 0x1b, 0x55, 0x83, 0x2e, 0xe5, + 0xf0, 0xf9, 0x5b, 0xd3, 0xc8, 0xe3, 0xf0, 0x29, 0xa1, 0x79, 0x61, 0xac, + 0xb6, 0xe9, 0x2e, 0x0d, 0x0b, 0x42, 0x38, 0x6f, 0xf7, 0xed, 0x87, 0xd8, + 0x0d, 0x27, 0x84, 0x10, 0xeb, 0xb6, 0x02, 0x09, 0x3b, 0xc1, 0x50, 0xd5, + 0xab, 0x88, 0xe0, 0x2e, 0x78, 0xc8, 0x9a, 0x55, 0x6f, 0x23, 0xfc, 0xa0, + 0x63, 0x51, 0x24, 0xd2, 0x31, 0x9d, 0xa1, 0x8f, 0x5a, 0x30, 0x82, 0x6e, + 0x26, 0xa1, 0x01, 0x1e, 0xff, 0xf7, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, + 0x82, 0x01, 0x00, 0x1a, 0x10, 0xd1, 0xf3, 0x29, 0x48, 0x20, 0xcb, 0xdc, + 0x3f, 0x3d, 0x56, 0xd0, 0x97, 0x2a, 0xed, 0x3a, 0x3e, 0x0e, 0x97, 0x36, + 0xd2, 0x27, 0x57, 0xcd, 0x0b, 0xab, 0x37, 0xf5, 0xa3, 0xbc, 0x48, 0xe6, + 0x9b, 0x47, 0xbd, 0xbe, 0x2d, 0xf0, 0x5c, 0x81, 0x58, 0x25, 0xa0, 0x8c, + 0x59, 0x37, 0x15, 0xaf, 0x5c, 0xb6, 0xd8, 0x7e, 0x55, 0x70, 0x00, 0x4a, + 0x38, 0x6c, 0x30, 0xc2, 0xad, 0xc3, 0x6e, 0xb1, 0xb2, 0x85, 0x61, 0x37, + 0x1f, 0x7f, 0x4e, 0x7c, 0x4c, 0x51, 0x8c, 0x4e, 0x4f, 0xbf, 0x7f, 0x3d, + 0xf2, 0x04, 0x86, 0xa0, 0xa4, 0x33, 0xbb, 0x24, 0x35, 0x5d, 0xcc, 0xe5, + 0x2b, 0xc9, 0x91, 0xd3, 0x2a, 0x21, 0x32, 0x49, 0xf4, 0x03, 0x0c, 0x64, + 0x42, 0x1f, 0xba, 0x46, 0x94, 0xb3, 0xcb, 0x0f, 0xfa, 0x2a, 0xc9, 0x31, + 0x2d, 0x8c, 0xb8, 0x0b, 0x8f, 0xcf, 0xdf, 0x30, 0x6f, 0x26, 0xdc, 0x48, + 0x1e, 0x23, 0x0d, 0x6e, 0x74, 0x64, 0x7e, 0xa3, 0xec, 0x78, 0x17, 0xd1, + 0x8b, 0x02, 0x6e, 0xce, 0x5e, 0xad, 0xc8, 0x85, 0x1e, 0xaa, 0xe5, 0xde, + 0x2d, 0x5b, 0xc4, 0xe2, 0xd1, 0xa5, 0xb9, 0x89, 0x7b, 0x23, 0xa0, 0x82, + 0xa3, 0x11, 0xc3, 0x9f, 0xff, 0xcc, 0xd4, 0x47, 0x54, 0xc1, 0xfc, 0x8f, + 0x4d, 0x3c, 0x69, 0x2d, 0xd7, 0x57, 0xd8, 0x2f, 0x8e, 0x62, 0x92, 0x38, + 0xfd, 0x4b, 0xc5, 0x54, 0x04, 0xa7, 0xa4, 0x79, 0xc3, 0x3d, 0xef, 0xf4, + 0x23, 0x57, 0x89, 0x65, 0x07, 0x98, 0x3d, 0x21, 0xb0, 0xff, 0xbe, 0xf9, + 0x12, 0xab, 0xd1, 0x80, 0x95, 0xba, 0x96, 0xd3, 0x0a, 0x43, 0x11, 0xe8, + 0xfe, 0x36, 0x05, 0xa8, 0x35, 0x8c, 0xd5, 0x11, 0xfb, 0xd1, 0xb0, 0x41, + 0x7a, 0x47, 0xb2, 0x57, 0x25, 0xa3, 0x1c, 0x7a, 0x36, 0xc4, 0x5a, 0x82, + 0xaa, 0x08, 0xe7, 0x76, 0x0c, 0xcd, 0xc1, 0x02, 0x81, 0x81, 0x00, 0xd4, + 0x7b, 0xc5, 0x4e, 0x6a, 0x35, 0xae, 0x81, 0xf4, 0x72, 0x77, 0x02, 0x1e, + 0x50, 0xc1, 0x0c, 0x1c, 0xa0, 0x16, 0x2d, 0xbf, 0xd1, 0x19, 0x18, 0x7d, + 0xb1, 0x47, 0x03, 0x65, 0x80, 0x74, 0x68, 0x15, 0x19, 0x7c, 0x82, 0xaa, + 0x13, 0x14, 0x1f, 0x82, 0xe4, 0x72, 0x97, 0x1a, 0x61, 0x6d, 0xbe, 0x6c, + 0x70, 0x3e, 0x91, 0x82, 0x9b, 0xcd, 0x29, 0x6a, 0xb3, 0xe8, 0x7d, 0xf8, + 0xa7, 0x3f, 0xc6, 0x68, 0x29, 0xe4, 0x5f, 0xf0, 0x18, 0xdf, 0x3c, 0x4c, + 0xdd, 0x0e, 0xdd, 0xdf, 0x7d, 0x18, 0x9c, 0xa4, 0x97, 0xf4, 0xd9, 0xa4, + 0xf8, 0x65, 0x3b, 0xc0, 0x96, 0x9c, 0xdf, 0x08, 0xb6, 0x24, 0x30, 0x8b, + 0xae, 0x80, 0xbe, 0x21, 0x99, 0xbc, 0x44, 0x32, 0x1f, 0x43, 0x73, 0x0d, + 0x2b, 0xaf, 0xad, 0xec, 0x67, 0x37, 0xdd, 0x00, 0xa1, 0x89, 0xe0, 0xab, + 0x3c, 0x62, 0x5c, 0x05, 0xeb, 0x1d, 0x3f, 0x02, 0x81, 0x81, 0x00, 0xd3, + 0x7a, 0xce, 0x3e, 0xe9, 0xae, 0xe7, 0x60, 0xdc, 0x8e, 0xf1, 0xe7, 0x30, + 0x91, 0x16, 0xa5, 0xea, 0x4a, 0x2e, 0xcf, 0xd0, 0x7e, 0xc7, 0xa7, 0x5a, + 0x18, 0xd8, 0xcc, 0x7b, 0x95, 0x24, 0xa3, 0x8b, 0x94, 0x8e, 0x83, 0x94, + 0x77, 0x32, 0xd0, 0x7a, 0x8c, 0xc6, 0x70, 0x5a, 0x8b, 0x08, 0xcf, 0x38, + 0xa2, 0x64, 0xb0, 0x5e, 0x08, 0x0e, 0x82, 0xc3, 0xe8, 0xaf, 0x65, 0xeb, + 0xa8, 0x1a, 0x3e, 0xa3, 0x03, 0x56, 0xa6, 0x43, 0xd5, 0xa2, 0x0f, 0xfe, + 0x1a, 0x29, 0x21, 0x2d, 0x7f, 0x4a, 0x2b, 0xae, 0x9e, 0x6b, 0x3f, 0x01, + 0x3d, 0x2c, 0x48, 0x73, 0xa2, 0xd6, 0xdf, 0xdf, 0xf0, 0x21, 0x3d, 0xbf, + 0xfe, 0x79, 0x14, 0x3c, 0x06, 0x22, 0xd3, 0xc6, 0x26, 0x4a, 0x84, 0xc3, + 0xd4, 0x00, 0x02, 0xca, 0x5b, 0xc3, 0xb8, 0x12, 0x2a, 0x5a, 0x53, 0xa7, + 0x83, 0xf4, 0x6b, 0xba, 0xcd, 0x17, 0x49, 0x02, 0x81, 0x80, 0x74, 0x44, + 0xa9, 0xc9, 0x67, 0xba, 0xaa, 0x1b, 0xb8, 0x3b, 0xee, 0x3c, 0xfd, 0x83, + 0xa8, 0xae, 0xcc, 0xf7, 0x18, 0x4b, 0xbe, 0x5b, 0x57, 0x85, 0x1c, 0x3a, + 0x26, 0x26, 0x88, 0xa3, 0x0a, 0x8f, 0x6d, 0x84, 0x3d, 0xaa, 0x6e, 0x0f, + 0xf4, 0x81, 0xed, 0xa8, 0xbd, 0xbf, 0x4b, 0x13, 0x26, 0xee, 0xfc, 0xfd, + 0x4a, 0x6f, 0x6a, 0xed, 0x80, 0x0c, 0xfa, 0xb3, 0x29, 0x33, 0xcd, 0x1d, + 0x0f, 0x7b, 0xac, 0xc1, 0xc7, 0x92, 0x6b, 0x99, 0xe0, 0x85, 0x69, 0x75, + 0xc8, 0xcd, 0xc5, 0x9d, 0xf9, 0x1a, 0xb5, 0xc1, 0xe4, 0x9c, 0x37, 0x76, + 0x28, 0x46, 0xd2, 0xf2, 0x57, 0x32, 0x7f, 0xdc, 0xd2, 0xde, 0xb1, 0x80, + 0x32, 0x38, 0x57, 0xc0, 0x7c, 0xd1, 0x40, 0x2f, 0xf5, 0x43, 0x7e, 0x60, + 0x7a, 0x48, 0x0c, 0x44, 0xca, 0xbf, 0xa6, 0xb9, 0xbd, 0xed, 0x6f, 0x25, + 0xa2, 0x98, 0xc2, 0x30, 0x4e, 0x17, 0x02, 0x81, 0x81, 0x00, 0x8d, 0x71, + 0x90, 0xaa, 0x61, 0x6f, 0x7b, 0x0f, 0xbc, 0xd4, 0xda, 0x94, 0x6f, 0x56, + 0x4b, 0x18, 0x93, 0x58, 0x24, 0x71, 0x18, 0x19, 0x03, 0x7e, 0x39, 0xf3, + 0x6e, 0x65, 0x6d, 0xae, 0xb5, 0xff, 0xb0, 0xa6, 0x9e, 0x9f, 0x00, 0xd8, + 0xac, 0xa2, 0xe5, 0xa4, 0x6c, 0x40, 0xcf, 0x3e, 0x38, 0x99, 0x28, 0x5e, + 0xed, 0x0b, 0x4d, 0x09, 0x04, 0x73, 0x8f, 0x86, 0x7c, 0x71, 0xa5, 0xa9, + 0x1c, 0x4c, 0x92, 0x88, 0xf4, 0x16, 0x66, 0x3e, 0x67, 0x93, 0x5c, 0xb7, + 0xb0, 0x94, 0xa1, 0x32, 0x86, 0xb0, 0x2a, 0xad, 0x88, 0x3b, 0x6e, 0x9f, + 0xb4, 0x93, 0x8c, 0xcb, 0xf7, 0xfd, 0x6a, 0x70, 0xd8, 0x3a, 0xcd, 0xc3, + 0xec, 0xc5, 0x8c, 0xd3, 0xe9, 0xcd, 0x4c, 0xb5, 0x65, 0x97, 0xe0, 0xc6, + 0x55, 0x6f, 0x88, 0x5a, 0xd1, 0xcf, 0xb6, 0x4e, 0x9f, 0x75, 0x5e, 0x10, + 0x12, 0x95, 0x4f, 0x2e, 0x47, 0x09, 0x02, 0x81, 0x80, 0x7d, 0x7c, 0xb4, + 0xd2, 0xb0, 0x17, 0xd5, 0x2f, 0xee, 0xbc, 0x76, 0xf5, 0x6a, 0x37, 0xc3, + 0x59, 0x5b, 0x0c, 0xd3, 0xda, 0xb4, 0x8b, 0xb2, 0xf0, 0xbf, 0xbb, 0x30, + 0x68, 0x92, 0x5c, 0x6d, 0xec, 0xba, 0xe0, 0xd2, 0x4a, 0x5c, 0x04, 0x80, + 0x59, 0x2f, 0x8e, 0x56, 0x3c, 0xa5, 0x34, 0x07, 0x4f, 0x48, 0x5c, 0xee, + 0x8d, 0xb1, 0x23, 0x8b, 0xd0, 0x8f, 0x51, 0x75, 0x22, 0xb4, 0xb2, 0x6c, + 0xea, 0xc2, 0xcc, 0x6c, 0x69, 0x04, 0x94, 0xac, 0x10, 0xb4, 0xf6, 0x0b, + 0xb8, 0x0f, 0x8d, 0x10, 0xc0, 0x51, 0xb4, 0xb9, 0x2a, 0x7d, 0x98, 0x18, + 0x95, 0x77, 0x3f, 0xf7, 0x54, 0x1a, 0xef, 0xdc, 0xe6, 0xeb, 0xd0, 0x6d, + 0x56, 0x9b, 0xff, 0x6a, 0x5a, 0x0a, 0xb5, 0x9d, 0x52, 0xbb, 0x11, 0x35, + 0xc9, 0x42, 0xa6, 0x91, 0xe3, 0xe6, 0xf8, 0x4a, 0x70, 0x93, 0x85, 0x98, + 0xcb, 0xef, 0x33, 0x7e, 0x2a, + }, + { + 0x30, 0x82, 0x04, 0xbe, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x04, 0xa8, 0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, + 0x01, 0x00, 0xcf, 0xb2, 0x9c, 0xc4, 0x6b, 0xdb, 0x4b, 0xe9, 0x01, 0x06, + 0x44, 0x8a, 0xe3, 0x78, 0xf5, 0xfd, 0x89, 0xaf, 0x3b, 0xce, 0x2f, 0xdd, + 0xa0, 0x34, 0x86, 0xfb, 0x3f, 0xe2, 0x85, 0xde, 0x2c, 0x86, 0xba, 0xfc, + 0x95, 0x87, 0xa3, 0x38, 0xa7, 0x16, 0xbf, 0xaf, 0xc9, 0x21, 0x1c, 0x76, + 0x87, 0x7d, 0x22, 0x80, 0x56, 0xa3, 0x05, 0x7f, 0x30, 0xbd, 0xc4, 0x7a, + 0x4d, 0x83, 0x6a, 0xd2, 0x53, 0xc6, 0xe1, 0x51, 0x83, 0x21, 0x41, 0x8a, + 0x36, 0xdb, 0xd1, 0xc1, 0x9c, 0xf8, 0x6b, 0x98, 0x03, 0x82, 0xb1, 0xd2, + 0x76, 0x58, 0xc5, 0x09, 0xb2, 0x2c, 0xb2, 0x01, 0x56, 0x93, 0x96, 0x35, + 0xd2, 0xef, 0xcf, 0x7b, 0x18, 0x55, 0xbb, 0xd6, 0x46, 0x32, 0xd9, 0x2a, + 0x34, 0x17, 0x06, 0x78, 0x69, 0x9a, 0x62, 0x7e, 0xdf, 0xc7, 0x61, 0x02, + 0x16, 0x49, 0x05, 0x6e, 0xc1, 0xb0, 0x33, 0x41, 0xf9, 0x92, 0xcd, 0xf2, + 0xc1, 0x23, 0x7b, 0x5c, 0xf5, 0xff, 0xd4, 0x18, 0x28, 0x4d, 0xcd, 0x14, + 0x82, 0xa2, 0x62, 0xea, 0x48, 0xc9, 0x03, 0x99, 0x99, 0x9f, 0x55, 0x5b, + 0x4d, 0x96, 0x2a, 0xf0, 0x42, 0x68, 0x29, 0x15, 0x13, 0x5f, 0xe4, 0xef, + 0x73, 0x48, 0x87, 0x16, 0xc8, 0x55, 0xbc, 0x2d, 0x21, 0xca, 0x22, 0x15, + 0x9f, 0xe6, 0x72, 0xd1, 0x55, 0x65, 0xa1, 0x55, 0x75, 0x83, 0x0f, 0x0e, + 0x45, 0xae, 0xce, 0x39, 0xac, 0x26, 0x8b, 0xf4, 0x77, 0xe0, 0xce, 0x59, + 0x8f, 0xc9, 0x54, 0xf4, 0x41, 0x8d, 0x94, 0x68, 0x60, 0x09, 0x68, 0xbe, + 0x5f, 0xfb, 0xc4, 0x81, 0xc8, 0x11, 0x1c, 0x07, 0x1b, 0x0d, 0xad, 0x6c, + 0x8b, 0x62, 0xf5, 0xf9, 0x37, 0x06, 0xba, 0xe4, 0xbc, 0x47, 0xd3, 0xdf, + 0x84, 0x62, 0x56, 0xa3, 0x9b, 0xd9, 0x2e, 0xea, 0x34, 0xa3, 0x21, 0x7c, + 0x28, 0xc9, 0x83, 0x9e, 0x62, 0x2d, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, + 0x82, 0x01, 0x00, 0x74, 0xbe, 0xeb, 0x4b, 0xa5, 0x6c, 0xc1, 0x00, 0x99, + 0xe8, 0xd2, 0x02, 0x57, 0xd6, 0x98, 0x3f, 0x09, 0x73, 0x8c, 0x64, 0x5b, + 0x97, 0x72, 0x03, 0xd4, 0x93, 0xc9, 0xd7, 0x24, 0x74, 0x3c, 0x07, 0x07, + 0x78, 0x0e, 0xfd, 0x0d, 0x0c, 0x3d, 0x2f, 0x96, 0x53, 0xd2, 0xf4, 0x5f, + 0xb1, 0x68, 0x7b, 0x38, 0x37, 0x77, 0x7b, 0x6f, 0x0d, 0x8d, 0x72, 0x3d, + 0xcc, 0x99, 0x61, 0x73, 0x5c, 0x02, 0x67, 0x5a, 0x23, 0x9d, 0x57, 0xc6, + 0x25, 0x50, 0xac, 0xc2, 0xfc, 0xd7, 0xa4, 0xf3, 0xa5, 0xa0, 0xe3, 0x87, + 0xdc, 0xcb, 0x55, 0x1e, 0x7c, 0x52, 0xc0, 0xb8, 0xd5, 0xf3, 0xba, 0x45, + 0xc6, 0xbf, 0x68, 0xb8, 0xf1, 0x72, 0x3e, 0x4e, 0xb5, 0x25, 0x04, 0x53, + 0xf9, 0x5b, 0x0a, 0x4e, 0x42, 0xc4, 0xe9, 0x58, 0xd0, 0x04, 0x2d, 0x68, + 0x23, 0xea, 0x2e, 0xe5, 0x74, 0xda, 0x3e, 0x96, 0xb1, 0x50, 0xfc, 0xe6, + 0xb6, 0xb6, 0x11, 0xc5, 0xcd, 0x1a, 0xf4, 0x1c, 0xd4, 0x07, 0xdf, 0xf6, + 0x01, 0xe4, 0x7a, 0xa1, 0x67, 0x7d, 0xd9, 0x21, 0xe9, 0x8a, 0xd0, 0x80, + 0x05, 0xe4, 0x72, 0x3e, 0xf7, 0xc6, 0xb7, 0x57, 0x64, 0x41, 0x88, 0xd6, + 0x94, 0x51, 0x0d, 0x24, 0xa3, 0x5a, 0xf2, 0x50, 0x64, 0x0c, 0x39, 0xe1, + 0x7e, 0xa3, 0x04, 0xb0, 0xcd, 0x35, 0xa4, 0x49, 0x53, 0xc0, 0xaf, 0x31, + 0x57, 0x32, 0x70, 0xb8, 0x30, 0x8e, 0x82, 0xb0, 0x8c, 0x77, 0x2e, 0x4e, + 0x14, 0x0d, 0x64, 0x2f, 0xc1, 0xc4, 0x65, 0xa9, 0xe6, 0x67, 0x3f, 0x79, + 0xaf, 0xb3, 0x67, 0x3e, 0x5c, 0xeb, 0x54, 0xc4, 0x19, 0x8e, 0xb0, 0x50, + 0xf1, 0x31, 0x10, 0x25, 0x2d, 0xa8, 0x14, 0xcf, 0x07, 0x2c, 0x17, 0x4c, + 0xba, 0x77, 0xfc, 0x54, 0xa0, 0x05, 0xf5, 0x0a, 0xca, 0x53, 0xa7, 0x65, + 0xaf, 0x74, 0xc2, 0x56, 0x18, 0x37, 0xa9, 0x02, 0x81, 0x81, 0x00, 0xe8, + 0x37, 0xd9, 0x95, 0xd2, 0x96, 0xae, 0x72, 0x09, 0x4e, 0x0c, 0xcd, 0x8b, + 0x8d, 0x9b, 0x3a, 0x2d, 0x5d, 0x84, 0x1e, 0x7a, 0x23, 0x43, 0xec, 0x49, + 0xee, 0x2d, 0x14, 0x28, 0xad, 0xe5, 0x6e, 0x8e, 0xbb, 0xd3, 0xff, 0x99, + 0x16, 0xcf, 0xf6, 0xe8, 0xa8, 0x5b, 0xd9, 0x13, 0xc6, 0xda, 0x29, 0x42, + 0x94, 0xb8, 0xcb, 0x0e, 0xa3, 0x5e, 0xa5, 0x3e, 0x56, 0x9d, 0x7a, 0xbb, + 0xbc, 0x06, 0x49, 0xcb, 0xc4, 0x6a, 0xe8, 0xc8, 0xcb, 0xb9, 0xbe, 0xe6, + 0x97, 0xa7, 0x7e, 0x0c, 0xdf, 0x36, 0x5b, 0x4b, 0x7a, 0x15, 0x8e, 0x81, + 0x2a, 0x28, 0x82, 0xad, 0xb8, 0xfc, 0xde, 0x73, 0x3c, 0xae, 0x34, 0x5d, + 0x9f, 0xbd, 0x9f, 0x07, 0x13, 0xc9, 0xf6, 0x5a, 0x79, 0x2a, 0x31, 0xd6, + 0xf3, 0x1b, 0x2e, 0xc7, 0xc5, 0x59, 0x14, 0x86, 0xea, 0x2c, 0xc5, 0x15, + 0xf5, 0x26, 0x43, 0xb1, 0x10, 0x78, 0x1f, 0x02, 0x81, 0x81, 0x00, 0xe4, + 0xf7, 0xe6, 0x81, 0xf1, 0x8e, 0x4d, 0xe2, 0x1a, 0x66, 0x72, 0xec, 0x7f, + 0xf2, 0x6f, 0x4a, 0x7b, 0x0e, 0x6f, 0x2f, 0x53, 0xa4, 0x4e, 0x7c, 0x55, + 0x64, 0x73, 0x70, 0xbc, 0xa0, 0x6a, 0xf6, 0x96, 0x0e, 0x85, 0x92, 0x22, + 0xdd, 0x68, 0x64, 0x62, 0x5f, 0x23, 0x5a, 0xb5, 0x39, 0xba, 0xee, 0xb5, + 0x2a, 0x1d, 0xa2, 0xe0, 0x84, 0x97, 0x61, 0xd7, 0x50, 0x38, 0x7b, 0x9d, + 0x6d, 0x28, 0x32, 0xbf, 0x5f, 0xc3, 0x3a, 0x20, 0x9e, 0xac, 0xcb, 0xa0, + 0x9b, 0x89, 0x6b, 0x81, 0x31, 0x37, 0x1e, 0xc8, 0x6f, 0xdc, 0x47, 0x39, + 0x07, 0xf1, 0x8a, 0xc8, 0x9d, 0xf2, 0x3a, 0x44, 0x10, 0x8d, 0x71, 0x32, + 0xfd, 0xcf, 0x7e, 0x50, 0x1e, 0x71, 0x90, 0x5c, 0xaa, 0xed, 0x50, 0x49, + 0x8c, 0x6f, 0x7d, 0xf0, 0x8b, 0xb6, 0x62, 0x4d, 0xcd, 0x4f, 0xb2, 0x9d, + 0xbf, 0xe4, 0xe4, 0x65, 0xab, 0x0c, 0x33, 0x02, 0x81, 0x81, 0x00, 0xd6, + 0x08, 0xac, 0x27, 0x18, 0x8b, 0xc3, 0xcb, 0xdd, 0xe1, 0xaf, 0x91, 0x40, + 0x40, 0x4b, 0xc3, 0xaa, 0x8e, 0x98, 0x38, 0xd3, 0x10, 0x70, 0x09, 0x78, + 0xf6, 0xeb, 0x5b, 0xba, 0x46, 0xd5, 0xc2, 0x3a, 0x12, 0xcb, 0x4a, 0xb9, + 0xda, 0xb9, 0xe1, 0xf4, 0xfd, 0x40, 0x72, 0xeb, 0x74, 0x8c, 0x1e, 0x1d, + 0x7d, 0x20, 0x7a, 0x20, 0x4a, 0xd9, 0xf0, 0x6a, 0x6d, 0xc1, 0x06, 0x9f, + 0xc4, 0xa4, 0xf3, 0x1d, 0xba, 0x33, 0x63, 0xcd, 0xb7, 0x60, 0xc3, 0xa0, + 0x9c, 0x45, 0x47, 0xbc, 0x2b, 0x7c, 0x59, 0x57, 0x4e, 0xf6, 0x51, 0x7b, + 0xa1, 0xd4, 0xe6, 0xe5, 0xa2, 0x2c, 0x88, 0x08, 0x24, 0xf4, 0xd7, 0xd5, + 0x37, 0x67, 0xba, 0x0c, 0xf6, 0xe3, 0x5e, 0x8a, 0x73, 0x45, 0x3d, 0xdc, + 0xc6, 0x03, 0xd6, 0xf1, 0x50, 0x67, 0x53, 0xb1, 0x2a, 0x64, 0x0b, 0x5f, + 0xba, 0xae, 0x03, 0x14, 0x4f, 0x7e, 0xb7, 0x02, 0x81, 0x81, 0x00, 0x87, + 0x08, 0x54, 0x1e, 0xea, 0x50, 0x5c, 0x4a, 0x2d, 0xfb, 0xa1, 0x67, 0x2c, + 0x85, 0xd3, 0x70, 0x7f, 0x66, 0x21, 0xd8, 0x2d, 0x61, 0xc7, 0x0f, 0xb8, + 0x64, 0x87, 0x58, 0x43, 0x45, 0xf0, 0x0f, 0x00, 0xac, 0x0b, 0xc6, 0x05, + 0x5d, 0xc2, 0x05, 0x01, 0xa6, 0xcd, 0xf8, 0xde, 0xcb, 0xef, 0x1c, 0x7c, + 0x27, 0x1c, 0x57, 0x44, 0x45, 0xad, 0xe9, 0x7b, 0xcb, 0xc9, 0xa3, 0xcd, + 0x51, 0xe4, 0xd5, 0x18, 0xdb, 0xe7, 0x0c, 0xce, 0xd4, 0x05, 0x60, 0x09, + 0x95, 0xf0, 0xd3, 0x3f, 0xdb, 0xc0, 0x80, 0x1d, 0x01, 0x2b, 0x4d, 0xad, + 0xaa, 0xab, 0x9e, 0x87, 0x75, 0xee, 0xb6, 0xc2, 0x11, 0x68, 0xf9, 0x0b, + 0x73, 0x77, 0xfd, 0x2c, 0x4e, 0xe7, 0xf6, 0x95, 0x50, 0x10, 0xa8, 0xba, + 0xd7, 0x61, 0xa1, 0x4a, 0x4d, 0x7b, 0x00, 0x3a, 0x96, 0x0c, 0x9c, 0x70, + 0x40, 0x0d, 0x43, 0x4d, 0x5a, 0x34, 0xd5, 0x02, 0x81, 0x80, 0x5e, 0x01, + 0xe3, 0xa4, 0xfd, 0xc2, 0x5c, 0x74, 0xb9, 0x04, 0x70, 0xa3, 0x14, 0x43, + 0x6e, 0x20, 0x81, 0x0a, 0x29, 0x0f, 0x85, 0xbb, 0x1e, 0x05, 0x97, 0xbf, + 0xd5, 0x89, 0xd5, 0x45, 0xf7, 0x8f, 0x4b, 0xf9, 0xf6, 0xf9, 0xcf, 0x08, + 0xa1, 0x0e, 0xb7, 0x36, 0x1f, 0x72, 0xe4, 0xd9, 0x23, 0xd5, 0xa0, 0xe0, + 0x8e, 0x61, 0xe8, 0x17, 0xa8, 0x65, 0x6b, 0x5a, 0xa2, 0x83, 0xd4, 0x5c, + 0x11, 0x97, 0xfb, 0xc6, 0x76, 0x87, 0x7c, 0x37, 0xbe, 0x21, 0x5d, 0x8b, + 0x17, 0x7e, 0x0a, 0x6c, 0xab, 0xde, 0x8e, 0x74, 0x5b, 0x4d, 0xcf, 0x58, + 0x11, 0x7b, 0xb5, 0xfc, 0x47, 0x37, 0xc6, 0x8f, 0xcc, 0x30, 0x7e, 0xb3, + 0x87, 0x4c, 0xab, 0xcd, 0xdb, 0xd7, 0x24, 0x9e, 0xeb, 0xd0, 0x12, 0x08, + 0xa0, 0xa7, 0x7e, 0x84, 0x97, 0x77, 0x9a, 0x27, 0x0b, 0x97, 0xe5, 0x25, + 0x97, 0x3c, 0xe8, 0xe8, 0x54, 0x95, + }, + { + 0x30, 0x82, 0x04, 0xbc, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x04, 0xa6, 0x30, 0x82, 0x04, 0xa2, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, + 0x01, 0x00, 0xad, 0xc0, 0xb7, 0xd8, 0x61, 0x82, 0x55, 0xa1, 0x39, 0xc8, + 0x22, 0xc1, 0x22, 0x16, 0x0f, 0x1e, 0x06, 0xc8, 0xf1, 0x37, 0xe5, 0x9a, + 0x63, 0x1f, 0xd1, 0x83, 0x76, 0x0b, 0xe3, 0xd8, 0x79, 0x7d, 0x02, 0x26, + 0xe1, 0x91, 0x2a, 0x32, 0xb1, 0xa9, 0xea, 0x5c, 0x1d, 0x1e, 0x7f, 0xed, + 0xa1, 0x06, 0xe7, 0x67, 0x41, 0x59, 0x1b, 0x68, 0xcf, 0x3e, 0xa7, 0x3a, + 0x12, 0x50, 0xc3, 0xed, 0x96, 0x9c, 0x80, 0x1c, 0xd3, 0x4f, 0x51, 0x31, + 0x74, 0x6d, 0xef, 0x04, 0xbf, 0xf7, 0x17, 0x14, 0xf8, 0x4c, 0x25, 0x63, + 0xad, 0xe2, 0x89, 0xf8, 0x02, 0x01, 0x2e, 0x05, 0x32, 0xb1, 0x71, 0x7f, + 0x04, 0x99, 0xc0, 0x93, 0x69, 0xc2, 0x2d, 0x6d, 0x4d, 0xef, 0xe2, 0xe2, + 0x0c, 0x1a, 0xf2, 0x91, 0xf6, 0x82, 0x83, 0xf1, 0x85, 0x0d, 0x37, 0x72, + 0xd6, 0x70, 0xa7, 0x4a, 0xbb, 0xf7, 0x06, 0x29, 0xef, 0xbb, 0x63, 0x6c, + 0x2e, 0x4d, 0x7d, 0x59, 0xd7, 0x08, 0xba, 0xb1, 0x7b, 0x1e, 0xa5, 0xde, + 0x9c, 0xf3, 0x6f, 0x9f, 0xcb, 0xad, 0xf6, 0xb2, 0x18, 0xef, 0x81, 0xa7, + 0xc5, 0xf4, 0xa3, 0x33, 0x4e, 0x70, 0x20, 0x88, 0x47, 0x68, 0x14, 0x6f, + 0xfc, 0x09, 0xea, 0xfe, 0x71, 0x2f, 0x3f, 0xac, 0x6a, 0x7e, 0xcf, 0xe7, + 0x2a, 0x52, 0x2c, 0xb5, 0x1d, 0x43, 0x6f, 0x31, 0x81, 0xa8, 0x5b, 0x4b, + 0x02, 0x0a, 0xec, 0x5a, 0x19, 0xc3, 0x43, 0xce, 0xe7, 0x7d, 0xbc, 0x70, + 0x5e, 0xf8, 0x83, 0xb8, 0xbc, 0xc3, 0xe3, 0x1f, 0x1a, 0x19, 0xb2, 0xeb, + 0xb3, 0x3e, 0x8f, 0x85, 0x60, 0xbe, 0x9a, 0xc4, 0xdf, 0xea, 0xa8, 0x89, + 0x19, 0xa3, 0xf3, 0x45, 0xea, 0xba, 0x11, 0xe6, 0xa6, 0x66, 0xb1, 0xb4, + 0x2e, 0xeb, 0x7a, 0xb4, 0x2c, 0x1d, 0xc9, 0xb3, 0xb1, 0x46, 0x53, 0x07, + 0xe4, 0xd5, 0xcc, 0x4f, 0xcf, 0x01, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, + 0x82, 0x01, 0x00, 0x59, 0x59, 0x41, 0xf0, 0xf6, 0x6b, 0x94, 0x67, 0x1d, + 0x14, 0x8a, 0xb2, 0xb0, 0x39, 0x91, 0xba, 0x8a, 0x18, 0xa8, 0xda, 0x6a, + 0xaa, 0x92, 0x90, 0x99, 0x27, 0xb5, 0xfb, 0x75, 0x46, 0x2b, 0x6b, 0x1a, + 0x41, 0x2e, 0x3b, 0x7c, 0xec, 0xa6, 0xa9, 0xce, 0xc7, 0x61, 0x7d, 0xfe, + 0xa1, 0x0e, 0xee, 0x42, 0xee, 0xb9, 0x0e, 0xfe, 0x66, 0x4e, 0x34, 0x54, + 0xc0, 0x2b, 0x0c, 0xf7, 0x43, 0xe4, 0xf0, 0x43, 0x7d, 0x0f, 0x38, 0xb9, + 0x72, 0xea, 0x01, 0x62, 0x29, 0x1a, 0x22, 0x10, 0xef, 0x8a, 0xa3, 0x75, + 0x92, 0xa5, 0xa7, 0x59, 0xf5, 0x7b, 0xea, 0x69, 0x64, 0xe8, 0xe5, 0x90, + 0x8c, 0x95, 0xac, 0x0e, 0xf8, 0x7f, 0x29, 0x08, 0x3d, 0x2e, 0x17, 0xce, + 0x9f, 0x19, 0x44, 0x7e, 0x3a, 0x8a, 0xfe, 0x20, 0xa5, 0x81, 0x1b, 0x03, + 0xc9, 0x7c, 0xf6, 0x5f, 0x40, 0xb2, 0x19, 0xdc, 0x44, 0x92, 0x6e, 0xfc, + 0x30, 0xc2, 0x59, 0x16, 0x29, 0x80, 0xcf, 0x4f, 0x26, 0x39, 0x6b, 0x84, + 0xb1, 0x55, 0xa0, 0xac, 0x76, 0xa2, 0xc6, 0x4e, 0x46, 0x44, 0x4f, 0x12, + 0x76, 0x39, 0xe9, 0x53, 0xb1, 0x46, 0x6a, 0x77, 0xf1, 0x0e, 0xdf, 0x88, + 0x4d, 0x3a, 0x73, 0x1b, 0x84, 0xee, 0x25, 0x04, 0x85, 0x1e, 0xd4, 0xd6, + 0xf7, 0x06, 0x16, 0xb3, 0x81, 0x16, 0xdb, 0xbb, 0xbf, 0x27, 0x6d, 0x0c, + 0xb4, 0x3e, 0x3b, 0x55, 0x25, 0x05, 0x7b, 0x47, 0xb5, 0xf4, 0xce, 0xe0, + 0xbb, 0xa0, 0xb5, 0xc6, 0x08, 0x8c, 0xdc, 0x75, 0x5b, 0xe9, 0xc2, 0xe2, + 0x61, 0x0c, 0xf9, 0x95, 0x62, 0xaf, 0x03, 0x9d, 0x99, 0x80, 0x67, 0x02, + 0xef, 0xcb, 0xb6, 0x0c, 0xe7, 0x7d, 0x1f, 0xf4, 0x6a, 0x47, 0xba, 0xf5, + 0x4a, 0xc0, 0x51, 0xec, 0x97, 0xb4, 0x2a, 0x4d, 0x2e, 0x93, 0xce, 0xb1, + 0xb4, 0x88, 0x06, 0xd3, 0x00, 0x38, 0xa5, 0x02, 0x81, 0x81, 0x00, 0xda, + 0x0d, 0x41, 0x44, 0x22, 0x42, 0x86, 0xe9, 0x69, 0x4a, 0xac, 0xe0, 0x63, + 0x20, 0x3f, 0x33, 0xc4, 0xfa, 0xe5, 0x67, 0x4c, 0x6a, 0xa4, 0x38, 0xcb, + 0x37, 0x90, 0xec, 0xaa, 0x6c, 0x76, 0x54, 0xd7, 0x6a, 0xce, 0x06, 0xf5, + 0xe9, 0xb4, 0xb6, 0x38, 0x6f, 0xe9, 0xd2, 0x38, 0x5c, 0x72, 0x5d, 0x8d, + 0xbf, 0x40, 0x13, 0x6b, 0xe1, 0xae, 0x3b, 0x68, 0x6c, 0x9f, 0xe3, 0xa0, + 0xb4, 0x88, 0xf1, 0x6b, 0x83, 0x37, 0x7b, 0x75, 0x52, 0x8c, 0x5b, 0x1e, + 0x76, 0x58, 0x19, 0xe0, 0xab, 0x82, 0x61, 0x8d, 0x47, 0x48, 0x3d, 0x46, + 0x6a, 0x28, 0x73, 0xd7, 0x12, 0x64, 0x35, 0x6b, 0x2f, 0x67, 0xbe, 0x52, + 0xfb, 0x05, 0x86, 0xe6, 0x41, 0x61, 0x1b, 0x49, 0x4a, 0x30, 0x79, 0xd9, + 0x20, 0xca, 0x6a, 0x1a, 0x6c, 0x8b, 0x2e, 0xea, 0x8d, 0x64, 0x4a, 0xde, + 0x91, 0x0a, 0x24, 0xc4, 0xe4, 0x62, 0x73, 0x02, 0x81, 0x81, 0x00, 0xcb, + 0xfd, 0xd5, 0xaa, 0x06, 0xed, 0x9a, 0x35, 0xaa, 0x95, 0xad, 0x73, 0xca, + 0x56, 0x4f, 0x92, 0xf8, 0x42, 0x88, 0xca, 0x35, 0x5a, 0x96, 0x1a, 0xa6, + 0x72, 0x2d, 0xe8, 0xc6, 0xf2, 0xd7, 0x32, 0x6d, 0xb5, 0xae, 0x4e, 0x28, + 0x62, 0xdf, 0xe3, 0x02, 0x05, 0x3c, 0xc2, 0xe9, 0x5a, 0x13, 0xc0, 0x03, + 0x63, 0xb2, 0x62, 0x79, 0x45, 0x6b, 0x74, 0x73, 0x40, 0x5f, 0x4b, 0x5b, + 0x06, 0x73, 0x06, 0x9f, 0xb3, 0x3a, 0xe9, 0x2b, 0xe6, 0xf7, 0x04, 0xb4, + 0xe9, 0xd4, 0x0e, 0x38, 0xb7, 0xa8, 0x52, 0xf5, 0x7b, 0x74, 0xdc, 0xb4, + 0x2f, 0x34, 0x40, 0x41, 0x04, 0xfb, 0x8f, 0xb0, 0xf0, 0x5b, 0xad, 0x06, + 0xd4, 0x4c, 0xf6, 0xf7, 0x63, 0x20, 0x1b, 0x00, 0x1b, 0x79, 0xd0, 0x08, + 0x8c, 0x4c, 0xf9, 0x8a, 0x90, 0x2a, 0xae, 0xcb, 0x83, 0xaa, 0x75, 0x8d, + 0x76, 0x32, 0x07, 0xb3, 0x9a, 0x47, 0xbb, 0x02, 0x81, 0x80, 0x3c, 0x40, + 0x5f, 0x61, 0x71, 0x4a, 0x0a, 0xb7, 0x62, 0x65, 0xc5, 0x01, 0x37, 0xc2, + 0xc8, 0x54, 0x08, 0xc0, 0xff, 0x34, 0x9c, 0xf3, 0xf6, 0xae, 0xce, 0xd0, + 0x6a, 0xd6, 0x4a, 0x7d, 0x2f, 0xf1, 0x5f, 0x98, 0xde, 0x97, 0x23, 0xca, + 0x51, 0x29, 0xec, 0xcc, 0x57, 0xc2, 0xc3, 0xda, 0x1f, 0xab, 0xe4, 0x28, + 0x0d, 0x8f, 0x90, 0x46, 0xca, 0xfb, 0x15, 0x6e, 0xb6, 0x7b, 0x92, 0x83, + 0x70, 0x37, 0xad, 0x6a, 0x93, 0xa1, 0x24, 0x99, 0x0b, 0xb9, 0x7d, 0xe0, + 0x5b, 0x95, 0xc2, 0xf4, 0x01, 0x51, 0xed, 0x54, 0xde, 0x0c, 0x38, 0x71, + 0xca, 0xfd, 0xbf, 0x57, 0x10, 0xa3, 0x86, 0x1b, 0x92, 0x42, 0x0f, 0xf1, + 0xd6, 0xe4, 0x20, 0xa9, 0xbe, 0x28, 0x69, 0x06, 0xfc, 0x00, 0x1f, 0x54, + 0x25, 0x3a, 0x4e, 0x54, 0xe1, 0xdc, 0xb3, 0xe9, 0x1f, 0xe6, 0x64, 0x3b, + 0x95, 0x9e, 0xf5, 0x5c, 0xda, 0x2b, 0x02, 0x81, 0x80, 0x37, 0xe9, 0x74, + 0x25, 0xed, 0x4e, 0x34, 0xc5, 0x5e, 0xe3, 0x35, 0xae, 0x96, 0x18, 0x00, + 0x29, 0x8d, 0x68, 0x57, 0x2b, 0xed, 0x61, 0xcd, 0xe0, 0xb2, 0x1c, 0x7b, + 0x9e, 0xe0, 0xe0, 0xf0, 0xc0, 0xfe, 0xb7, 0x1c, 0x76, 0x89, 0xb5, 0x48, + 0x1e, 0x65, 0x90, 0x5f, 0xee, 0x20, 0x9d, 0xb2, 0xea, 0x1f, 0x43, 0xb5, + 0x42, 0x2e, 0x71, 0x2b, 0x99, 0xb8, 0x79, 0x3d, 0x65, 0x5f, 0x26, 0xae, + 0x62, 0x73, 0x65, 0x61, 0xd1, 0x45, 0x5a, 0x41, 0xfc, 0x85, 0x45, 0xcd, + 0xfc, 0xb3, 0xe9, 0x0a, 0xf6, 0xea, 0x18, 0x13, 0x98, 0xe6, 0xbe, 0x45, + 0x79, 0x13, 0x19, 0x35, 0x86, 0x55, 0xfe, 0x1b, 0x72, 0x9a, 0x7b, 0x98, + 0x38, 0xdc, 0xe7, 0xcb, 0x85, 0xfb, 0x0a, 0x8c, 0xe1, 0xcb, 0x66, 0x01, + 0x37, 0x28, 0x96, 0x89, 0x4c, 0x6d, 0xb5, 0xc2, 0x04, 0x04, 0x69, 0x10, + 0x1f, 0x72, 0x47, 0xe8, 0x3f, 0x02, 0x81, 0x80, 0x1e, 0x91, 0xcd, 0xad, + 0x31, 0x10, 0xa4, 0x7d, 0xb0, 0x32, 0x06, 0x9a, 0x31, 0x58, 0x5b, 0x08, + 0xad, 0x60, 0x51, 0x7b, 0x3e, 0xa3, 0xed, 0x81, 0x04, 0x0e, 0x7d, 0xc6, + 0x22, 0xdb, 0x9f, 0xd9, 0x72, 0xb6, 0x5b, 0xe9, 0x30, 0xa4, 0x2f, 0x1b, + 0x08, 0xba, 0x01, 0x1f, 0x63, 0xae, 0xd2, 0x77, 0x2a, 0xbe, 0xb8, 0x89, + 0x4a, 0xc7, 0xaf, 0x38, 0x55, 0xad, 0xf6, 0x2f, 0xb0, 0xee, 0x42, 0xa3, + 0x0a, 0x95, 0x5d, 0x6a, 0x31, 0xc4, 0x10, 0x7b, 0x68, 0xd5, 0xbd, 0x6a, + 0xaa, 0xb1, 0xe3, 0xe3, 0xd9, 0x3d, 0x5f, 0x42, 0x11, 0x51, 0xd0, 0x3e, + 0xc2, 0x10, 0xda, 0x13, 0x51, 0xa8, 0x47, 0x77, 0x6c, 0xdf, 0x70, 0x28, + 0x0b, 0xca, 0x27, 0x67, 0x20, 0xe6, 0xcb, 0x5c, 0x88, 0x48, 0x62, 0x2e, + 0x9a, 0x14, 0x72, 0x63, 0x98, 0x5d, 0xad, 0xeb, 0xdd, 0x28, 0xb5, 0x10, + 0x1d, 0x01, 0xea, 0x2c, + }, + { + 0x30, 0x82, 0x04, 0xbe, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x04, 0xa8, 0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, + 0x01, 0x00, 0xb1, 0xd9, 0xbc, 0x21, 0x62, 0xf6, 0xee, 0x9c, 0xb1, 0x4b, + 0xe2, 0x4d, 0x2f, 0xfa, 0x8a, 0x0e, 0x10, 0xd7, 0x9a, 0xe5, 0xb2, 0xca, + 0x8e, 0xd6, 0x9b, 0xa1, 0x41, 0x82, 0xdf, 0x6e, 0xc4, 0x0a, 0x66, 0xe0, + 0xd8, 0x47, 0x54, 0xae, 0xe2, 0x61, 0x65, 0x0f, 0x13, 0x95, 0xa7, 0x01, + 0x83, 0x30, 0x7d, 0xcf, 0x4a, 0xcf, 0x3c, 0xa7, 0x89, 0x35, 0x8e, 0x4a, + 0xef, 0x66, 0xf4, 0x62, 0xab, 0x81, 0x2d, 0xfd, 0xc7, 0xda, 0xfe, 0x71, + 0x7d, 0x35, 0x86, 0xf1, 0x7b, 0xde, 0x93, 0x4d, 0x07, 0x88, 0x81, 0xe0, + 0x8d, 0x1d, 0xb9, 0xfe, 0xb5, 0x24, 0xf5, 0x8d, 0x4e, 0xfd, 0x20, 0x27, + 0xc9, 0x19, 0x57, 0xf3, 0x7a, 0x32, 0x09, 0xbd, 0x77, 0xcc, 0x6a, 0xd8, + 0x9b, 0xd8, 0x81, 0xb8, 0x0e, 0xc4, 0xa2, 0x3a, 0x0b, 0x61, 0xf0, 0x29, + 0x41, 0x07, 0xbd, 0xe5, 0x96, 0x46, 0xcd, 0x67, 0x31, 0xa1, 0xd2, 0x67, + 0x91, 0x65, 0x5b, 0xff, 0xf6, 0x82, 0xb9, 0x4a, 0x8a, 0x41, 0x40, 0x87, + 0x15, 0x6e, 0xa7, 0x6a, 0xe6, 0xd6, 0x3d, 0x80, 0x0b, 0xcc, 0xb9, 0xe7, + 0x0e, 0x52, 0xe6, 0x26, 0x35, 0x8c, 0x04, 0x99, 0xf0, 0x5c, 0x60, 0x3e, + 0xf7, 0x33, 0x05, 0xd1, 0xc0, 0x52, 0xea, 0x20, 0xff, 0xdf, 0x7f, 0x30, + 0x83, 0xb7, 0x2a, 0x87, 0x1f, 0x6c, 0xf0, 0x28, 0x98, 0x27, 0x7c, 0x6a, + 0xe7, 0x1f, 0xbd, 0x53, 0x46, 0x7d, 0xd4, 0xf5, 0x6c, 0x40, 0x81, 0xbe, + 0x75, 0x66, 0xdc, 0x35, 0xd1, 0x1f, 0xc6, 0x3a, 0xfd, 0x96, 0xe9, 0x10, + 0xc4, 0xfe, 0x1e, 0x2f, 0x97, 0x47, 0x94, 0xf5, 0xe7, 0x65, 0x89, 0xf6, + 0xee, 0xa6, 0xd1, 0x88, 0x3d, 0x0c, 0x55, 0x7b, 0x21, 0x10, 0x90, 0x2d, + 0x0e, 0x76, 0x59, 0x30, 0xdb, 0x5d, 0x6b, 0xc3, 0x00, 0x17, 0xed, 0x3a, + 0x48, 0xad, 0x74, 0x02, 0xe9, 0x2f, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, + 0x82, 0x01, 0x00, 0x13, 0x80, 0xf0, 0x3b, 0xad, 0x11, 0xad, 0x5b, 0x2b, + 0xaa, 0x49, 0xb7, 0xf3, 0x42, 0x03, 0x6f, 0xc5, 0x73, 0x0f, 0x7e, 0x8f, + 0x1b, 0xdd, 0x8c, 0x35, 0x66, 0xfa, 0xbb, 0x32, 0x6b, 0x80, 0x3e, 0x2f, + 0xc8, 0x0a, 0x8f, 0xcf, 0xfe, 0x99, 0xbe, 0xda, 0x89, 0x4b, 0xb9, 0x86, + 0xa6, 0x08, 0xd0, 0x01, 0x8c, 0x01, 0x05, 0x36, 0xfe, 0x9c, 0x72, 0x6e, + 0x17, 0x53, 0xa7, 0xb3, 0xb7, 0x3a, 0x5d, 0xa1, 0x40, 0x8a, 0x71, 0x3e, + 0xa4, 0x0d, 0x7a, 0x18, 0x41, 0x55, 0x37, 0x34, 0x6a, 0x27, 0x20, 0x56, + 0x35, 0xdc, 0xe3, 0xdc, 0xa8, 0xcd, 0x0e, 0xaf, 0x8a, 0xfe, 0x95, 0x8d, + 0xb8, 0x2f, 0x3a, 0x83, 0x45, 0xa5, 0x29, 0xe5, 0x9d, 0x8c, 0x8c, 0xb3, + 0x4e, 0xeb, 0xd0, 0x53, 0x71, 0x6e, 0xd5, 0x2b, 0x81, 0x38, 0x0c, 0x56, + 0x7c, 0xff, 0xaf, 0xac, 0xa8, 0x87, 0xd1, 0x5d, 0xe7, 0x2a, 0x2f, 0x09, + 0x0a, 0x1e, 0x18, 0xf1, 0x02, 0xe5, 0x2c, 0xc6, 0x9e, 0x5c, 0x91, 0xf1, + 0xdf, 0xa1, 0x0a, 0x0a, 0x50, 0xa6, 0x67, 0x77, 0xc7, 0x8b, 0xb1, 0xdf, + 0x49, 0x1e, 0xbd, 0x8c, 0x45, 0x89, 0x2b, 0xfe, 0x32, 0xcc, 0xb4, 0x62, + 0xed, 0xdc, 0xec, 0xd9, 0xfe, 0xf1, 0x18, 0x3a, 0x8f, 0x92, 0x0a, 0x6c, + 0xc2, 0x84, 0xfc, 0xe3, 0x8a, 0x88, 0xdc, 0x04, 0x31, 0x83, 0x41, 0x97, + 0x04, 0xeb, 0x32, 0x42, 0x98, 0x63, 0x6c, 0x93, 0xc7, 0x63, 0x75, 0x0a, + 0xf7, 0xfc, 0x3a, 0xe7, 0x57, 0x88, 0xa8, 0x98, 0x8c, 0xf0, 0xf4, 0x74, + 0x18, 0xda, 0x27, 0x32, 0x92, 0x95, 0x6c, 0x74, 0x76, 0x52, 0x80, 0x5a, + 0x7d, 0x6c, 0xa1, 0x9f, 0xd1, 0xf1, 0x24, 0x1b, 0x70, 0x84, 0x70, 0xe3, + 0xd5, 0x91, 0xfa, 0x5d, 0xd2, 0x0f, 0xda, 0x48, 0x41, 0x7c, 0x64, 0xd6, + 0x92, 0xdb, 0x1e, 0x67, 0x29, 0xe4, 0x69, 0x02, 0x81, 0x81, 0x00, 0xd5, + 0x9b, 0x27, 0x2d, 0x63, 0x7c, 0x4d, 0xb8, 0x99, 0xe4, 0xd5, 0x03, 0xaa, + 0x0b, 0xea, 0x51, 0x29, 0x7d, 0x4a, 0x46, 0xe3, 0xa5, 0xf4, 0x49, 0xdd, + 0x5d, 0x0d, 0x4c, 0x36, 0xc5, 0x95, 0x8e, 0xa7, 0x06, 0x3f, 0x0d, 0xf8, + 0xda, 0xc1, 0xde, 0x05, 0xb0, 0x64, 0x84, 0x0e, 0xea, 0x97, 0x20, 0x3b, + 0x24, 0xba, 0xdd, 0xf0, 0x29, 0x21, 0x91, 0x1a, 0x58, 0x76, 0x3b, 0x15, + 0x36, 0x99, 0xa7, 0x49, 0xfb, 0x8a, 0x6a, 0x28, 0x9e, 0x68, 0x84, 0xb3, + 0x7c, 0x93, 0x2a, 0xd7, 0x2d, 0xc9, 0x0a, 0x58, 0xa1, 0xe3, 0x4f, 0x5c, + 0x5b, 0x7a, 0x08, 0x33, 0xdc, 0x88, 0xad, 0x7a, 0x8e, 0x2b, 0x0e, 0x56, + 0x49, 0x59, 0x45, 0xfd, 0x1a, 0x84, 0x2a, 0x57, 0x40, 0xf3, 0x70, 0x03, + 0x86, 0xcd, 0x3d, 0xf3, 0xd4, 0x98, 0x86, 0xaa, 0xa1, 0x9f, 0x04, 0x74, + 0xf4, 0xc2, 0x83, 0x6a, 0x0b, 0xc0, 0xab, 0x02, 0x81, 0x81, 0x00, 0xd5, + 0x25, 0xec, 0x3a, 0xa3, 0x6c, 0x9b, 0x4d, 0x23, 0xcf, 0x2b, 0xf3, 0x60, + 0x36, 0x2d, 0x03, 0xcc, 0x1f, 0xf3, 0xd3, 0x0e, 0xcc, 0xe5, 0x45, 0xb3, + 0xee, 0xc5, 0x8d, 0x81, 0x7e, 0xfe, 0x2f, 0x78, 0x31, 0x3e, 0x23, 0xfe, + 0x60, 0x0b, 0xc6, 0x18, 0xae, 0x04, 0xac, 0xfe, 0x7b, 0xbd, 0x58, 0x30, + 0x62, 0xc6, 0xfb, 0x55, 0x3e, 0xbd, 0x1e, 0x28, 0x53, 0x4d, 0xf2, 0x29, + 0xeb, 0x86, 0x07, 0x4b, 0x2d, 0x0f, 0xb6, 0xa9, 0x99, 0x30, 0x19, 0xc1, + 0x78, 0x04, 0xd3, 0x46, 0xe0, 0x7e, 0x56, 0x48, 0xd1, 0xb8, 0x61, 0xcc, + 0xb4, 0xc9, 0x04, 0x7e, 0x5e, 0x50, 0x24, 0x22, 0x5b, 0x3c, 0x03, 0x87, + 0xf6, 0x6b, 0xfc, 0x78, 0xf8, 0xe3, 0xf2, 0x09, 0x75, 0xfc, 0xfd, 0xd3, + 0xfa, 0xc9, 0x93, 0x4e, 0x15, 0xda, 0x2c, 0xea, 0x3e, 0x75, 0x1e, 0x55, + 0xc4, 0x4e, 0x5f, 0xd0, 0x9b, 0x61, 0x8d, 0x02, 0x81, 0x81, 0x00, 0xb0, + 0xfa, 0x52, 0xd4, 0x65, 0x58, 0x09, 0x21, 0x08, 0xc8, 0xb2, 0xc0, 0xcd, + 0x73, 0xff, 0xd8, 0xed, 0x12, 0x34, 0xa7, 0x55, 0x58, 0x1b, 0x75, 0xd3, + 0xa5, 0x94, 0x2d, 0xf0, 0x37, 0x76, 0x79, 0x71, 0xe3, 0x57, 0xfc, 0xe0, + 0x91, 0x87, 0x0c, 0xc8, 0x13, 0xe4, 0xc4, 0x11, 0x83, 0xc6, 0x60, 0xda, + 0xff, 0xcc, 0x9f, 0xb3, 0xaa, 0xb0, 0x78, 0x03, 0xa7, 0x05, 0x96, 0x60, + 0x36, 0x13, 0xca, 0xaa, 0x1e, 0x72, 0x09, 0x10, 0xcc, 0x66, 0x76, 0x84, + 0x2b, 0x93, 0xd7, 0x6b, 0xdf, 0x30, 0x71, 0x95, 0x0b, 0xd4, 0x44, 0xdd, + 0x95, 0xa1, 0x1f, 0x08, 0xb1, 0x10, 0xc1, 0x03, 0x08, 0xa5, 0x9d, 0xc4, + 0x5a, 0x13, 0x50, 0x4c, 0xab, 0x5a, 0x93, 0xa6, 0x25, 0x57, 0x4d, 0x18, + 0x6e, 0xe8, 0xc0, 0x0a, 0xb4, 0xbb, 0x06, 0xe0, 0x7a, 0xf6, 0x40, 0x80, + 0x12, 0x86, 0xd2, 0xd0, 0xdb, 0x19, 0xef, 0x02, 0x81, 0x81, 0x00, 0xd2, + 0xda, 0xf1, 0x16, 0x11, 0x15, 0x1f, 0x8c, 0xf0, 0x1f, 0xc2, 0x1a, 0xc0, + 0xd2, 0xd7, 0x35, 0x2a, 0x79, 0x71, 0x7b, 0x18, 0xe9, 0x18, 0xe6, 0x1b, + 0x59, 0xd9, 0x94, 0x62, 0xc6, 0x7e, 0x2d, 0xf7, 0x78, 0xbf, 0x40, 0x5c, + 0x24, 0x27, 0x81, 0x90, 0x68, 0x95, 0xb7, 0x8d, 0x08, 0x5c, 0x07, 0x21, + 0xd6, 0x95, 0x81, 0x8c, 0x92, 0xc4, 0x03, 0x51, 0x61, 0x17, 0x7a, 0xb8, + 0x7d, 0x85, 0xd9, 0x31, 0x45, 0x22, 0x86, 0x50, 0xc0, 0x7a, 0x15, 0x00, + 0x8e, 0xfa, 0x37, 0x07, 0xa6, 0x8d, 0x54, 0x4e, 0x35, 0x29, 0x71, 0x96, + 0x50, 0x41, 0x8b, 0x70, 0x77, 0x5c, 0x50, 0x62, 0xac, 0x5e, 0x72, 0x1f, + 0xce, 0x66, 0x85, 0x1c, 0x27, 0x1b, 0x58, 0x14, 0x83, 0xe4, 0x02, 0x35, + 0xf1, 0xa1, 0xf7, 0x57, 0x07, 0xc7, 0x0d, 0x27, 0xad, 0x21, 0xaf, 0x20, + 0xa9, 0x51, 0xde, 0xf7, 0x3e, 0xbf, 0x01, 0x02, 0x81, 0x80, 0x01, 0xda, + 0x50, 0xd0, 0x7d, 0x59, 0x03, 0xe6, 0xb8, 0x0a, 0xe9, 0x9c, 0xc0, 0x4c, + 0x39, 0xe5, 0x28, 0xd7, 0x80, 0x54, 0x94, 0x04, 0xbd, 0xa1, 0x20, 0x5a, + 0x54, 0x61, 0x36, 0xd5, 0xc3, 0x7a, 0x93, 0x51, 0x0a, 0xca, 0x7d, 0x5b, + 0x65, 0xd5, 0xd5, 0x82, 0x96, 0x60, 0x8a, 0x52, 0x11, 0x62, 0x8a, 0x85, + 0xc2, 0xf0, 0x01, 0x3d, 0xab, 0xfc, 0xb1, 0xeb, 0x2f, 0xd2, 0x58, 0x74, + 0x2c, 0xfb, 0x53, 0x53, 0xed, 0x43, 0x96, 0x49, 0x16, 0x12, 0xba, 0xa9, + 0xa0, 0xd1, 0xb9, 0x86, 0x39, 0x7c, 0x58, 0xb5, 0x92, 0xae, 0xcc, 0x1e, + 0xf5, 0xdb, 0xcf, 0xbe, 0xd5, 0x71, 0x3e, 0x84, 0x52, 0xbc, 0xab, 0xe6, + 0x5d, 0x1f, 0xae, 0x9c, 0x16, 0x38, 0x38, 0x99, 0xde, 0xc0, 0x31, 0xd8, + 0x0d, 0xc2, 0xd7, 0x14, 0xb2, 0xe1, 0x8d, 0x28, 0xbe, 0x7a, 0x1c, 0xc3, + 0x82, 0x42, 0x0b, 0x47, 0x87, 0x10, + }, + { + 0x30, 0x82, 0x04, 0xbe, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x04, 0xa8, 0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, + 0x01, 0x00, 0xe0, 0x8c, 0x17, 0xe8, 0xd0, 0xf4, 0x18, 0x7f, 0x90, 0x2d, + 0x44, 0x5c, 0x31, 0x1f, 0x57, 0x36, 0x0f, 0x5b, 0x3f, 0x37, 0x0f, 0xda, + 0x5b, 0x80, 0xe3, 0x53, 0xe8, 0xf8, 0x0a, 0x75, 0x6a, 0xe0, 0xb3, 0xa9, + 0xea, 0x29, 0x82, 0x31, 0x24, 0x19, 0xc0, 0x34, 0x7e, 0x75, 0x88, 0x1a, + 0x3c, 0xdd, 0x30, 0x6c, 0x6a, 0x04, 0x68, 0xae, 0xa0, 0x04, 0x16, 0x40, + 0x6e, 0xdf, 0xe6, 0x1a, 0xb1, 0x7d, 0x86, 0x4b, 0x0f, 0x3b, 0x0c, 0x3c, + 0x73, 0xbe, 0xc5, 0x8e, 0x94, 0x72, 0x1f, 0x3e, 0x9e, 0x6d, 0x45, 0x5e, + 0xab, 0x77, 0xd2, 0xe2, 0x55, 0x67, 0xb2, 0x25, 0xce, 0x26, 0x2f, 0x6a, + 0x1d, 0x01, 0xac, 0xd4, 0xfe, 0x2b, 0x07, 0x8e, 0x38, 0xb6, 0x08, 0x5b, + 0xc3, 0xdd, 0xbc, 0xe7, 0x66, 0x70, 0x92, 0x80, 0x41, 0x17, 0x7e, 0xd7, + 0x68, 0xca, 0x6d, 0x8b, 0xc9, 0xc5, 0x74, 0xf5, 0xd2, 0xb7, 0xab, 0xcd, + 0x87, 0xf3, 0x72, 0x85, 0xd6, 0xcd, 0xc6, 0xd1, 0xf4, 0x5d, 0xfe, 0xb9, + 0xd5, 0x3f, 0x38, 0x38, 0x21, 0x24, 0x89, 0x68, 0x49, 0xd9, 0xba, 0xcd, + 0x80, 0x99, 0xc7, 0x04, 0x51, 0xec, 0x89, 0x8c, 0x42, 0x7c, 0x1b, 0xae, + 0x5a, 0xbd, 0x0d, 0x76, 0x35, 0x9b, 0xb2, 0xee, 0x54, 0x24, 0x96, 0xe2, + 0x41, 0x0d, 0x29, 0x0d, 0xd2, 0xa4, 0xbf, 0x63, 0x84, 0x9a, 0xcb, 0x06, + 0xe0, 0x43, 0xd7, 0x70, 0x47, 0xac, 0xe7, 0x7d, 0xf4, 0x14, 0x89, 0xa1, + 0xcc, 0xa4, 0x14, 0x14, 0x5d, 0xf6, 0x1b, 0xf4, 0xe8, 0x8c, 0xda, 0xbe, + 0xad, 0xf5, 0xe0, 0x2e, 0x10, 0xdc, 0xe2, 0xb6, 0x62, 0xd0, 0x7b, 0x33, + 0x18, 0x98, 0x10, 0x70, 0xf5, 0x6c, 0x39, 0x04, 0x51, 0xd9, 0x08, 0x2a, + 0xa3, 0xa1, 0xbe, 0x8a, 0x0b, 0x14, 0xf6, 0x8b, 0xa5, 0x80, 0x0e, 0xaa, + 0xb0, 0x22, 0x2a, 0x5c, 0x26, 0x11, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, + 0x82, 0x01, 0x00, 0x71, 0x3b, 0x33, 0xf3, 0x5b, 0x2b, 0x3c, 0x0f, 0x57, + 0x8c, 0xfc, 0x23, 0xe1, 0x41, 0x84, 0xa0, 0xe6, 0x2d, 0x85, 0x3b, 0x26, + 0xeb, 0x5d, 0xa9, 0x28, 0xf9, 0x6f, 0x49, 0x65, 0x3c, 0xc0, 0xa0, 0x7e, + 0xf8, 0xf7, 0xf3, 0xe6, 0x8b, 0x11, 0x41, 0x4b, 0x03, 0x2d, 0xa1, 0xe6, + 0xf8, 0x14, 0x29, 0x15, 0xfa, 0x15, 0x9c, 0x13, 0x8d, 0xbb, 0x73, 0x54, + 0x25, 0x39, 0xc3, 0xfe, 0x2d, 0x07, 0xc3, 0xdc, 0xac, 0x8d, 0x09, 0xa1, + 0xa9, 0xfa, 0x54, 0x01, 0x1b, 0x5d, 0x10, 0x0f, 0x61, 0x6c, 0x52, 0x7d, + 0xe0, 0xb7, 0x66, 0xbd, 0x19, 0x15, 0x28, 0x2d, 0x55, 0x93, 0x16, 0xfe, + 0x61, 0xec, 0x2e, 0xa4, 0x10, 0xe3, 0xfa, 0x9e, 0xdc, 0xc1, 0x0c, 0x4a, + 0x3a, 0xbc, 0x12, 0x5c, 0xe7, 0xf3, 0x68, 0x60, 0xf7, 0xfb, 0xda, 0xf5, + 0x43, 0x7a, 0xbc, 0x59, 0x71, 0x5c, 0x56, 0x0e, 0x1c, 0xc8, 0x1c, 0x94, + 0x99, 0x8a, 0x95, 0x8b, 0x2d, 0x65, 0x0a, 0xc8, 0x6f, 0xdb, 0xdd, 0x85, + 0xcf, 0x6f, 0x38, 0x47, 0xef, 0x27, 0x21, 0xb7, 0x7f, 0x8d, 0xe9, 0xf4, + 0x3c, 0x15, 0xcb, 0x1c, 0x03, 0x91, 0xa6, 0x0f, 0x3f, 0x61, 0x7f, 0xa5, + 0x32, 0xff, 0x10, 0x14, 0x1c, 0xdc, 0xc3, 0xb9, 0xbb, 0x51, 0xb7, 0xe9, + 0x91, 0xf2, 0xff, 0x6e, 0x4b, 0x35, 0x90, 0x24, 0xf7, 0xe9, 0x39, 0x94, + 0x17, 0x36, 0x9d, 0x0c, 0xe7, 0x57, 0x96, 0x64, 0x35, 0xc1, 0x7c, 0x57, + 0x15, 0xe8, 0xec, 0x31, 0x2f, 0xac, 0xed, 0x08, 0x0a, 0x02, 0x39, 0xb3, + 0x22, 0x2c, 0x47, 0x97, 0x06, 0xa8, 0x15, 0x8f, 0x9f, 0x0d, 0x50, 0x95, + 0x70, 0x7b, 0xfd, 0x85, 0x78, 0x74, 0xfa, 0x3d, 0x37, 0x54, 0xbd, 0xfa, + 0xfe, 0x76, 0x8b, 0xc3, 0x60, 0xbc, 0x47, 0x57, 0x36, 0x4f, 0xb0, 0x3c, + 0x35, 0xc5, 0x72, 0x22, 0xb3, 0xac, 0xe1, 0x02, 0x81, 0x81, 0x00, 0xfb, + 0x6d, 0xc5, 0x79, 0x03, 0x62, 0x90, 0x75, 0x15, 0x2a, 0x9a, 0x0d, 0x90, + 0x6e, 0x03, 0xb8, 0x86, 0x52, 0x40, 0x7a, 0x6b, 0x4f, 0x46, 0x37, 0xa9, + 0xf8, 0xc4, 0x4d, 0x73, 0x1a, 0x6e, 0xd8, 0x1a, 0xb5, 0xfa, 0x75, 0x9c, + 0xc6, 0x73, 0x51, 0x60, 0xed, 0x08, 0x2d, 0xe6, 0x23, 0x62, 0x60, 0xb4, + 0x42, 0x6c, 0x36, 0xac, 0x34, 0x58, 0xf1, 0x9f, 0x49, 0x75, 0xa9, 0x22, + 0xb0, 0x97, 0x91, 0x37, 0x44, 0x40, 0x05, 0x34, 0x34, 0x61, 0x65, 0x8f, + 0xae, 0x4e, 0x4f, 0x6b, 0x1f, 0x1f, 0x2d, 0x89, 0x97, 0x2b, 0x48, 0x98, + 0x33, 0x4b, 0x79, 0x46, 0xaa, 0x8c, 0x96, 0x04, 0xfc, 0xe3, 0xe2, 0xbb, + 0x85, 0x96, 0x40, 0xa6, 0xe5, 0x4b, 0xb0, 0xa3, 0x26, 0x80, 0x98, 0x66, + 0xf1, 0x36, 0xb5, 0xdb, 0xa0, 0x9b, 0x17, 0x8a, 0x91, 0x88, 0xb8, 0x78, + 0x1a, 0xc0, 0x24, 0x93, 0x3c, 0x50, 0xa3, 0x02, 0x81, 0x81, 0x00, 0xe4, + 0xa1, 0x34, 0xf1, 0xde, 0x5a, 0x1d, 0xf0, 0xba, 0x1f, 0x8a, 0xcb, 0x9d, + 0x7a, 0xb0, 0xee, 0x90, 0x60, 0x56, 0xa6, 0x49, 0x38, 0x24, 0xb6, 0xaf, + 0x6d, 0xb9, 0x93, 0xe3, 0xc1, 0xdf, 0x49, 0x79, 0x05, 0x20, 0x0f, 0xdd, + 0x04, 0xbf, 0xcc, 0x33, 0x09, 0x13, 0xe4, 0x7c, 0x2d, 0x33, 0xec, 0xb2, + 0xc4, 0xca, 0xd9, 0x93, 0x82, 0x9f, 0xf8, 0xb2, 0xd8, 0xf1, 0x00, 0x2d, + 0xf0, 0x72, 0x0f, 0x9d, 0xb8, 0x9b, 0x29, 0x8f, 0x9f, 0x4b, 0xc5, 0xe6, + 0xa9, 0x30, 0xd9, 0xf1, 0x2f, 0x83, 0x19, 0xb6, 0xef, 0xa9, 0x88, 0x03, + 0x9d, 0x4c, 0x79, 0x3a, 0x47, 0x59, 0xab, 0xa1, 0xe3, 0x29, 0xe8, 0x75, + 0xbd, 0x1d, 0x58, 0x54, 0x9d, 0x7d, 0xd6, 0x37, 0x39, 0xb4, 0x55, 0x44, + 0x9b, 0x24, 0x16, 0xc1, 0x49, 0xfd, 0x77, 0xc9, 0x52, 0x9d, 0x61, 0x2e, + 0xc6, 0xf4, 0x54, 0x49, 0x2e, 0xb5, 0xbb, 0x02, 0x81, 0x81, 0x00, 0xd7, + 0x9f, 0x90, 0x56, 0xf3, 0xbc, 0x37, 0xbe, 0xf1, 0x64, 0x13, 0xb1, 0xc6, + 0x47, 0x53, 0x11, 0x90, 0xc0, 0x41, 0x63, 0xbb, 0x5b, 0xc5, 0x10, 0x3f, + 0xc8, 0x84, 0x31, 0x17, 0xa1, 0x77, 0xa6, 0x05, 0x62, 0x43, 0x65, 0x52, + 0x0a, 0x2f, 0x23, 0x7f, 0x48, 0x48, 0x57, 0x74, 0xa2, 0xab, 0xdf, 0xdb, + 0x37, 0x8b, 0xae, 0x67, 0x88, 0x8d, 0x2e, 0xca, 0x38, 0x29, 0x9b, 0xdf, + 0x90, 0x2a, 0xab, 0xf3, 0x05, 0xa9, 0x11, 0xcd, 0x6f, 0x8e, 0x87, 0x96, + 0xed, 0x19, 0x63, 0xaa, 0xae, 0x8a, 0x02, 0x77, 0x99, 0x19, 0x19, 0xac, + 0xcd, 0x70, 0xd0, 0x94, 0xf5, 0xf6, 0x93, 0x41, 0xbb, 0xcf, 0x83, 0xc6, + 0xe6, 0x0d, 0xb1, 0x6c, 0x70, 0x00, 0xb5, 0x7f, 0x75, 0xa9, 0x9b, 0x09, + 0xcd, 0x9b, 0xe9, 0xe2, 0x6c, 0x9e, 0x15, 0x56, 0x54, 0xf1, 0x37, 0xa2, + 0x68, 0xee, 0x97, 0x6f, 0x5d, 0xae, 0x99, 0x02, 0x81, 0x81, 0x00, 0x99, + 0xae, 0x77, 0x1d, 0x62, 0x1e, 0x67, 0x63, 0xca, 0x68, 0xab, 0x6e, 0x85, + 0x13, 0xd4, 0xdc, 0xf7, 0xb5, 0x9a, 0x69, 0x3a, 0x6c, 0x7f, 0xd9, 0x41, + 0x00, 0xdb, 0xae, 0xba, 0x79, 0x36, 0x73, 0x77, 0xe2, 0xe7, 0x13, 0x62, + 0xb8, 0xef, 0x87, 0xfe, 0x82, 0x4a, 0x99, 0xda, 0x92, 0xb4, 0x05, 0x96, + 0x81, 0x4b, 0xed, 0x87, 0x07, 0x56, 0x2a, 0xe1, 0x65, 0x8c, 0x52, 0x57, + 0x8e, 0xf9, 0xcc, 0xdf, 0x73, 0x18, 0xf1, 0x8d, 0xf6, 0x41, 0x03, 0xed, + 0x31, 0xab, 0x81, 0xc4, 0x6a, 0xf0, 0x90, 0x17, 0x74, 0xf0, 0x1b, 0x5a, + 0x7a, 0xa0, 0xce, 0x18, 0x4b, 0x9f, 0xce, 0x0c, 0x0a, 0xdb, 0xbf, 0xc3, + 0x6c, 0xf4, 0xcd, 0xcc, 0x4a, 0xf5, 0x2c, 0x4a, 0x81, 0x02, 0x64, 0x12, + 0xb2, 0xca, 0x46, 0x75, 0x75, 0x1b, 0xe9, 0x2a, 0x2d, 0xdb, 0x56, 0x59, + 0x10, 0x29, 0x6a, 0x82, 0xaa, 0xe0, 0x43, 0x02, 0x81, 0x80, 0x6e, 0x4f, + 0x83, 0xd9, 0xb0, 0xe7, 0x8a, 0x2e, 0x86, 0x9a, 0x45, 0x86, 0x9f, 0xd9, + 0x3b, 0x4b, 0xfc, 0x46, 0xd8, 0x57, 0x82, 0x5a, 0x90, 0xe6, 0x29, 0xf6, + 0xcf, 0x19, 0xf1, 0x41, 0x64, 0xa6, 0x66, 0x7e, 0xb2, 0xeb, 0x29, 0xa1, + 0x01, 0x9c, 0x28, 0xce, 0xae, 0xdd, 0xaa, 0x1d, 0x3d, 0x6f, 0xb0, 0x75, + 0xaf, 0x0f, 0xc0, 0x38, 0xc4, 0x45, 0x78, 0x15, 0xbf, 0x86, 0x8c, 0x19, + 0xce, 0xb6, 0x29, 0xdf, 0xa0, 0xaf, 0xbb, 0x8c, 0xae, 0xc6, 0x85, 0xd3, + 0x93, 0x4a, 0x2c, 0x26, 0x10, 0x36, 0x39, 0x9a, 0x75, 0x78, 0x56, 0x46, + 0xf4, 0xed, 0x3c, 0xb2, 0x2c, 0xb7, 0x31, 0xf1, 0x6d, 0xbb, 0xb3, 0xc5, + 0xea, 0x95, 0xf9, 0xcd, 0x8f, 0x31, 0xb6, 0xfd, 0x77, 0x20, 0x90, 0x9c, + 0xd7, 0xe5, 0x58, 0x6d, 0xe6, 0xd3, 0xd9, 0x1b, 0x34, 0x3e, 0x30, 0x24, + 0x72, 0xc2, 0x5d, 0xd8, 0xf9, 0x76, + }, + { + 0x30, 0x82, 0x04, 0xbd, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x04, 0xa7, 0x30, 0x82, 0x04, 0xa3, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, + 0x01, 0x00, 0xcb, 0x23, 0x69, 0xbc, 0x10, 0x8d, 0x7d, 0x50, 0x4b, 0xae, + 0xa8, 0xea, 0xa5, 0x32, 0xe1, 0xda, 0x93, 0x77, 0x03, 0x3e, 0x04, 0x8d, + 0xfa, 0x1a, 0x6d, 0x91, 0xd7, 0xd3, 0x30, 0x90, 0x0d, 0x2e, 0x4e, 0x7a, + 0xf6, 0xdd, 0xd6, 0x90, 0x90, 0xad, 0xf1, 0x48, 0x22, 0x84, 0xe7, 0xb7, + 0x7c, 0x33, 0x45, 0x13, 0x6f, 0x6c, 0xad, 0x86, 0xf7, 0xcf, 0xf7, 0x50, + 0x53, 0x96, 0x2c, 0xbd, 0xdb, 0x46, 0x2a, 0xfb, 0xa1, 0xae, 0x34, 0xcd, + 0x2a, 0xc0, 0xdc, 0xee, 0xc5, 0x06, 0x9c, 0x36, 0x3f, 0x1e, 0x10, 0xc1, + 0x1c, 0x6b, 0x25, 0xb3, 0xdf, 0xd4, 0xcb, 0x13, 0x3e, 0xc6, 0x5e, 0x3b, + 0xcf, 0xde, 0x10, 0x14, 0x54, 0x5d, 0xa4, 0xb5, 0x4d, 0x0d, 0x21, 0xd6, + 0xd9, 0xb7, 0xea, 0x3f, 0x76, 0x58, 0x0f, 0x2b, 0x0a, 0x4a, 0x91, 0x86, + 0x2d, 0x65, 0x01, 0xab, 0x27, 0x23, 0x70, 0x0f, 0xf3, 0xfc, 0xb5, 0xde, + 0x02, 0x64, 0xb1, 0x63, 0x42, 0x61, 0xa1, 0x81, 0x05, 0x63, 0x4c, 0xb8, + 0x86, 0x3b, 0xeb, 0xbe, 0xb3, 0xda, 0xc6, 0x2a, 0xda, 0x1b, 0x36, 0x3f, + 0xa3, 0xa7, 0xa5, 0xcc, 0xa4, 0x54, 0x4a, 0x6e, 0xcf, 0xf2, 0x28, 0xdb, + 0x71, 0x64, 0x2a, 0x8d, 0x0e, 0xb5, 0x9c, 0xd7, 0x34, 0x4f, 0xc5, 0x2d, + 0x64, 0x2d, 0x5b, 0xe6, 0x4b, 0x12, 0x02, 0x1f, 0xfd, 0xdf, 0x13, 0xf6, + 0x47, 0xe8, 0x1f, 0x7d, 0x91, 0x77, 0x0e, 0x72, 0xd7, 0xbd, 0xab, 0x24, + 0x36, 0x21, 0x9f, 0xac, 0x69, 0x5f, 0x7a, 0x65, 0xb2, 0x2e, 0x78, 0x89, + 0x72, 0x60, 0xd0, 0x6f, 0x11, 0x93, 0x42, 0x1d, 0xbc, 0xa3, 0x87, 0x6e, + 0xd8, 0x23, 0x93, 0x35, 0xbb, 0x6b, 0x9a, 0xcb, 0x3a, 0xc5, 0x98, 0x90, + 0xb5, 0xf1, 0xb1, 0xfb, 0x3d, 0xd9, 0xd4, 0xd4, 0x91, 0x18, 0x75, 0x55, + 0xf0, 0xb5, 0x92, 0x50, 0x1d, 0x9d, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, + 0x82, 0x01, 0x00, 0x13, 0xed, 0x64, 0x68, 0xf3, 0x82, 0xbd, 0xc1, 0x4b, + 0xbb, 0x55, 0x8c, 0xb7, 0x29, 0xf7, 0xa7, 0xf6, 0x81, 0x5d, 0x4b, 0x0f, + 0x2d, 0x52, 0x54, 0x97, 0x5d, 0x10, 0x3c, 0x02, 0x6e, 0x8f, 0x2c, 0x74, + 0xbc, 0x71, 0x4d, 0x2f, 0xc8, 0x3c, 0xb9, 0x23, 0x8b, 0x3c, 0xff, 0xa6, + 0xaf, 0x84, 0x3a, 0x8f, 0x53, 0x29, 0xc2, 0x12, 0x7f, 0xc3, 0xfd, 0x9d, + 0xb8, 0x3e, 0x8a, 0xe3, 0xd6, 0xb8, 0x14, 0xf9, 0x10, 0x78, 0x3a, 0xdb, + 0xa9, 0x28, 0xc1, 0x84, 0xaa, 0xd3, 0x9f, 0x23, 0x61, 0xda, 0xed, 0x3b, + 0x1d, 0x86, 0x89, 0x28, 0x48, 0xad, 0xf3, 0x74, 0xfc, 0x6d, 0x53, 0x0e, + 0x59, 0x75, 0xb9, 0xc6, 0x00, 0x08, 0x8a, 0xab, 0x26, 0xb1, 0xe9, 0x50, + 0xe1, 0x7f, 0xb8, 0x1f, 0xfd, 0x9b, 0xc1, 0x65, 0xb3, 0x86, 0x2d, 0xec, + 0x7a, 0xaa, 0x27, 0x57, 0x73, 0x8f, 0xc2, 0x55, 0x32, 0xeb, 0x6f, 0x39, + 0x31, 0xa0, 0x4a, 0x17, 0x55, 0xa5, 0x03, 0xbb, 0x34, 0xf0, 0xa9, 0x17, + 0xb7, 0x2f, 0x23, 0x83, 0x2c, 0xf7, 0x4e, 0x3f, 0x78, 0x2e, 0xaf, 0xe8, + 0x15, 0xd4, 0xf6, 0xb3, 0xb3, 0x57, 0x1c, 0x2b, 0x71, 0x1a, 0xae, 0x04, + 0x86, 0xdd, 0xf1, 0x53, 0x3b, 0xbe, 0xa1, 0x71, 0xfb, 0x6f, 0xff, 0x77, + 0xbb, 0x05, 0x53, 0x96, 0xae, 0xb2, 0xb2, 0xae, 0xc9, 0x28, 0x55, 0x0e, + 0x28, 0xbe, 0x36, 0x0e, 0xb7, 0xe1, 0x77, 0x44, 0xba, 0x58, 0x3f, 0xa1, + 0xff, 0xf1, 0xa1, 0x28, 0xa2, 0xd1, 0x31, 0x13, 0x46, 0x5c, 0x24, 0x89, + 0x72, 0x35, 0x86, 0x17, 0x3a, 0x28, 0x87, 0x7b, 0x79, 0xfa, 0xed, 0xc0, + 0xa8, 0x81, 0x11, 0x90, 0x49, 0xf2, 0x7e, 0xda, 0xc9, 0x93, 0x83, 0x66, + 0xd6, 0xd5, 0x67, 0x9c, 0x96, 0x11, 0x9f, 0x86, 0xca, 0xcb, 0x41, 0xf5, + 0xbb, 0x97, 0x53, 0xcf, 0x96, 0x71, 0x0d, 0x02, 0x81, 0x81, 0x00, 0xea, + 0xa0, 0x38, 0x53, 0xf3, 0xac, 0x5c, 0x3b, 0x57, 0xfe, 0xe0, 0xb9, 0x76, + 0x15, 0x22, 0x7e, 0xab, 0xd5, 0x6a, 0x5c, 0xec, 0xa4, 0x09, 0x56, 0x88, + 0xd8, 0xde, 0x33, 0x14, 0x78, 0xca, 0xcb, 0xb7, 0xd4, 0x2a, 0x3d, 0x6b, + 0xf8, 0xcf, 0x2b, 0xc8, 0x37, 0xf2, 0x26, 0x00, 0x96, 0x53, 0xe2, 0x19, + 0xd7, 0xf4, 0x14, 0x5e, 0x9a, 0x3b, 0x4f, 0xc3, 0x24, 0x39, 0x0a, 0xc7, + 0x9e, 0xa3, 0xd8, 0x0d, 0x17, 0xf7, 0xa6, 0xf7, 0xd0, 0x41, 0xa8, 0x52, + 0x82, 0xa8, 0x2e, 0x4a, 0x52, 0xbd, 0x38, 0x72, 0x55, 0xcd, 0xf1, 0xfa, + 0xa3, 0x34, 0x5e, 0x69, 0x81, 0xa9, 0x7e, 0x96, 0x94, 0x61, 0xd0, 0x61, + 0x17, 0xb4, 0x71, 0x37, 0xdf, 0xa7, 0x19, 0x62, 0x65, 0x73, 0xa5, 0xae, + 0xe7, 0x69, 0xad, 0xc0, 0xb8, 0x35, 0x32, 0xbd, 0xaf, 0x3f, 0xdb, 0x11, + 0x3d, 0x54, 0x4e, 0xef, 0xb2, 0x62, 0xaf, 0x02, 0x81, 0x81, 0x00, 0xdd, + 0xa4, 0xdc, 0xe9, 0x4b, 0x7b, 0x8d, 0xd0, 0x96, 0x2c, 0x93, 0x85, 0x11, + 0xeb, 0x26, 0x78, 0x12, 0x0f, 0x55, 0xdd, 0xf1, 0x7b, 0x1a, 0x31, 0x27, + 0xa8, 0x6e, 0x8c, 0x3b, 0xdf, 0x81, 0x27, 0x4f, 0xb2, 0x41, 0x65, 0x4a, + 0x21, 0x40, 0x4b, 0xd9, 0x4f, 0x80, 0x9e, 0xb7, 0xa7, 0x69, 0xb0, 0xb2, + 0x48, 0x5f, 0xf2, 0x48, 0xd2, 0xcb, 0xbb, 0x56, 0x85, 0xf0, 0x24, 0xfe, + 0x2f, 0x3f, 0xc3, 0xe6, 0x5b, 0xe0, 0xa5, 0x5a, 0xb3, 0xbf, 0xda, 0xe1, + 0x96, 0x61, 0x44, 0xd5, 0x5f, 0x03, 0xe1, 0x16, 0xfa, 0x22, 0x28, 0xea, + 0xbc, 0x83, 0x34, 0x2f, 0xba, 0x3f, 0x84, 0xd7, 0x70, 0x69, 0x5e, 0x9d, + 0x45, 0x6e, 0x3c, 0x7a, 0x04, 0x4c, 0x6f, 0x51, 0x30, 0x20, 0xff, 0x90, + 0xb1, 0xed, 0xd5, 0xe1, 0xaa, 0x51, 0x42, 0x87, 0x8b, 0x2c, 0x7f, 0x03, + 0x97, 0x64, 0xe0, 0x5d, 0xf5, 0x07, 0x73, 0x02, 0x81, 0x81, 0x00, 0x91, + 0x53, 0x36, 0x30, 0x81, 0xc0, 0xe3, 0xcb, 0xfa, 0x20, 0x2b, 0xef, 0x93, + 0xfd, 0x59, 0x45, 0x5e, 0x87, 0xac, 0xfe, 0xeb, 0xb0, 0x5a, 0xfc, 0x57, + 0x7d, 0x04, 0x7c, 0x5d, 0x14, 0xc8, 0x7f, 0xd0, 0x58, 0x40, 0xe5, 0x94, + 0x88, 0x23, 0x42, 0x3a, 0x1c, 0xb5, 0x06, 0xe6, 0xdc, 0x36, 0x0e, 0xd8, + 0xf5, 0x13, 0x00, 0xbe, 0xec, 0x07, 0x0a, 0xcb, 0x93, 0x17, 0x25, 0xdf, + 0x29, 0x1a, 0xd1, 0x0a, 0xa3, 0x2a, 0xc9, 0x27, 0xbb, 0xbd, 0xbf, 0x55, + 0x84, 0x0a, 0x49, 0xc9, 0xcd, 0x5f, 0xd8, 0xa3, 0xb1, 0x14, 0x03, 0xea, + 0xbb, 0xc6, 0x07, 0x28, 0x90, 0xa0, 0x7c, 0x81, 0x26, 0x5d, 0x43, 0xa5, + 0xaf, 0xfa, 0x1b, 0x1a, 0x81, 0x94, 0x4d, 0xbb, 0xcd, 0x9d, 0x09, 0x25, + 0xc1, 0xbe, 0x8a, 0x59, 0xd1, 0x7f, 0x59, 0xf3, 0x75, 0xa6, 0x36, 0xb9, + 0x81, 0xe4, 0x43, 0x94, 0x62, 0x48, 0x8f, 0x02, 0x81, 0x80, 0x0e, 0x87, + 0x68, 0x13, 0xc2, 0xe6, 0xee, 0x7c, 0xe5, 0xdb, 0x36, 0x30, 0x5a, 0xe4, + 0x57, 0x39, 0x37, 0xb5, 0xd9, 0xa3, 0x1a, 0xd9, 0x3e, 0xf6, 0x49, 0xeb, + 0x38, 0x93, 0xe8, 0xb8, 0xd5, 0xf6, 0x85, 0xba, 0xe1, 0x9e, 0x8a, 0x25, + 0x73, 0xd9, 0x84, 0xfd, 0x39, 0x29, 0x2e, 0x50, 0xc3, 0x14, 0xda, 0x1a, + 0xf4, 0x6f, 0x25, 0x66, 0xec, 0x03, 0xf3, 0xc0, 0x03, 0xe5, 0xe8, 0xe3, + 0xa0, 0x02, 0x6f, 0x6f, 0x46, 0x62, 0x90, 0x77, 0x87, 0xf4, 0x09, 0x25, + 0x9a, 0x6a, 0xb0, 0xb1, 0xf1, 0xc7, 0x27, 0x82, 0x6e, 0x0a, 0xd0, 0x98, + 0x5a, 0xe1, 0x68, 0xdf, 0x84, 0x9a, 0x68, 0xa4, 0xed, 0xc3, 0x4d, 0xfb, + 0xe7, 0x25, 0xff, 0xb9, 0x98, 0x69, 0x13, 0xa1, 0x5c, 0x08, 0x64, 0xab, + 0xc5, 0x33, 0x67, 0xff, 0x8a, 0x24, 0x1a, 0x4e, 0x7d, 0x34, 0x36, 0x40, + 0x0e, 0x54, 0xc6, 0xd3, 0xf1, 0x11, 0x02, 0x81, 0x80, 0x5e, 0x0e, 0x07, + 0x87, 0x9c, 0x14, 0x52, 0x7a, 0x6b, 0x3e, 0xc4, 0x70, 0x73, 0x97, 0x0b, + 0xf8, 0xe4, 0x4a, 0xde, 0x0e, 0xc8, 0x62, 0x98, 0x59, 0x20, 0x9e, 0x00, + 0x42, 0xb9, 0x2d, 0x38, 0xa0, 0xd0, 0x68, 0xde, 0x5a, 0xb7, 0x09, 0x5c, + 0xbc, 0x24, 0x1a, 0xa7, 0x7b, 0xde, 0x2f, 0x5b, 0x0a, 0x69, 0x3e, 0xc0, + 0xc7, 0x37, 0xe3, 0xdc, 0xa6, 0x02, 0x11, 0x72, 0xb4, 0x6f, 0xe9, 0x58, + 0x9d, 0xfb, 0x3e, 0x1f, 0xaa, 0xce, 0xa4, 0x64, 0xbc, 0xbb, 0x99, 0xce, + 0xa3, 0x35, 0x5e, 0x84, 0x48, 0x3a, 0x13, 0x12, 0xa2, 0x65, 0x31, 0xcb, + 0xa0, 0xb5, 0x36, 0x08, 0x06, 0xef, 0xf7, 0x63, 0xa9, 0x09, 0x4f, 0x30, + 0x2a, 0x86, 0x70, 0xaf, 0x43, 0xe4, 0xb0, 0xfc, 0x5a, 0x67, 0x16, 0xf4, + 0x61, 0x50, 0xa8, 0x28, 0xf4, 0x81, 0xd1, 0xfc, 0x35, 0xe0, 0x75, 0x01, + 0x00, 0x93, 0xc2, 0xb9, 0x9c, + }, + { + 0x30, 0x82, 0x04, 0xbd, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x04, 0xa7, 0x30, 0x82, 0x04, 0xa3, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, + 0x01, 0x00, 0xe7, 0x86, 0x9d, 0xc6, 0xae, 0x8a, 0x70, 0x82, 0x9c, 0xfb, + 0xe1, 0x35, 0x82, 0x11, 0x83, 0x47, 0x0d, 0xcd, 0x5a, 0x63, 0x4a, 0xe6, + 0xdc, 0xb3, 0x1d, 0x4d, 0x7b, 0xa6, 0x37, 0x05, 0x10, 0x7c, 0xbe, 0x52, + 0xf8, 0xb2, 0xe1, 0xa8, 0xed, 0x05, 0x5d, 0x95, 0xfd, 0x20, 0x0d, 0x39, + 0x0a, 0x90, 0x68, 0xe4, 0xed, 0x0e, 0x81, 0x64, 0x8a, 0x57, 0xe8, 0x6a, + 0x63, 0xa1, 0xaf, 0x86, 0xdb, 0x92, 0xa7, 0xda, 0x54, 0x4b, 0x07, 0x67, + 0x60, 0x0f, 0x4f, 0x06, 0x95, 0x65, 0x24, 0x02, 0x19, 0x77, 0xd8, 0x24, + 0x7e, 0x76, 0x84, 0xe0, 0x01, 0x54, 0xab, 0x4f, 0x2f, 0x70, 0xcf, 0x34, + 0xbf, 0xe2, 0xd0, 0x12, 0x6b, 0x9e, 0x8c, 0x4e, 0x0a, 0xf9, 0x06, 0xfb, + 0x41, 0xf9, 0x1a, 0xcb, 0x62, 0x21, 0xea, 0xce, 0x61, 0x04, 0x8f, 0x9d, + 0x20, 0x28, 0x2a, 0x87, 0xcc, 0xfe, 0x1a, 0x57, 0xb6, 0xe2, 0x44, 0x6c, + 0x28, 0xe3, 0xfc, 0xfe, 0xf6, 0x99, 0xa8, 0x49, 0x2e, 0xf5, 0xc7, 0x58, + 0xed, 0xf8, 0x78, 0x8a, 0xaf, 0x68, 0x73, 0xea, 0xcd, 0xbb, 0xd7, 0x8c, + 0xb8, 0x29, 0x83, 0x39, 0x04, 0x24, 0xca, 0x16, 0x59, 0x76, 0xbe, 0x27, + 0xe1, 0xfc, 0x27, 0x17, 0x40, 0x82, 0x27, 0x3b, 0x59, 0x18, 0xe9, 0xfb, + 0x23, 0x19, 0x09, 0xd7, 0x2d, 0xd7, 0x13, 0x8e, 0x4f, 0x8a, 0xcc, 0x53, + 0x11, 0x0d, 0xcf, 0x55, 0xcb, 0x0e, 0x97, 0xf5, 0x17, 0xf3, 0x3e, 0xdd, + 0x95, 0x52, 0x5d, 0x70, 0x91, 0xa7, 0xc4, 0x23, 0x46, 0xb2, 0xc5, 0xec, + 0x0a, 0x1a, 0x28, 0xb5, 0x48, 0x8b, 0x0b, 0x55, 0x18, 0xba, 0xa8, 0x07, + 0x31, 0x31, 0x07, 0xb4, 0x3b, 0x31, 0xae, 0x10, 0x9b, 0x23, 0xdf, 0xf9, + 0x8a, 0xd8, 0x78, 0x23, 0xf9, 0x35, 0x42, 0x91, 0x1d, 0xed, 0xc9, 0x64, + 0xe4, 0x41, 0x89, 0x52, 0x43, 0x6d, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, + 0x82, 0x01, 0x00, 0x2c, 0x11, 0x49, 0x52, 0x99, 0xc2, 0x2b, 0xec, 0x10, + 0x69, 0x38, 0xd2, 0x70, 0x71, 0x58, 0xe5, 0x5b, 0x51, 0x2c, 0xd5, 0x1d, + 0x5a, 0x97, 0xc3, 0x57, 0xde, 0x79, 0x9c, 0x87, 0x78, 0x65, 0x4c, 0xf9, + 0x04, 0xdd, 0xe5, 0x5e, 0x62, 0x42, 0xca, 0xf4, 0xa7, 0x29, 0xc8, 0xc1, + 0xfb, 0x8b, 0x71, 0x35, 0x25, 0xed, 0xa1, 0x07, 0x2b, 0xd5, 0xd1, 0x29, + 0xa1, 0xa6, 0x59, 0x0e, 0x09, 0xc3, 0x1d, 0x44, 0xe9, 0x9f, 0x85, 0xb0, + 0x61, 0x12, 0xbd, 0x97, 0x33, 0x0f, 0x98, 0x72, 0xa4, 0x1b, 0xa8, 0xdd, + 0xf1, 0x04, 0x49, 0x16, 0xbd, 0x74, 0x43, 0x63, 0x5f, 0xed, 0x0d, 0xb1, + 0xc2, 0xbe, 0x8b, 0x33, 0x43, 0x6e, 0x4c, 0x7d, 0x34, 0x15, 0x8e, 0x94, + 0xaf, 0x9a, 0x3d, 0x09, 0xc6, 0x90, 0x65, 0x18, 0x46, 0x33, 0xdd, 0x24, + 0x60, 0xa9, 0xd0, 0x3b, 0x58, 0x21, 0xc2, 0x73, 0xcd, 0x22, 0x37, 0xce, + 0x4b, 0xcc, 0xdb, 0x8c, 0xbd, 0xef, 0xda, 0xa7, 0x7b, 0xfd, 0x89, 0x22, + 0xc3, 0x79, 0xad, 0x4d, 0x4d, 0x12, 0x96, 0x17, 0x68, 0x19, 0x01, 0x2b, + 0x67, 0xf5, 0xf8, 0xa2, 0xe7, 0x2a, 0x41, 0xa1, 0x05, 0xfa, 0xe7, 0xcf, + 0xcc, 0xff, 0xf5, 0xa1, 0xe0, 0x9f, 0x25, 0xc9, 0x3a, 0xac, 0xa8, 0x33, + 0x4f, 0x1a, 0x11, 0xfc, 0x47, 0x7f, 0x33, 0x64, 0x0a, 0x95, 0xb0, 0x86, + 0x01, 0xbf, 0x5e, 0xd4, 0xd7, 0x99, 0xdf, 0x14, 0x76, 0x31, 0xa7, 0xc1, + 0xe2, 0x9f, 0x33, 0xb7, 0xb9, 0xde, 0x18, 0x2c, 0x3a, 0x4a, 0x6b, 0x93, + 0x63, 0x5a, 0xa4, 0x6d, 0xcb, 0xc1, 0xc8, 0xa9, 0x5a, 0xa7, 0xd5, 0x5e, + 0x55, 0xc1, 0xb4, 0xcd, 0xd4, 0xb2, 0x1d, 0x1b, 0x1d, 0x80, 0xf7, 0xc3, + 0x27, 0x76, 0x7e, 0x92, 0x2a, 0x8b, 0x2d, 0x98, 0x7a, 0x90, 0xc7, 0x8a, + 0xa8, 0xa2, 0x63, 0xb2, 0x73, 0xc0, 0x81, 0x02, 0x81, 0x81, 0x00, 0xfa, + 0x35, 0x5e, 0xe0, 0xb9, 0x4e, 0x6b, 0x67, 0xfb, 0xbc, 0x30, 0x6e, 0x75, + 0xf4, 0x13, 0xd0, 0xac, 0xb6, 0x4e, 0x24, 0xcf, 0x43, 0xf7, 0xe6, 0x21, + 0xb8, 0x49, 0x13, 0xe9, 0x3d, 0xcf, 0xe7, 0x81, 0xc7, 0x2f, 0x85, 0xe5, + 0x16, 0x4e, 0x5c, 0xf5, 0x5c, 0x52, 0xa8, 0x01, 0xf9, 0x7c, 0x77, 0x6b, + 0xf5, 0xc2, 0x40, 0xe8, 0xe1, 0xc4, 0x49, 0x59, 0xc1, 0x82, 0x7a, 0x59, + 0xcc, 0x3a, 0xe0, 0x21, 0x9d, 0x07, 0xfa, 0xdf, 0x4b, 0xae, 0xcb, 0x45, + 0xe8, 0x8f, 0xe7, 0x62, 0x43, 0x7b, 0x3c, 0x89, 0xf1, 0xb4, 0x58, 0xc1, + 0xb1, 0x1c, 0x85, 0x05, 0x19, 0xbd, 0xcc, 0xe8, 0x48, 0xad, 0xd1, 0x0d, + 0x30, 0x81, 0x18, 0x42, 0xb9, 0xd6, 0x56, 0x07, 0x31, 0x95, 0xe5, 0xa4, + 0x3d, 0xc3, 0x52, 0x46, 0x39, 0xb3, 0x0c, 0x5b, 0xa4, 0x15, 0x31, 0xbd, + 0xda, 0xc3, 0x2b, 0x1c, 0xaf, 0x43, 0x7d, 0x02, 0x81, 0x81, 0x00, 0xec, + 0xe2, 0x8a, 0x52, 0xdc, 0x0f, 0x82, 0xaf, 0x7d, 0xde, 0x72, 0xc9, 0x7a, + 0x19, 0x32, 0x4e, 0x12, 0x4b, 0xfd, 0x0c, 0x6f, 0xf2, 0x10, 0x22, 0x5f, + 0xe6, 0x0e, 0x1f, 0x79, 0xb9, 0x42, 0xea, 0xd6, 0x4b, 0x0f, 0x8d, 0x8b, + 0x6f, 0x69, 0x4a, 0x85, 0x60, 0x1d, 0x9e, 0xde, 0x47, 0xc6, 0x53, 0x42, + 0xaf, 0x08, 0x14, 0xf3, 0x68, 0xad, 0xab, 0xb4, 0x57, 0x6a, 0xb2, 0x60, + 0xaa, 0x99, 0x95, 0x65, 0x59, 0x90, 0x2a, 0xb3, 0x2d, 0x34, 0x7e, 0xf5, + 0x02, 0x4c, 0x2c, 0x53, 0xad, 0xaf, 0xb8, 0xf5, 0xec, 0xf8, 0xb8, 0xb6, + 0x4e, 0x96, 0x92, 0x22, 0x3f, 0xd0, 0x6d, 0xde, 0xae, 0x51, 0x37, 0xb5, + 0xf2, 0x4f, 0x81, 0x67, 0x52, 0xa4, 0x86, 0x01, 0x87, 0x24, 0x00, 0x4b, + 0x19, 0xb2, 0xcb, 0xab, 0xd6, 0x8b, 0x71, 0x06, 0x9c, 0x11, 0x1b, 0x09, + 0x1d, 0x41, 0x62, 0x2d, 0x46, 0x22, 0xb1, 0x02, 0x81, 0x81, 0x00, 0xcb, + 0x09, 0x16, 0x15, 0xab, 0xa9, 0x8c, 0x14, 0xd4, 0x3d, 0x30, 0xdb, 0xe2, + 0x11, 0x8f, 0x26, 0xcd, 0x90, 0x1e, 0x37, 0x56, 0x8d, 0xa4, 0x13, 0x76, + 0x17, 0x72, 0x0d, 0x9b, 0x3e, 0x92, 0x80, 0x1d, 0x5a, 0xc2, 0x20, 0x97, + 0xf6, 0x92, 0xde, 0x72, 0x28, 0x59, 0xd6, 0x85, 0xcb, 0xab, 0x09, 0xc9, + 0x6c, 0xa3, 0x16, 0x43, 0x24, 0x39, 0x41, 0x00, 0x79, 0xbb, 0xe4, 0x87, + 0x0b, 0xe8, 0x56, 0xca, 0x50, 0x00, 0x12, 0xbe, 0xb2, 0x4e, 0x84, 0x1b, + 0x15, 0x8d, 0xf8, 0xc8, 0x4f, 0x54, 0x28, 0x9b, 0x13, 0xad, 0x32, 0x3c, + 0xf2, 0xf8, 0xde, 0xa4, 0x75, 0xf0, 0x03, 0xe0, 0x16, 0x62, 0x1d, 0xfa, + 0xe2, 0xfa, 0x4e, 0xf2, 0x8e, 0x94, 0xf4, 0x38, 0x60, 0x2f, 0x70, 0x2a, + 0xe8, 0x07, 0xa0, 0xb2, 0xe5, 0xcd, 0x7a, 0x60, 0xbe, 0xf4, 0x87, 0xe9, + 0xb5, 0x24, 0x9c, 0xce, 0x69, 0x16, 0x8d, 0x02, 0x81, 0x80, 0x43, 0xe8, + 0xde, 0x06, 0xcb, 0x17, 0x97, 0x3b, 0x80, 0x36, 0x19, 0x6a, 0x31, 0x1a, + 0xb0, 0x15, 0x39, 0xa1, 0x97, 0x35, 0xcd, 0x91, 0xf3, 0x96, 0x27, 0x2f, + 0xe8, 0xb4, 0x95, 0x4c, 0xcb, 0x26, 0x4f, 0xb0, 0xcd, 0x10, 0xa2, 0xfc, + 0x99, 0x8e, 0xc7, 0x95, 0xba, 0x68, 0x25, 0x09, 0x26, 0x8e, 0x0f, 0xfb, + 0xe5, 0xb1, 0x3b, 0xe6, 0xb2, 0x83, 0x1a, 0xfc, 0x44, 0x96, 0x46, 0xa8, + 0x21, 0xc7, 0x76, 0x7a, 0xaa, 0x9d, 0xd3, 0x28, 0x0e, 0x78, 0xa6, 0xfc, + 0xb5, 0xa1, 0xaf, 0x8b, 0x87, 0x68, 0xda, 0xf7, 0xc8, 0x86, 0x0b, 0x9d, + 0x94, 0x04, 0xe8, 0xd4, 0xaa, 0x88, 0x14, 0xe2, 0x9c, 0x5e, 0x00, 0x47, + 0x2b, 0x0b, 0xc9, 0xf7, 0x23, 0x3b, 0x57, 0x8c, 0xca, 0x81, 0x5b, 0x06, + 0x4e, 0xff, 0x9c, 0x8f, 0xb1, 0x02, 0x02, 0x07, 0x13, 0xd0, 0x50, 0x58, + 0x96, 0xb9, 0xf2, 0x88, 0xbc, 0xc1, 0x02, 0x81, 0x80, 0x32, 0xa2, 0x39, + 0x0a, 0xa4, 0xf0, 0x85, 0xdc, 0x71, 0x7a, 0x05, 0x66, 0x46, 0x3d, 0x3a, + 0x3c, 0xbc, 0xf2, 0x8c, 0x93, 0x64, 0x3c, 0xf1, 0xf7, 0x1e, 0xb5, 0x1b, + 0xbc, 0x0e, 0x4f, 0x65, 0xbd, 0xd3, 0x0a, 0x6a, 0x89, 0x3c, 0x34, 0xfe, + 0x23, 0x5a, 0x1a, 0xd3, 0x7d, 0x9b, 0x95, 0xcb, 0xe9, 0x33, 0xf6, 0xe4, + 0x05, 0x6c, 0xeb, 0x97, 0x20, 0x86, 0x62, 0x95, 0xc3, 0x22, 0xa2, 0x6c, + 0x10, 0x3b, 0x0d, 0xc0, 0xc5, 0xc8, 0xfb, 0xf2, 0xd2, 0x08, 0xab, 0x3e, + 0x3b, 0x78, 0x73, 0x32, 0x2c, 0xc1, 0x35, 0x8e, 0x0e, 0xc7, 0xe4, 0x4c, + 0xcc, 0xae, 0xca, 0x71, 0x85, 0xff, 0xf8, 0xb0, 0x1b, 0x17, 0x03, 0x8a, + 0x03, 0x6a, 0xa3, 0x8d, 0x6e, 0x78, 0xb1, 0x00, 0xf3, 0xd6, 0xc1, 0xd6, + 0xbd, 0xe4, 0x5e, 0x4a, 0x81, 0xb5, 0xbc, 0x94, 0x8a, 0x88, 0x53, 0x97, + 0x43, 0x1b, 0x07, 0x16, 0x51, + }, +}; + +static const uint8_t kTestSandbox[] = {0x01, 0x02, 0x03}; } // namespace wvoec diff --git a/oemcrypto/test/oemcrypto_session_tests_helper.cpp b/oemcrypto/test/oemcrypto_session_tests_helper.cpp index f91a39a..31b46a1 100644 --- a/oemcrypto/test/oemcrypto_session_tests_helper.cpp +++ b/oemcrypto/test/oemcrypto_session_tests_helper.cpp @@ -92,12 +92,11 @@ void SessionUtil::InstallTestRSAKey(Session* s) { wrapped_private_key.data(), &wrapped_private_key_size, &key_type)); // Assume the public key has been verified by the server and the DRM cert is // returned. - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_LoadDRMPrivateKey(s->session_id(), key_type, - wrapped_private_key.data(), - wrapped_private_key_size)); + wrapped_private_key.resize(wrapped_private_key_size); ASSERT_NO_FATAL_FAILURE( - s->SetRsaPublicKey(public_key.data(), public_key_size)); + s->LoadWrappedDrmKey(key_type, wrapped_private_key)); + ASSERT_NO_FATAL_FAILURE(s->SetPublicKeyFromSubjectPublicKey( + key_type, public_key.data(), public_key_size)); return; } @@ -108,10 +107,10 @@ void SessionUtil::InstallTestRSAKey(Session* s) { ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey()); } // Load the wrapped rsa test key. - ASSERT_NO_FATAL_FAILURE(s->InstallRSASessionTestKey(wrapped_rsa_key_)); + ASSERT_NO_FATAL_FAILURE(s->LoadWrappedRsaDrmKey(wrapped_rsa_key_)); } // Test RSA key should be loaded. - ASSERT_NO_FATAL_FAILURE(s->PreparePublicKey()); + ASSERT_NO_FATAL_FAILURE(s->SetTestRsaPublicKey()); } } // namespace wvoec diff --git a/oemcrypto/test/oemcrypto_test.cpp b/oemcrypto/test/oemcrypto_test.cpp index c791c15..56be420 100644 --- a/oemcrypto/test/oemcrypto_test.cpp +++ b/oemcrypto/test/oemcrypto_test.cpp @@ -172,6 +172,7 @@ const size_t kMaxConcurrentSession[] = { 10, 20, 30, 40}; const size_t kMaxKeysPerSession[] = { 4, 20, 20, 30}; const size_t kMaxTotalKeys[] = { 16, 40, 80, 90}; const size_t kLargeMessageSize[] = { 8*KiB, 8*KiB, 16*KiB, 32*KiB}; +const size_t kMaxTotalDRMPrivateKeys[] = { 2, 4, 6, 8}; // Note: Frame rate and simultaneous playback are specified by resource rating, // but are tested at the system level, so there are no unit tests for frame // rate. Similarly, number of subsamples for AV1 @@ -265,7 +266,7 @@ TEST_F(OEMCryptoClientTest, FreeUnallocatedSecureBufferNoFailure) { */ TEST_F(OEMCryptoClientTest, VersionNumber) { const std::string log_message = - "OEMCrypto unit tests for API 17.0. Tests last updated 2021-12-03"; + "OEMCrypto unit tests for API 17. Tests last updated 2022-04-13"; cout << " " << log_message << "\n"; cout << " " << "These tests are part of Android T." @@ -277,8 +278,8 @@ TEST_F(OEMCryptoClientTest, VersionNumber) { EXPECT_EQ(ODK_MINOR_VERSION, 0); EXPECT_EQ(kCurrentAPI, 17u); OEMCrypto_Security_Level level = OEMCrypto_SecurityLevel(); - ASSERT_GT(level, OEMCrypto_Level_Unknown); - ASSERT_LE(level, OEMCrypto_Level3); + EXPECT_GT(level, OEMCrypto_Level_Unknown); + EXPECT_LE(level, OEMCrypto_Level3); cout << " OEMCrypto Security Level is L" << level << endl; uint32_t version = OEMCrypto_APIVersion(); uint32_t minor_version = OEMCrypto_MinorAPIVersion(); @@ -464,9 +465,6 @@ TEST_F(OEMCryptoClientTest, CheckHDCPCapabilityAPI09) { TEST_F(OEMCryptoClientTest, CheckSRMCapabilityV13) { // This just tests some trivial functionality of the SRM update functions. - bool supported = OEMCrypto_IsSRMUpdateSupported(); - printf(" Update SRM Supported: %s.\n", - supported ? "true" : "false"); uint16_t version = 0; OEMCryptoResult current_result = OEMCrypto_GetCurrentSRMVersion(&version); if (current_result == OEMCrypto_SUCCESS) { @@ -477,28 +475,16 @@ TEST_F(OEMCryptoClientTest, CheckSRMCapabilityV13) { } else { EXPECT_EQ(OEMCrypto_ERROR_NOT_IMPLEMENTED, current_result); } - vector bad_srm(42); - GetRandBytes(bad_srm.data(), bad_srm.size()); - EXPECT_NE(OEMCrypto_SUCCESS, - OEMCrypto_LoadSRM(bad_srm.data(), bad_srm.size())); } TEST_F(OEMCryptoClientTest, CheckNullBuildInformationAPI17) { OEMCryptoResult sts; std::string build_info; sts = OEMCrypto_BuildInformation(&build_info[0], nullptr); - ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE, sts); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts); size_t buf_length = 0; sts = OEMCrypto_BuildInformation(nullptr, &buf_length); - ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE, sts); -} - -TEST_F(OEMCryptoClientTest, OEMCryptoMemoryLoadSrmHugeLargeSrm) { - auto oemcrypto_function = [](size_t buffer_length) { - vector srm_buffer(buffer_length); - return OEMCrypto_LoadSRM(srm_buffer.data(), srm_buffer.size()); - }; - TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts); } TEST_F(OEMCryptoClientTest, CheckMaxNumberOfSessionsAPI10) { @@ -1210,8 +1196,8 @@ TEST_F(OEMCryptoProv30Test, GetCertOnlyAPI16) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); // Install the DRM Cert's RSA key. - ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_)); - ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey()); + ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_)); + ASSERT_NO_FATAL_FAILURE(s.SetTestRsaPublicKey()); // Request the OEM Cert. -- This should NOT load the OEM Private key. vector public_cert; size_t public_cert_length = 0; @@ -1243,8 +1229,8 @@ TEST_F(OEMCryptoProv30Test, OEMCryptoMemoryGetOEMPublicCertForHugeCertLength) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); // Install the DRM Cert's RSA key. - ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_)); - ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey()); + ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_)); + ASSERT_NO_FATAL_FAILURE(s.SetTestRsaPublicKey()); auto oemcrypto_function = [](size_t input_length) { size_t public_cert_length = input_length; @@ -1348,13 +1334,8 @@ TEST_F(OEMCryptoProv40Test, GenerateCertificateKeyPairSuccess) { wrapped_private_key.data(), &wrapped_private_key_size, &key_type), OEMCrypto_SUCCESS); // Parse the public key generated to make sure it is correctly formatted. - if (key_type == OEMCrypto_PrivateKeyType::OEMCrypto_RSA_Private_Key) { - ASSERT_NO_FATAL_FAILURE( - s.SetRsaPublicKey(public_key.data(), public_key_size)); - } else if (key_type == OEMCrypto_PrivateKeyType::OEMCrypto_ECC_Private_Key) { - ASSERT_NO_FATAL_FAILURE( - s.SetEcPublicKey(public_key.data(), public_key_size)); - } + ASSERT_NO_FATAL_FAILURE(s.SetPublicKeyFromSubjectPublicKey( + key_type, public_key.data(), public_key_size)); } // Verifies the generated key pairs are different on each call. @@ -1513,11 +1494,19 @@ TEST_F(OEMCryptoProv40Test, InstallOemPrivateKeyCanBeUsed) { wrapped_private_key2.resize(wrapped_private_key_size2); // Verify public_key_signature2 with public_key1. - ASSERT_NO_FATAL_FAILURE( - s.SetRsaPublicKey(public_key1.data(), public_key1.size())); - ASSERT_NO_FATAL_FAILURE( - s.VerifyRSASignature(public_key2, public_key_signature2.data(), - public_key_signature2.size(), kSign_RSASSA_PSS)); + if (key_type2 == OEMCrypto_PrivateKeyType::OEMCrypto_RSA_Private_Key) { + ASSERT_NO_FATAL_FAILURE(s.SetRsaPublicKeyFromSubjectPublicKey( + public_key1.data(), public_key1.size())); + ASSERT_NO_FATAL_FAILURE( + s.VerifyRsaSignature(public_key2, public_key_signature2.data(), + public_key_signature2.size(), kSign_RSASSA_PSS)); + } else if (key_type2 == OEMCrypto_PrivateKeyType::OEMCrypto_ECC_Private_Key) { + ASSERT_NO_FATAL_FAILURE(s.SetEccPublicKeyFromSubjectPublicKey( + public_key1.data(), public_key1.size())); + ASSERT_NO_FATAL_FAILURE(s.VerifyEccSignature(public_key2, + public_key_signature2.data(), + public_key_signature2.size())); + } } // @@ -1584,59 +1573,6 @@ class OEMCryptoSessionTests : public OEMCryptoClientTest { license_messages.EncryptAndSignResponse(); return license_messages.LoadResponse(); } - - void TestLoadLicenseForHugeBufferLengths( - const std::function f, bool check_status, - bool update_core_message_substring_values) { - auto oemcrypto_function = [&](size_t message_length) { - Session s; - LicenseRoundTrip license_messages(&s); - s.open(); - InstallTestRSAKey(&s); - bool verify_keys_loaded = true; - license_messages.SignAndVerifyRequest(); - license_messages.CreateDefaultResponse(); - if (update_core_message_substring_values) { - // Make the license message big enough so that updated core message - // substring offset and length values from tests are still able to read - // data from license_message buffer rather than reading some garbage - // data. - license_messages.set_message_size( - sizeof(license_messages.response_data()) + message_length); - } - f(message_length, &license_messages); - if (update_core_message_substring_values) { - // We will be updating offset for these tests, which will cause verify - // keys to fail with an assertion. Hence skipping verification. - verify_keys_loaded = false; - } - license_messages.EncryptAndSignResponse(); - OEMCryptoResult result = - license_messages.LoadResponse(&s, verify_keys_loaded); - s.close(); - return result; - }; - TestHugeLengthDoesNotCrashAPI(oemcrypto_function, check_status); - } - - void TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( - const std::function f) { - Session s; - LicenseRoundTrip license_messages(&s); - s.open(); - InstallTestRSAKey(&s); - license_messages.SignAndVerifyRequest(); - license_messages.CreateDefaultResponse(); - size_t message_length = sizeof(license_messages.response_data()); - f(message_length, &license_messages); - license_messages.EncryptAndSignResponse(); - OEMCryptoResult result = license_messages.LoadResponse(); - s.close(); - // Verifying error is not due to signature failure which can be caused due - // to test code. - ASSERT_NE(OEMCrypto_ERROR_SIGNATURE_FAILURE, result); - ASSERT_NE(OEMCrypto_SUCCESS, result); - } }; TEST_F(OEMCryptoSessionTests, @@ -1710,6 +1646,77 @@ TEST_F(OEMCryptoSessionTests, !kCheckStatus); } +class OEMCryptoLicenseOverflowTest : public OEMCryptoSessionTests, + public WithParamInterface { + public: + OEMCryptoLicenseOverflowTest() : license_api_version_(kCurrentAPI) {} + + void SetUp() override { + OEMCryptoSessionTests::SetUp(); + license_api_version_ = GetParam(); + } + + void TearDown() override { OEMCryptoSessionTests::TearDown(); } + + void TestLoadLicenseForHugeBufferLengths( + const std::function f, bool check_status, + bool update_core_message_substring_values) { + auto oemcrypto_function = [&](size_t message_length) { + Session s; + LicenseRoundTrip license_messages(&s); + license_messages.set_api_version(license_api_version_); + s.open(); + InstallTestRSAKey(&s); + bool verify_keys_loaded = true; + license_messages.SignAndVerifyRequest(); + license_messages.CreateDefaultResponse(); + if (update_core_message_substring_values) { + // Make the license message big enough so that updated core message + // substring offset and length values from tests are still able to read + // data from license_message buffer rather than reading some garbage + // data. + license_messages.set_message_size( + sizeof(license_messages.response_data()) + message_length); + } + f(message_length, &license_messages); + if (update_core_message_substring_values) { + // We will be updating offset for these tests, which will cause verify + // keys to fail with an assertion. Hence skipping verification. + verify_keys_loaded = false; + } + license_messages.EncryptAndSignResponse(); + OEMCryptoResult result = + license_messages.LoadResponse(&s, verify_keys_loaded); + s.close(); + return result; + }; + TestHugeLengthDoesNotCrashAPI(oemcrypto_function, check_status); + } + + void TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( + const std::function f) { + Session s; + LicenseRoundTrip license_messages(&s); + license_messages.set_api_version(license_api_version_); + s.open(); + InstallTestRSAKey(&s); + license_messages.SignAndVerifyRequest(); + license_messages.CreateDefaultResponse(); + size_t message_length = sizeof(license_messages.response_data()); + f(message_length, &license_messages); + license_messages.EncryptAndSignResponse(); + OEMCryptoResult result = license_messages.LoadResponse(); + s.close(); + // Verifying error is not due to signature failure which can be caused due + // to test code. + ASSERT_NE(OEMCrypto_ERROR_SIGNATURE_FAILURE, result); + ASSERT_NE(OEMCrypto_SUCCESS, result); + } + + protected: + uint32_t license_api_version_; +}; + // This class is for testing a single license with the default API version // of 16. Used for buffer overflow tests. class OEMCryptoMemoryLicenseTest : public OEMCryptoLicenseTestAPI16 { @@ -2027,7 +2034,6 @@ TEST_P(OEMCryptoEntitlementLicenseTest, LoadEntitlementKeysAPI17) { ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( session_.session_id(), &key_session_id)); ASSERT_NE(key_session_id, 0u); - EntitledMessage entitled_message_1(&license_messages_); entitled_message_1.FillKeyArray(); entitled_message_1.SetEntitledKeySession(key_session_id); @@ -2084,6 +2090,8 @@ TEST_P(OEMCryptoEntitlementLicenseTest, entitled_message_1.LoadKeys(OEMCrypto_ERROR_INVALID_CONTEXT)); } +// This verifies that entitled content keys cannot be loaded if we have loaded +// the wrong entitlement keys. TEST_P(OEMCryptoEntitlementLicenseTest, CasOnlyLoadCasKeysNoEntitlementKeysAPI17) { license_messages_.set_license_type(OEMCrypto_EntitlementLicense); @@ -2204,6 +2212,9 @@ TEST_P(OEMCryptoEntitlementLicenseTest, OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION)); } +INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoEntitlementLicenseTest, + Range(kCoreMessagesAPI, kCurrentAPI + 1)); + TEST_F(OEMCryptoMemoryLicenseTest, OEMCryptoMemoryPrepareRenewalRequestForHugeBufferLength) { RenewalRoundTrip renewal_messages(&license_messages_); @@ -3435,7 +3446,7 @@ TEST_F(OEMCryptoSessionTests, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); } -TEST_F(OEMCryptoSessionTests, +TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyIdLength) { TestLoadLicenseForHugeBufferLengths( [](size_t length, LicenseRoundTrip* license_messages) { @@ -3445,7 +3456,7 @@ TEST_F(OEMCryptoSessionTests, !kCheckStatus, kUpdateCoreMessageSubstringValues); } -TEST_F(OEMCryptoSessionTests, +TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyIdOffset) { TestLoadLicenseForHugeBufferLengths( [](size_t offset, LicenseRoundTrip* license_messages) { @@ -3454,7 +3465,7 @@ TEST_F(OEMCryptoSessionTests, !kCheckStatus, kUpdateCoreMessageSubstringValues); } -TEST_F(OEMCryptoSessionTests, +TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyIdLength) { TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( [](size_t response_message_length, LicenseRoundTrip* license_messages) { @@ -3463,7 +3474,7 @@ TEST_F(OEMCryptoSessionTests, }); } -TEST_F(OEMCryptoSessionTests, +TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyIdOffset) { TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( [](size_t response_message_length, LicenseRoundTrip* license_messages) { @@ -3472,7 +3483,7 @@ TEST_F(OEMCryptoSessionTests, }); } -TEST_F(OEMCryptoSessionTests, +TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyDataIvLength) { TestLoadLicenseForHugeBufferLengths( [](size_t length, LicenseRoundTrip* license_messages) { @@ -3482,7 +3493,7 @@ TEST_F(OEMCryptoSessionTests, !kCheckStatus, kUpdateCoreMessageSubstringValues); } -TEST_F(OEMCryptoSessionTests, +TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyDataIvOffset) { TestLoadLicenseForHugeBufferLengths( [](size_t offset, LicenseRoundTrip* license_messages) { @@ -3492,8 +3503,8 @@ TEST_F(OEMCryptoSessionTests, !kCheckStatus, kUpdateCoreMessageSubstringValues); } -TEST_F( - OEMCryptoSessionTests, +TEST_P( + OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyDataIvLength) { TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( [](size_t response_message_length, LicenseRoundTrip* license_messages) { @@ -3503,8 +3514,8 @@ TEST_F( }); } -TEST_F( - OEMCryptoSessionTests, +TEST_P( + OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyDataIvOffset) { TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( [](size_t response_message_length, LicenseRoundTrip* license_messages) { @@ -3514,7 +3525,7 @@ TEST_F( }); } -TEST_F(OEMCryptoSessionTests, +TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyDataLength) { TestLoadLicenseForHugeBufferLengths( [](size_t length, LicenseRoundTrip* license_messages) { @@ -3524,7 +3535,7 @@ TEST_F(OEMCryptoSessionTests, !kCheckStatus, kUpdateCoreMessageSubstringValues); } -TEST_F(OEMCryptoSessionTests, +TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyDataOffset) { TestLoadLicenseForHugeBufferLengths( [](size_t offset, LicenseRoundTrip* license_messages) { @@ -3533,8 +3544,8 @@ TEST_F(OEMCryptoSessionTests, !kCheckStatus, kUpdateCoreMessageSubstringValues); } -TEST_F( - OEMCryptoSessionTests, +TEST_P( + OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyDataLength) { TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( [](size_t response_message_length, LicenseRoundTrip* license_messages) { @@ -3544,8 +3555,8 @@ TEST_F( }); } -TEST_F( - OEMCryptoSessionTests, +TEST_P( + OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyDataOffset) { TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( [](size_t response_message_length, LicenseRoundTrip* license_messages) { @@ -3555,8 +3566,8 @@ TEST_F( }); } -TEST_F( - OEMCryptoSessionTests, +TEST_P( + OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyControlIvLength) { TestLoadLicenseForHugeBufferLengths( [](size_t length, LicenseRoundTrip* license_messages) { @@ -3566,8 +3577,8 @@ TEST_F( !kCheckStatus, kUpdateCoreMessageSubstringValues); } -TEST_F( - OEMCryptoSessionTests, +TEST_P( + OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyControlIvOffset) { TestLoadLicenseForHugeBufferLengths( [](size_t offset, LicenseRoundTrip* license_messages) { @@ -3577,8 +3588,8 @@ TEST_F( !kCheckStatus, kUpdateCoreMessageSubstringValues); } -TEST_F( - OEMCryptoSessionTests, +TEST_P( + OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyControlIvLength) { TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( [](size_t response_message_length, LicenseRoundTrip* license_messages) { @@ -3589,8 +3600,8 @@ TEST_F( }); } -TEST_F( - OEMCryptoSessionTests, +TEST_P( + OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyControlIvOffset) { TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( [](size_t response_message_length, LicenseRoundTrip* license_messages) { @@ -3601,7 +3612,7 @@ TEST_F( }); } -TEST_F(OEMCryptoSessionTests, +TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyControlLength) { TestLoadLicenseForHugeBufferLengths( [](size_t length, LicenseRoundTrip* license_messages) { @@ -3611,7 +3622,7 @@ TEST_F(OEMCryptoSessionTests, !kCheckStatus, kUpdateCoreMessageSubstringValues); } -TEST_F(OEMCryptoSessionTests, +TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyControlOffset) { TestLoadLicenseForHugeBufferLengths( [](size_t offset, LicenseRoundTrip* license_messages) { @@ -3621,8 +3632,8 @@ TEST_F(OEMCryptoSessionTests, !kCheckStatus, kUpdateCoreMessageSubstringValues); } -TEST_F( - OEMCryptoSessionTests, +TEST_P( + OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyControlLength) { TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( [](size_t response_message_length, LicenseRoundTrip* license_messages) { @@ -3632,8 +3643,8 @@ TEST_F( }); } -TEST_F( - OEMCryptoSessionTests, +TEST_P( + OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyControlOffset) { TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( [](size_t response_message_length, LicenseRoundTrip* license_messages) { @@ -3643,7 +3654,7 @@ TEST_F( }); } -TEST_F(OEMCryptoSessionTests, +TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringEncMacKeyIvLength) { TestLoadLicenseForHugeBufferLengths( [](size_t length, LicenseRoundTrip* license_messages) { @@ -3652,7 +3663,7 @@ TEST_F(OEMCryptoSessionTests, !kCheckStatus, kUpdateCoreMessageSubstringValues); } -TEST_F(OEMCryptoSessionTests, +TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringEncMacKeyIvOffset) { TestLoadLicenseForHugeBufferLengths( [](size_t offset, LicenseRoundTrip* license_messages) { @@ -3661,8 +3672,8 @@ TEST_F(OEMCryptoSessionTests, !kCheckStatus, kUpdateCoreMessageSubstringValues); } -TEST_F( - OEMCryptoSessionTests, +TEST_P( + OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringEncMacKeyIvLength) { TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( [](size_t response_message_length, LicenseRoundTrip* license_messages) { @@ -3673,8 +3684,8 @@ TEST_F( }); } -TEST_F( - OEMCryptoSessionTests, +TEST_P( + OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringEncMacKeyIvOffset) { TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( [](size_t response_message_length, LicenseRoundTrip* license_messages) { @@ -3685,7 +3696,7 @@ TEST_F( }); } -TEST_F(OEMCryptoSessionTests, +TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringEncMacKeyLength) { TestLoadLicenseForHugeBufferLengths( [](size_t length, LicenseRoundTrip* license_messages) { @@ -3694,7 +3705,7 @@ TEST_F(OEMCryptoSessionTests, !kCheckStatus, kUpdateCoreMessageSubstringValues); } -TEST_F(OEMCryptoSessionTests, +TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringEncMacKeyOffset) { TestLoadLicenseForHugeBufferLengths( [](size_t offset, LicenseRoundTrip* license_messages) { @@ -3703,8 +3714,8 @@ TEST_F(OEMCryptoSessionTests, !kCheckStatus, kUpdateCoreMessageSubstringValues); } -TEST_F( - OEMCryptoSessionTests, +TEST_P( + OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringEncMacKeyLength) { TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( [](size_t response_message_length, LicenseRoundTrip* license_messages) { @@ -3713,8 +3724,8 @@ TEST_F( }); } -TEST_F( - OEMCryptoSessionTests, +TEST_P( + OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringEncMacKeyOffset) { TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( [](size_t response_message_length, LicenseRoundTrip* license_messages) { @@ -3723,7 +3734,7 @@ TEST_F( }); } -TEST_F(OEMCryptoSessionTests, +TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringPstLength) { TestLoadLicenseForHugeBufferLengths( [](size_t length, LicenseRoundTrip* license_messages) { @@ -3732,7 +3743,7 @@ TEST_F(OEMCryptoSessionTests, !kCheckStatus, kUpdateCoreMessageSubstringValues); } -TEST_F(OEMCryptoSessionTests, +TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringPstOffset) { TestLoadLicenseForHugeBufferLengths( [](size_t offset, LicenseRoundTrip* license_messages) { @@ -3741,7 +3752,7 @@ TEST_F(OEMCryptoSessionTests, !kCheckStatus, kUpdateCoreMessageSubstringValues); } -TEST_F(OEMCryptoSessionTests, +TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringPstLength) { TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( [](size_t response_message_length, LicenseRoundTrip* license_messages) { @@ -3750,7 +3761,7 @@ TEST_F(OEMCryptoSessionTests, }); } -TEST_F(OEMCryptoSessionTests, +TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringPstOffset) { TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( [](size_t response_message_length, LicenseRoundTrip* license_messages) { @@ -3759,8 +3770,8 @@ TEST_F(OEMCryptoSessionTests, }); } -TEST_F( - OEMCryptoSessionTests, +TEST_P( + OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringSrmRestrictionDataLength) { TestLoadLicenseForHugeBufferLengths( [](size_t length, LicenseRoundTrip* license_messages) { @@ -3769,8 +3780,8 @@ TEST_F( !kCheckStatus, kUpdateCoreMessageSubstringValues); } -TEST_F( - OEMCryptoSessionTests, +TEST_P( + OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringSrmRestrictionDataOffset) { TestLoadLicenseForHugeBufferLengths( [](size_t offset, LicenseRoundTrip* license_messages) { @@ -3779,8 +3790,8 @@ TEST_F( !kCheckStatus, kUpdateCoreMessageSubstringValues); } -TEST_F( - OEMCryptoSessionTests, +TEST_P( + OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringSrmRestrictionDataLength) { TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( [](size_t response_message_length, LicenseRoundTrip* license_messages) { @@ -3791,8 +3802,8 @@ TEST_F( }); } -TEST_F( - OEMCryptoSessionTests, +TEST_P( + OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringSrmRestrictionDataOffset) { TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( [](size_t response_message_length, LicenseRoundTrip* license_messages) { @@ -3803,7 +3814,8 @@ TEST_F( }); } -TEST_F(OEMCryptoSessionTests, OEMCryptoMemoryLoadLicenseForHugeResponseLength) { +TEST_P(OEMCryptoLicenseOverflowTest, + OEMCryptoMemoryLoadLicenseForHugeResponseLength) { TestLoadLicenseForHugeBufferLengths( [](size_t message_size, LicenseRoundTrip* license_messages) { license_messages->set_message_size(message_size); @@ -3811,7 +3823,7 @@ TEST_F(OEMCryptoSessionTests, OEMCryptoMemoryLoadLicenseForHugeResponseLength) { !kCheckStatus, !kUpdateCoreMessageSubstringValues); } -TEST_F(OEMCryptoSessionTests, +TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageLength) { TestLoadLicenseForHugeBufferLengths( [](size_t message_size, LicenseRoundTrip* license_messages) { @@ -3820,6 +3832,9 @@ TEST_F(OEMCryptoSessionTests, !kCheckStatus, !kUpdateCoreMessageSubstringValues); } +INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoLicenseOverflowTest, + Range(kCurrentAPI - 1, kCurrentAPI + 1)); + TEST_F(OEMCryptoSessionTests, OEMCryptoMemoryLoadRenewalForHugeResponseLength) { auto oemcrypto_function = [&](size_t message_size) { Session s; @@ -5294,7 +5309,7 @@ TEST_F(OEMCryptoLoadsCertificate, LoadRSASessionKey) { ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_)); + ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_)); } TEST_F(OEMCryptoLoadsCertificate, SignProvisioningRequest) { @@ -5728,14 +5743,10 @@ TEST_F(OEMCryptoLoadsCertificate, // Test that a wrapped RSA key can be loaded. TEST_F(OEMCryptoLoadsCertificate, LoadWrappedRSAKey) { - OEMCryptoResult sts; ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key, - wrapped_rsa_key_.data(), - wrapped_rsa_key_.size()); - ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_)); } TEST_F(OEMCryptoLoadsCertificate, @@ -5744,9 +5755,8 @@ TEST_F(OEMCryptoLoadsCertificate, auto oemcrypto_function = [&](size_t wrapped_rsa_key_length) { Session s; s.open(); - vector wrapped_rsa_key_buffer(wrapped_rsa_key_length); - memcpy(wrapped_rsa_key_buffer.data(), wrapped_rsa_key_.data(), - wrapped_rsa_key_.size()); + vector wrapped_rsa_key_buffer = wrapped_rsa_key_; + wrapped_rsa_key_buffer.resize(wrapped_rsa_key_length); OEMCryptoResult result = OEMCrypto_LoadDRMPrivateKey( s.session_id(), OEMCrypto_RSA_Private_Key, wrapped_rsa_key_buffer.data(), wrapped_rsa_key_buffer.size()); @@ -5787,9 +5797,9 @@ class OEMCryptoLoadsCertVariousKeys : public OEMCryptoLoadsCertificate { ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE( - s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); - ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_)); + ASSERT_NO_FATAL_FAILURE(s.SetRsaPublicKeyFromPrivateKeyInfo( + encoded_rsa_key_.data(), encoded_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_)); LicenseRoundTrip license_messages(&s); ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); @@ -5858,12 +5868,9 @@ TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) { Session s1; // Session s1 loads the default rsa key, but doesn't use it // until after s2 uses its key. ASSERT_NO_FATAL_FAILURE(s1.open()); - ASSERT_NO_FATAL_FAILURE( - s1.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_LoadDRMPrivateKey( - s1.session_id(), OEMCrypto_RSA_Private_Key, - wrapped_rsa_key_.data(), wrapped_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE(s1.SetRsaPublicKeyFromPrivateKeyInfo( + encoded_rsa_key_.data(), encoded_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE(s1.LoadWrappedRsaDrmKey(wrapped_rsa_key_)); Session s2; // Session s2 uses a different rsa key. encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeyInfo4_2048, @@ -5871,9 +5878,9 @@ TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) { sizeof(kTestRSAPKCS8PrivateKeyInfo4_2048)); ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey()); ASSERT_NO_FATAL_FAILURE(s2.open()); - ASSERT_NO_FATAL_FAILURE( - s2.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); - ASSERT_NO_FATAL_FAILURE(s2.InstallRSASessionTestKey(wrapped_rsa_key_)); + ASSERT_NO_FATAL_FAILURE(s2.SetRsaPublicKeyFromPrivateKeyInfo( + encoded_rsa_key_.data(), encoded_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE(s2.LoadWrappedRsaDrmKey(wrapped_rsa_key_)); LicenseRoundTrip license_messages2(&s2); ASSERT_NO_FATAL_FAILURE(license_messages2.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages2.CreateDefaultResponse()); @@ -5891,6 +5898,55 @@ TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) { ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR()); } +// This tests the maximum number of DRM private keys that OEMCrypto can load +TEST_F(OEMCryptoLoadsCertificate, TestMaxDRMKeys) { + const size_t max_total_keys = GetResourceValue(kMaxTotalDRMPrivateKeys); + std::vector> sessions; + std::vector> licenses; + + // It should be able to load up to kMaxTotalDRMPrivateKeys keys + for (size_t i = 0; i < max_total_keys; i++) { + sessions.push_back(std::unique_ptr(new Session())); + licenses.push_back(std::unique_ptr( + new LicenseRoundTrip(sessions[i].get()))); + const size_t key_index = i % kTestRSAPKCS8PrivateKeys_2048.size(); + encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeys_2048[key_index].begin(), + kTestRSAPKCS8PrivateKeys_2048[key_index].end()); + ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey()); + ASSERT_NO_FATAL_FAILURE(sessions[i]->open()); + ASSERT_NO_FATAL_FAILURE(sessions[i]->SetRsaPublicKeyFromPrivateKeyInfo( + encoded_rsa_key_.data(), encoded_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE( + sessions[i]->LoadWrappedRsaDrmKey(wrapped_rsa_key_)); + } + + // Attempts to load one more key than the kMaxTotalDRMPrivateKeys + Session s; + encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeyInfo3_3072, + kTestRSAPKCS8PrivateKeyInfo3_3072 + + sizeof(kTestRSAPKCS8PrivateKeyInfo3_3072)); + Session ps; + ProvisioningRoundTrip provisioning_messages(&ps, encoded_rsa_key_); + provisioning_messages.PrepareSession(keybox_); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); + OEMCryptoResult result = provisioning_messages.LoadResponse(); + // Key loading is allowed to fail due to resource restriction + if (result != OEMCrypto_SUCCESS) { + ASSERT_TRUE(result == OEMCrypto_ERROR_INSUFFICIENT_RESOURCES || + result == OEMCrypto_ERROR_TOO_MANY_KEYS); + } + // Verifies that the DRM keys which are already loaded should still function + for (size_t i = 0; i < licenses.size(); i++) { + ASSERT_NO_FATAL_FAILURE(licenses[i]->SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(licenses[i]->CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(licenses[i]->EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, licenses[i]->LoadResponse()); + ASSERT_NO_FATAL_FAILURE(sessions[i]->TestDecryptCTR()); + } +} + // Devices that load certificates, should at least support RSA 2048 keys. TEST_F(OEMCryptoLoadsCertificate, SupportsCertificatesAPI13) { ASSERT_NE(0u, @@ -5935,7 +5991,7 @@ class OEMCryptoUsesCertificate : public OEMCryptoLoadsCertificate { ASSERT_NO_FATAL_FAILURE(session_.open()); if (global_features.derive_key_method == DeviceFeatures::LOAD_TEST_RSA_KEY) { - ASSERT_NO_FATAL_FAILURE(session_.PreparePublicKey( + ASSERT_NO_FATAL_FAILURE(session_.SetRsaPublicKeyFromPrivateKeyInfo( encoded_rsa_key_.data(), encoded_rsa_key_.size())); } else { InstallTestRSAKey(&session_); @@ -5974,10 +6030,7 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) { while (clock.now() - start_time < kTestDuration) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key, - wrapped_rsa_key_.data(), - wrapped_rsa_key_.size()); - ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_)); const size_t size = 50; vector licenseRequest(size); GetRandBytes(licenseRequest.data(), licenseRequest.size()); @@ -6016,15 +6069,12 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_LoadDRMPrivateKey( - s.session_id(), OEMCrypto_RSA_Private_Key, - wrapped_rsa_key_.data(), wrapped_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_)); vector session_key; vector enc_session_key; - ASSERT_NO_FATAL_FAILURE( - s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); - ASSERT_TRUE(s.GenerateRSASessionKey(&session_key, &enc_session_key)); + ASSERT_NO_FATAL_FAILURE(s.SetRsaPublicKeyFromPrivateKeyInfo( + encoded_rsa_key_.data(), encoded_rsa_key_.size())); + ASSERT_TRUE(s.GenerateRsaSessionKey(&session_key, &enc_session_key)); vector mac_context; vector enc_context; s.FillDefaultContext(&mac_context, &enc_context); @@ -6071,7 +6121,7 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) { TEST_F(OEMCryptoUsesCertificate, GenerateDerivedKeysLargeBuffer) { vector session_key; vector enc_session_key; - ASSERT_TRUE(session_.GenerateRSASessionKey(&session_key, &enc_session_key)); + ASSERT_TRUE(session_.GenerateRsaSessionKey(&session_key, &enc_session_key)); const size_t max_size = GetResourceValue(kLargeMessageSize); vector mac_context(max_size); vector enc_context(max_size); @@ -6091,7 +6141,7 @@ TEST_F(OEMCryptoUsesCertificate, OEMCryptoMemoryDeriveKeysFromSessionKeyForHugeMacContext) { vector session_key; vector enc_session_key; - ASSERT_TRUE(session_.GenerateRSASessionKey(&session_key, &enc_session_key)); + ASSERT_TRUE(session_.GenerateRsaSessionKey(&session_key, &enc_session_key)); vector mac_context; vector enc_context; session_.FillDefaultContext(&mac_context, &enc_context); @@ -6111,7 +6161,7 @@ TEST_F(OEMCryptoUsesCertificate, OEMCryptoMemoryDeriveKeysFromSessionKeyForHugeEncContext) { vector session_key; vector enc_session_key; - ASSERT_TRUE(session_.GenerateRSASessionKey(&session_key, &enc_session_key)); + ASSERT_TRUE(session_.GenerateRsaSessionKey(&session_key, &enc_session_key)); vector mac_context; vector enc_context; session_.FillDefaultContext(&mac_context, &enc_context); @@ -6131,7 +6181,7 @@ TEST_F(OEMCryptoUsesCertificate, OEMCryptoMemoryDeriveKeysFromSessionKeyForHugeEncSessionKey) { vector session_key; vector enc_session_key; - ASSERT_TRUE(session_.GenerateRSASessionKey(&session_key, &enc_session_key)); + ASSERT_TRUE(session_.GenerateRsaSessionKey(&session_key, &enc_session_key)); vector mac_context; vector enc_context; session_.FillDefaultContext(&mac_context, &enc_context); @@ -6155,10 +6205,7 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { OEMCryptoResult sts; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key, - wrapped_rsa_key_.data(), - wrapped_rsa_key_.size()); - ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_)); // Sign a Message vector licenseRequest(size); @@ -6185,20 +6232,16 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { } void TestSignature(RSA_Padding_Scheme scheme, size_t size) { - OEMCryptoResult sts; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key, - wrapped_rsa_key_.data(), - wrapped_rsa_key_.size()); - ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_)); vector licenseRequest(size); GetRandBytes(licenseRequest.data(), licenseRequest.size()); size_t signature_length = 0; - sts = OEMCrypto_GenerateRSASignature(s.session_id(), licenseRequest.data(), - licenseRequest.size(), nullptr, - &signature_length, scheme); + OEMCryptoResult sts = OEMCrypto_GenerateRSASignature( + s.session_id(), licenseRequest.data(), licenseRequest.size(), nullptr, + &signature_length, scheme); ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); ASSERT_NE(static_cast(0), signature_length); @@ -6210,27 +6253,23 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { ASSERT_EQ(OEMCrypto_SUCCESS, sts) << "Failed to sign with padding scheme=" << (int)scheme << ", size=" << (int)size; - ASSERT_NO_FATAL_FAILURE( - s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); - ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(licenseRequest, signature, + ASSERT_NO_FATAL_FAILURE(s.SetRsaPublicKeyFromPrivateKeyInfo( + encoded_rsa_key_.data(), encoded_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE(s.VerifyRsaSignature(licenseRequest, signature, signature_length, scheme)); delete[] signature; } void DisallowDeriveKeys() { - OEMCryptoResult sts; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key, - wrapped_rsa_key_.data(), - wrapped_rsa_key_.size()); - ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_)); s.GenerateNonce(); vector session_key; vector enc_session_key; - ASSERT_NO_FATAL_FAILURE( - s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); - ASSERT_TRUE(s.GenerateRSASessionKey(&session_key, &enc_session_key)); + ASSERT_NO_FATAL_FAILURE(s.SetRsaPublicKeyFromPrivateKeyInfo( + encoded_rsa_key_.data(), encoded_rsa_key_.size())); + ASSERT_TRUE(s.GenerateRsaSessionKey(&session_key, &enc_session_key)); vector mac_context; vector enc_context; s.FillDefaultContext(&mac_context, &enc_context); @@ -6277,10 +6316,7 @@ TEST_F(OEMCryptoLoadsCertificateAlternates, if (key_loaded_) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key, - wrapped_rsa_key_.data(), - wrapped_rsa_key_.size()); - ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_)); vector message_buffer(10); size_t signature_length = 0; @@ -6304,7 +6340,6 @@ TEST_F(OEMCryptoLoadsCertificateAlternates, TEST_F(OEMCryptoLoadsCertificateAlternates, OEMCryptoMemoryGenerateRSASignatureForHugeSignatureLength) { - OEMCryptoResult sts; LoadWithAllowedSchemes(kSign_PKCS1_Block1, false); // If the device is a cast receiver, then this scheme is required. if (global_features.cast_receiver) { @@ -6313,10 +6348,7 @@ TEST_F(OEMCryptoLoadsCertificateAlternates, if (key_loaded_) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key, - wrapped_rsa_key_.data(), - wrapped_rsa_key_.size()); - ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_)); vector message_buffer(50); vector signature; @@ -6526,10 +6558,7 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates { OEMCryptoResult sts; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key, - wrapped_rsa_key_.data(), - wrapped_rsa_key_.size()); - ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_)); // The application will compute the SHA-1 Hash of the message, so this // test must do that also. @@ -6560,8 +6589,8 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates { ASSERT_EQ(OEMCrypto_SUCCESS, sts) << "Failed to sign with padding scheme=" << (int)scheme << ", size=" << (int)message.size(); - ASSERT_NO_FATAL_FAILURE( - s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE(s.SetRsaPublicKeyFromPrivateKeyInfo( + encoded_rsa_key_.data(), encoded_rsa_key_.size())); // Verify that the signature matches the official test vector. ASSERT_EQ(correct_signature.size(), signature_length); @@ -6570,9 +6599,9 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates { // Also verify that our verification algorithm agrees. This is not needed // to test OEMCrypto, but it does verify that this test is valid. - ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(digest, signature.data(), + ASSERT_NO_FATAL_FAILURE(s.VerifyRsaSignature(digest, signature.data(), signature_length, scheme)); - ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature( + ASSERT_NO_FATAL_FAILURE(s.VerifyRsaSignature( digest, correct_signature.data(), correct_signature.size(), scheme)); } }; @@ -10040,6 +10069,40 @@ TEST_P(OEMCryptoUsageTableTest, PSTLargeBuffer) { ASSERT_NO_FATAL_FAILURE(s.close()); } +// Verify that a usage entry with an invalid session cannot be used. +TEST_P(OEMCryptoUsageTableTest, UsageEntryWithInvalidSession) { + std::string pst("pst"); + LicenseWithUsageEntry entry; + entry.license_messages().set_pst(pst); + + entry.session().open(); + ASSERT_NO_FATAL_FAILURE(entry.session().CreateNewUsageEntry()); + entry.session().close(); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_SESSION, + OEMCrypto_DeactivateUsageEntry( + entry.session().session_id(), + reinterpret_cast(pst.c_str()), pst.length())); + + entry.session().open(); + ASSERT_NO_FATAL_FAILURE(entry.session().CreateNewUsageEntry()); + entry.session().close(); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_SESSION, + OEMCrypto_MoveEntry(entry.session().session_id(), 0)); +} + +// Verify that a usage entry with an invalid session cannot be used. +TEST_P(OEMCryptoUsageTableTest, ReuseUsageEntryWithInvalidSessionAPI17) { + std::string pst("pst"); + LicenseWithUsageEntry entry; + entry.license_messages().set_pst(pst); + + entry.session().open(); + ASSERT_NO_FATAL_FAILURE(entry.session().CreateNewUsageEntry()); + entry.session().close(); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_SESSION, + OEMCrypto_ReuseUsageEntry(entry.session().session_id(), 0)); +} + INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoUsageTableTest, Range(kCoreMessagesAPI, kCurrentAPI + 1)); diff --git a/oemcrypto/test/oemcrypto_unittests.gypi b/oemcrypto/test/oemcrypto_unittests.gypi index ec9de14..c3cf344 100644 --- a/oemcrypto/test/oemcrypto_unittests.gypi +++ b/oemcrypto/test/oemcrypto_unittests.gypi @@ -39,6 +39,7 @@ '<(oemcrypto_dir)/test', '<(oemcrypto_dir)/test/fuzz_tests', '<(oemcrypto_dir)/odk/include', + '<(oemcrypto_dir)/util/include', ], 'defines': [ 'OEMCRYPTO_TESTS', diff --git a/oemcrypto/test/ota_keybox_test.cpp b/oemcrypto/test/ota_keybox_test.cpp index ce527fb..359da3a 100644 --- a/oemcrypto/test/ota_keybox_test.cpp +++ b/oemcrypto/test/ota_keybox_test.cpp @@ -135,6 +135,11 @@ static const uint8_t TestKeyPKCS8[] = { 0x13, 0xf3, 0xa7, 0xdb, 0xdb, 0x5d, 0x89, 0x2d, 0xd7, 0x02, 0x96, 0xaf, 0xeb, 0x72, 0x8d, 0xd5, 0x56, 0x3b, 0x3a}; +constexpr size_t kInitialOtaKeyboxRequestSize = 8 * 1024; // 8 kB. +// TODO(fredgc): Make sure that partners can use a test cert when +// |use_test_key| parameter of OEMCrypto_GenerateOTARequest() is true. +constexpr uint32_t kUseTestKey = 1; + // TODO(fredgc): duplicate code. Move to common util package. // Return a printable string from data. If all the characters are printable, // then just use the string. Otherwise, convert to hex. @@ -184,12 +189,15 @@ TEST_F(OTAKeyboxProvisioningTest, BasicTest) { << "Keybox valid after initialization. Skipping rest of test." << endl; return; } - ASSERT_EQ(result, OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING); + if (result != OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING) { + cout << " " + << "OTA Keybox functions not supported. Skipping rest of test." + << endl; + return; + } cout << " " << "OTA Keybox functions supported. Device needs provisioning." << endl; - // TODO(fredgc): Make sure that partners can use a test cert when use_test_key - // is true. - constexpr uint32_t use_test_key = 1; + size_t request_length = 0; std::vector request; @@ -198,12 +206,12 @@ TEST_F(OTAKeyboxProvisioningTest, BasicTest) { ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, OEMCrypto_GenerateOTARequest(session_id, nullptr, &request_length, - use_test_key)); + kUseTestKey)); ASSERT_NE(request_length, 0u); request.resize(request_length); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GenerateOTARequest(session_id, request.data(), - &request_length, use_test_key)); + &request_length, kUseTestKey)); ASSERT_GT(request_length, kMinimumRequestLength); request.resize(request_length); // First 16 bytes should match the label for Option 1 or 2. @@ -260,7 +268,7 @@ TEST_F(OTAKeyboxProvisioningTest, BasicTest) { std::copy(bit_size_string.begin(), bit_size_string.end(), std::back_inserter(enc_context)); KeyDeriver keys; - keys.DeriveKeys(model_key.data(), mac_context, enc_context); + keys.DeriveKeys(model_key.data(), model_key.size(), mac_context, enc_context); const std::vector message( request.data(), request.data() + request.size() - HMAC_SHA256_SIGNATURE_SIZE); @@ -287,9 +295,56 @@ TEST_F(OTAKeyboxProvisioningTest, BasicTest) { // Finally, send the response back to the device. EXPECT_EQ(OEMCrypto_SUCCESS, OEMCrypto_ProcessOTAKeybox(session_id, response.data(), - response.size(), use_test_key)); + response.size(), kUseTestKey)); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CloseSession(session_id)); // After installation, the keybox should be valid. EXPECT_EQ(OEMCrypto_IsKeyboxValid(), OEMCrypto_SUCCESS); } + +TEST_F(OTAKeyboxProvisioningTest, StressTest) { + static constexpr size_t kStressRequestCount = 1000; + const OEMCrypto_ProvisioningMethod method = OEMCrypto_GetProvisioningMethod(); + if (method != OEMCrypto_Keybox) { + std::cout << "Skipping for non keybox-based devices: method = " + << ProvisioningMethodName(method) << std::endl; + GTEST_SKIP() << "Device is not keybox based: method = " + << ProvisioningMethodName(method); + return; + } + OEMCrypto_SESSION session_id; + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_OpenSession(&session_id)) + << "Failed to open OTA keybox session"; + // First request is to ensure operation is supported. + std::vector request(kInitialOtaKeyboxRequestSize); + size_t request_size = request.size(); + OEMCryptoResult result = OEMCrypto_GenerateOTARequest( + session_id, request.data(), &request_size, kUseTestKey); + if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED) { + // Not a failure, just skip the test. + OEMCrypto_CloseSession(session_id); + std::cout << "Skipping for device which do not support " + << "OTA keybox provisioning" << std::endl; + GTEST_SKIP() << "OTA Keybox not supported"; + return; + } + + for (size_t i = 0; i < kStressRequestCount; i++) { + request.resize(kInitialOtaKeyboxRequestSize); + request_size = request.size(); + result = OEMCrypto_GenerateOTARequest(session_id, request.data(), + &request_size, kUseTestKey); + if (result == OEMCrypto_ERROR_SHORT_BUFFER) { + request.resize(request_size); + result = OEMCrypto_GenerateOTARequest(session_id, request.data(), + &request_size, kUseTestKey); + } + if (result != OEMCrypto_SUCCESS) { + OEMCrypto_CloseSession(session_id); + std::cout << "Failed on attempt " << (i + 1) << std::endl; + FAIL() << "Failed to generate request: i = " << i; + return; + } + } + OEMCrypto_CloseSession(session_id); +} } // namespace wvoec diff --git a/oemcrypto/util/include/cmac.h b/oemcrypto/util/include/cmac.h new file mode 100644 index 0000000..85ef7c4 --- /dev/null +++ b/oemcrypto/util/include/cmac.h @@ -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. +// +// Reference implementation utilities of OEMCrypto APIs +// +#ifndef WVOEC_UTIL_CMAC_H_ +#define WVOEC_UTIL_CMAC_H_ + +#include +#include + +#include +#include + +#include + +namespace wvoec { +namespace util { +class Cmac { + public: + // Creates an AES-128-CMAC or an AES-256-CMAC depending on |key_size|. + // Returns an empty pointer if the key size is not valid. + static std::unique_ptr Create(const uint8_t* key, size_t key_size); + static std::unique_ptr Create(const std::vector& key); + + // Updates the CMAC with more data. This allows for streaming or + // scatter-gather based MAC generation. + // Returns true if the data was updated successfully and false + // if any unexpected errors occur. + bool Update(const uint8_t* data, size_t data_length); + bool Update(const std::vector& data); + bool Update(uint8_t datum); + + // Generates the final MAC and stores it in the |mac| output + // parameter. + // After finalizing, one must reset the Cmac instance before it + // can digest additional information. + bool Finalize(std::vector* mac); + // Similar to Finalize() except that the output is appended to + // the end of the provided |mac| buffer. + bool FinalizeAppend(std::vector* mac); + + // Clears the underlying CMAC without clearing the key. Resetting + // it to its post-initialization state. + void Reset(); + + ~Cmac(); + + private: + Cmac() {} + + // Assumes |key_size| is a valid AES-128 or AES-256 key. + bool Init(const uint8_t* key, size_t key_size); + + CMAC_CTX* ctx_ = nullptr; + bool ready_ = false; +}; +} // namespace util +} // namespace wvoec +#endif // WVOEC_UTIL_CMAC_H_ diff --git a/oemcrypto/util/include/oemcrypto_drm_key.h b/oemcrypto/util/include/oemcrypto_drm_key.h new file mode 100644 index 0000000..257429d --- /dev/null +++ b/oemcrypto/util/include/oemcrypto_drm_key.h @@ -0,0 +1,85 @@ +// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. +// +// Reference implementation utilities of OEMCrypto APIs +// +#ifndef WVOEC_UTIL_DRM_KEY_H_ +#define WVOEC_UTIL_DRM_KEY_H_ + +#include +#include +#include + +#include "OEMCryptoCENCCommon.h" +#include "oemcrypto_ecc_key.h" +#include "oemcrypto_rsa_key.h" + +namespace wvoec { +namespace util { +// DRM private key performs all of the operations required by an +// OEMCrypto session's RSA/ECC private key. +class DrmPrivateKey { + public: + // Create an RSA-based DRM key. + static std::unique_ptr Create( + std::shared_ptr&& rsa_key); + static std::unique_ptr Create( + std::unique_ptr&& rsa_key); + // Create an ECC-based DRM key. + static std::unique_ptr Create( + std::shared_ptr&& ecc_key); + static std::unique_ptr Create( + std::unique_ptr&& ecc_key); + + bool IsRsaKey() const { return static_cast(rsa_key_); } + bool IsEccKey() const { return static_cast(ecc_key_); } + + // Generates a session key from the key source. + // For RSA keys, |key_source| is an encrypted session key. + // For ECC keys, |key_source| is a ephemeral public key to be + // used in ECDH. + OEMCryptoResult GetSessionKey(const uint8_t* key_source, + size_t key_source_size, + std::vector* session_key) const; + std::vector GetSessionKey( + const std::vector& key_source) const; + + // Generates a encryption key from the key source. + // For RSA keys, |key_source| is an encrypted encryption key. + // For ECC keys, this method is not supported. + std::vector GetEncryptionKey( + const std::vector& key_source) const; + + // Generates a signature for the provided message. + // For RSA keys, the signature is RSASSA-PSS. + // For ECC keys, the signature is ECDSA. + OEMCryptoResult GenerateSignature(const uint8_t* message, + size_t message_length, uint8_t* signature, + size_t* signature_length) const; + std::vector GenerateSignature( + const std::vector& message) const; + size_t SignatureSize() const; + + // Generates a signature for the provided message. + // For RSA keys, the signature is RSASSA-PKCS1. + // For ECC keys, this is not supported. + OEMCryptoResult GenerateRsaSignature(const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length) const; + std::vector GenerateRsaSignature( + const std::vector& message) const; + + ~DrmPrivateKey() {} + + private: + DrmPrivateKey() {} + + // Only one will be set. + std::shared_ptr ecc_key_; + std::shared_ptr rsa_key_; +}; +} // namespace util +} // namespace wvoec +#endif // WVOEC_UTIL_DRM_KEY_H_ diff --git a/oemcrypto/util/include/oemcrypto_ecc_key.h b/oemcrypto/util/include/oemcrypto_ecc_key.h new file mode 100644 index 0000000..62df66d --- /dev/null +++ b/oemcrypto/util/include/oemcrypto_ecc_key.h @@ -0,0 +1,281 @@ +// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. +// +// Reference implementation utilities of OEMCrypto APIs +// +#ifndef WVOEC_UTIL_ECC_KEY_H_ +#define WVOEC_UTIL_ECC_KEY_H_ + +#include +#include + +#include +#include +#include + +#include + +#include "OEMCryptoCENCCommon.h" + +namespace wvoec { +namespace util { +enum EccCurve { + kEccCurveUnknown = 0, + kEccSecp256r1 = 256, + kEccSecp384r1 = 384, + kEccSecp521r1 = 521 +}; + +// Returns the string representation of the provided curve. +// Intended for logging purposes. +std::string EccCurveToString(EccCurve curve); + +class EccPrivateKey; + +class EccPublicKey { + public: + // Creates a new public key equivalent of the provided private key. + static std::unique_ptr New(const EccPrivateKey& private_key); + + // Loads a serialized EC public key. + // The provided |buffer| must contain a valid ASN.1 DER encoded + // SubjectPublicKey. Only supported curves by this API are those + // enumerated by EccCurve. + // + // buffer: SubjectPublicKeyInfo = { + // algorithm: AlgorithmIdentifier = { + // algorithm: OID = id-ecPublicKey, + // parameters: ECParameters = { + // namedCurve: OID = secp256r1 | secp384r1 | secp521r1 + // } + // }, + // subjectPublicKey: BIT STRING = ... -- SEC1 encoded ECPoint + // } + // + // Failure will occur if the provided |buffer| does not contain a + // valid SubjectPublicKey, or if the specified curve is not + // supported. + static std::unique_ptr Load(const uint8_t* buffer, + size_t length); + static std::unique_ptr Load(const std::string& buffer); + static std::unique_ptr Load(const std::vector& buffer); + + // Loads a serialized ECC private key, but only converting the public key. + static std::unique_ptr LoadPrivateKeyInfo(const uint8_t* buffer, + size_t length); + static std::unique_ptr LoadPrivateKeyInfo( + const std::string& buffer); + static std::unique_ptr LoadPrivateKeyInfo( + const std::vector& buffer); + + EccCurve curve() const { return curve_; } + const EC_KEY* GetEcKey() const { return key_; } + + // Checks if the provided |private_key| is the EC private key of this + // public key. + bool IsMatchingPrivateKey(const EccPrivateKey& private_key) const; + + // Serializes the public key into an ASN.1 DER encoded SubjectPublicKey + // representation. + // On success, |buffer_size| is populated with the number of bytes + // written to |buffer|, and OEMCrypto_SUCCESS is returned. + // If the provided |buffer_size| is too small, ERROR_SHORT_BUFFER + // is returned and |buffer_size| is set to the required buffer size. + OEMCryptoResult Serialize(uint8_t* buffer, size_t* buffer_size) const; + // Same as above, except directly returns the serialized key. + // Returns an empty vector on error. + std::vector Serialize() const; + + // Verifies the |signature| matches the provided |message| by the + // private equivalent of this public key. + // The |signature| should be a valid ASN.1 DER encoded + // ECDSA-Sig-Value. + // This implementation uses ECDSA with the following digest + // algorithms for the supported curve types. + // - SHA-256 / secp256r1 + // - SHA-384 / secp384r1 (optional support) + // - SHA-512 / secp521r1 (optional support) + // Returns: + // OEMCrypto_SUCCESS if signature is valid + // OEMCrypto_ERROR_SIGNATURE_FAILURE if the signature is invalid + // Any other result indicates an unexpected error + OEMCryptoResult VerifySignature(const uint8_t* message, size_t message_length, + const uint8_t* signature, + size_t signature_length) const; + OEMCryptoResult VerifySignature(const std::string& message, + const std::string& signature) const; + OEMCryptoResult VerifySignature(const std::vector& message, + const std::vector& signature) const; + + ~EccPublicKey(); + + EccPublicKey(const EccPublicKey&) = delete; + EccPublicKey(EccPublicKey&&) = delete; + const EccPublicKey& operator=(const EccPublicKey&) = delete; + EccPublicKey& operator=(EccPublicKey&&) = delete; + + private: + EccPublicKey() {} + + // Initializes the public key object using the provided |buffer|. + // In case of any failure, false is return and the key should be + // discarded. + bool InitFromSubjectPublicKeyInfo(const uint8_t* buffer, size_t length); + bool InitFromPrivateKeyInfo(const uint8_t* buffer, size_t length); + // Initializes the public key object from a private. + bool InitFromPrivateKey(const EccPrivateKey& private_key); + + // OpenSSL/BoringSSL implementation of an ECC key. + // As a public key, this will only have key point initialized. + EC_KEY* key_ = nullptr; + EccCurve curve_ = kEccCurveUnknown; +}; // class EccPublicKey + +class EccPrivateKey { + public: + // Creates a new, pseudorandom ECC private key belonging to the + // curve specified. + static std::unique_ptr New(EccCurve curve); + + // Loads a serialized ECC private key. + // The provided |buffer| must contain a valid ASN.1 DER encoded + // PrivateKeyInfo containing a valid ECC key description. Only + // supported curves by this API are those enumerated by EccCurve. + // + // PrivateKeyInfo := { + // version: INTEGER = v1(0) | v2(1), + // privateKeyAlgorithm: AlgorithmIdentifier := { + // algorithm: OID = id-ecPublicKey, + // parameters: ECParameters = { + // namedCurve: OID = secp256r1 | secp384r1 | secp521r1 + // } + // }, + // privateKey: OCTET STRING = ..., -- BER encoding of ECPrivateKey + // } + // + // ECPrivateKey := { + // version: INTEGER = ecPrivateKeyVer1(1), + // privateKey: OCTET STRING = ..., -- I2OSP of private key point + // -- |parameters| are obtained from PrivateKeyInfo + // publicKey: BIT STRING OPTIONAL = ... -- SEC1 encoded ECPoint + // } + // Note: If the public key is not included, then it is computed from + // the private key. + // + // References: + // RFC 5208 - Description of PrivateKeyInfo + // RFC 5480 - Curve OIDs + // RFC 5915 - Description of ECPrivateKey in PrivateKeyInfo + // + // Failure will occur if the provided |buffer| does not contain a + // valid PrivateKeyInfo, key is not an ECC key, the specified + // curve is not supported, or the key is not valid. + static std::unique_ptr Load(const uint8_t* buffer, + size_t length); + static std::unique_ptr Load(const std::string& buffer); + static std::unique_ptr Load( + const std::vector& buffer); + + // Creates a new ECC public key of this private key. + // Equivalent to calling EccPublicKey::New with this private + // key. + std::unique_ptr MakePublicKey() const; + + EccCurve curve() const { return curve_; } + const EC_KEY* GetEcKey() const { return key_; } + + // Checks if the provided |public_key| is the EC public key of this + // private key. + bool IsMatchingPublicKey(const EccPublicKey& public_key) const; + + // Serializes the private key into an ASN.1 DER encoded PrivateKeyInfo + // representation. + // On success, |buffer_size| is populated with the number of bytes + // written to |buffer|, and SUCCESS is returned. + // If the provided |buffer_size| is too small, + // OEMCrypto_ERROR_SHORT_BUFFER is returned and |buffer_size| is + // set to the required buffer size. + OEMCryptoResult Serialize(uint8_t* buffer, size_t* buffer_size) const; + // Same as above, except directly returns the serialized key. + // Returns an empty vector on error. + std::vector Serialize() const; + + // Serializes the public component of the private key into an ASN.1 + // DER encoded SubjectPublicKey representation. + // On success, |buffer_size| is populated with the number of bytes + // written to |buffer|, and SUCCESS is returned. + // If the provided |buffer_size| is too small, + // OEMCrypto_ERROR_SHORT_BUFFER is returned and |buffer_size| is + // set to the required buffer size. + OEMCryptoResult SerializeAsPublicKey(uint8_t* buffer, + size_t* buffer_size) const; + // Same as above, except directly returns the serialized key. + // Returns an empty vector on error. + std::vector SerializeAsPublicKey() const; + + // Signs the provided |message| and serializes the signature + // point to |signature| as a ASN.1 DER encoded ECDSA-Sig-Value. + // This implementation uses ECDSA with the following digest + // algorithms for the supported curve types. + // - SHA-256 / secp256r1 + // - SHA-384 / secp384r1 (optional support) + // - SHA-512 / secp521r1 (optional support) + // On success, |signature_length| is populated with the number of + // bytes written to |signature|, and SUCCESS is returned. + // If the provided |signature_length| is too small, + // OEMCrypto_ERROR_SHORT_BUFFER is returned and |signature_length| + // is set to the required signature size. + OEMCryptoResult GenerateSignature(const uint8_t* message, + size_t message_length, uint8_t* signature, + size_t* signature_length) const; + // Same as above, except directly returns the serialized signature. + // Returns an empty vector on error. + std::vector GenerateSignature( + const std::vector& message) const; + std::vector GenerateSignature(const std::string& message) const; + // Returns an upper bound for the signature size. May be larger than + // the actual signature generated by GenerateSignature(). + size_t SignatureSize() const; + + // Derives the OEMCrypto session key used for deriving other keys. + // The provided public key must be of the same curve. + // On success, |session_key_size| is populated with the number of + // bytes written to |session_key|, and OEMCrypto_SUCCESS is returned. + // If the provided |session_key_size| is too small, + // OEMCrypto_ERROR_SHORT_BUFFER is returned and |session_key_size| + // is set to the required buffer size. + OEMCryptoResult DeriveSessionKey(const EccPublicKey& public_key, + uint8_t* session_key, + size_t* session_key_size) const; + // Same as above, except directly returns the derived key. + std::vector DeriveSessionKey(const EccPublicKey& public_key) const; + // Returns the byte length of the symmetric key that would be derived + // by DeriveSymmetricKey(). + size_t SessionKeyLength() const; + + ~EccPrivateKey(); + + EccPrivateKey(const EccPrivateKey&) = delete; + EccPrivateKey(EccPrivateKey&&) = delete; + const EccPrivateKey& operator=(const EccPrivateKey&) = delete; + EccPrivateKey& operator=(EccPrivateKey&&) = delete; + + private: + EccPrivateKey() {} + + // Initializes the public key object using the provided |buffer|. + // In case of any failure, false is return and the key should be + // discarded. + bool InitFromPrivateKeyInfo(const uint8_t* buffer, size_t length); + // Generates a new key based on the provided curve. + bool InitFromCurve(EccCurve curve); + + // OpenSSL/BoringSSL implementation of an ECC key. + // The public point of the key will always be present. + EC_KEY* key_ = nullptr; + EccCurve curve_ = kEccCurveUnknown; +}; // class EccPrivateKey +} // namespace util +} // namespace wvoec +#endif // WVOEC_UTIL_ECC_KEY_H_ diff --git a/oemcrypto/util/include/oemcrypto_key_deriver.h b/oemcrypto/util/include/oemcrypto_key_deriver.h new file mode 100644 index 0000000..4a52547 --- /dev/null +++ b/oemcrypto/util/include/oemcrypto_key_deriver.h @@ -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. +// +// Reference implementation utilities of OEMCrypto APIs +// +#ifndef WVOEC_UTIL_KEY_DERIVER_H_ +#define WVOEC_UTIL_KEY_DERIVER_H_ + +#include +#include + +#include +#include + +#include "cmac.h" + +namespace wvoec { +namespace util { +class KeyDeriver { + public: + // Create a new key deriver using either the session key or the device + // key. + // Returns an empty pointer if the key size is not valid. + static std::unique_ptr Create(const uint8_t* key, + size_t key_size); + static std::unique_ptr Create(const std::vector& key); + + // Derive the mac_key[server] from the provided |mac_key_context|. + bool DeriveServerMacKey(const uint8_t* mac_key_context, + size_t mac_key_context_size, + std::vector* mac_key_server); + bool DeriveServerMacKey(const std::vector& mac_key_context, + std::vector* mac_key_server); + + // Derive the mac_key[client] from the provided |mac_key_context|. + bool DeriveClientMacKey(const uint8_t* mac_key_context, + size_t mac_key_context_size, + std::vector* mac_key_client); + bool DeriveClientMacKey(const std::vector& mac_key_context, + std::vector* mac_key_client); + + // Derive the enc_key from the provided |enc_key_context|. + bool DeriveEncryptionKey(const uint8_t* enc_key_context, + size_t enc_key_context_size, + std::vector* enc_key); + bool DeriveEncryptionKey(const std::vector& enc_key_context, + std::vector* enc_key); + + ~KeyDeriver() {} + + private: + KeyDeriver() {} + + bool Init(const uint8_t* key, size_t key_size); + + std::unique_ptr cmac_; +}; +} // namespace util +} // namespace wvoec +#endif // WVOEC_UTIL_KEY_DERIVER_H_ diff --git a/oemcrypto/util/include/oemcrypto_oem_cert.h b/oemcrypto/util/include/oemcrypto_oem_cert.h new file mode 100644 index 0000000..f633280 --- /dev/null +++ b/oemcrypto/util/include/oemcrypto_oem_cert.h @@ -0,0 +1,104 @@ +// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. +// +// Reference implementation utilities of OEMCrypto APIs +// +#ifndef WVOEC_UTIL_OEM_CERT_H_ +#define WVOEC_UTIL_OEM_CERT_H_ + +#include +#include + +#include "OEMCryptoCENCCommon.h" + +namespace wvoec { +namespace util { +class OemPublicCertificate; + +// An OEM Certificate is a factory provisioned root of trust +// certificate which consists of a public certificate and its +// matching private key. +// The public certificate must be an ASN.1 DER encoded PKCS #7 +// ContentInfo of type signedData (RFC2315). The device's X.509 +// certificate must be the first certificate in the chain of +// SignedContent |certificates|. +// The certificates are X.509 Certificate as defined in RFC 5280 +// signed by the device manufacturers certificate which is signed +// by Google. +// The OEM Public Cert should only contain the device's certificate +// and the OEM's intermediate certificate. +// The private key storage format is at the discretion of the OEM; +// the reference implementation uses PKCS8 PrivateKeyInfo. +class OemCertificate { + public: + enum KeyType { + kNone = 0, + // Private key is an ASN.1 DER encoded PrivateKeyInfo specifying + // an RSA encryption key. + kRsa = 1 + }; + + // Creates a new OEM Certificate and performs basic validation + // to ensure that the private key and public cert are well-formed. + // The |public_cert| provided is parsed as an X.509 Certificate + // and the public key is verified against the private key. + // The |private_key| is parsed depending on the key type. + // If any error occurs or if the provided data is malformed, an + // empty pointer is returned. + static std::unique_ptr Create(const uint8_t* private_key, + size_t private_key_size, + const uint8_t* public_cert, + size_t public_cert_size); + static std::unique_ptr Create( + const std::vector& private_key, + const std::vector& public_cert); + + // Returns the key type of the OEM Public key and private key. + // As of OEMCrypto v16, the only supported key type is RSA. + KeyType key_type() const; + + // Returns the private key data. Intended to be used for calls + // to OEMCrypto_LoadOEMPrivateKey(). + const std::vector& GetPrivateKey() const { return private_key_; } + + // Returns a copy of the ASN.1 DER encoded PKCS #7 certificate chain. + // If |*public_cert_length| is large enough, the complete + // certificate is copied to the buffer specified by |public_cert|, + // |*public_cert_length| is adjusted to the actual size of the + // certificate data, and SUCCESS is returned. + // If |*public_cert_length| is not large enough, then it is + // set to size of the certificate and ERROR_SHORT_BUFFER is + // returned. + OEMCryptoResult GetPublicCertificate(uint8_t* public_cert, + size_t* public_cert_length) const; + // Returns the certificate directly. Intended to be used for + // testing. + const std::vector& GetPublicCertificate() const; + + // Verifies that the RSA key included in the OEM Cert is valid. + // The existence of an OemCertificate already ensures that the + // OEM Public Certificate and private key data are well-formed. + // This takes the check another step further and ensures that + // the private key matches the public key in the public cert + // (ie, same modulos and public exponent). + OEMCryptoResult IsCertificateValid() const; + + ~OemCertificate(); + + OemCertificate(const OemCertificate&) = delete; + OemCertificate(OemCertificate&&) = delete; + const OemCertificate& operator=(const OemCertificate&) = delete; + OemCertificate& operator=(OemCertificate&&) = delete; + + private: + OemCertificate(); + + // Serialized private key matching the OEM certificate. + std::vector private_key_; + // Serialized OEM Certificate. + std::unique_ptr public_cert_; +}; // class OemCertificate +} // namespace util +} // namespace wvoec +#endif // WVOEC_UTIL_OEM_CERT_H_ diff --git a/oemcrypto/util/include/oemcrypto_rsa_key.h b/oemcrypto/util/include/oemcrypto_rsa_key.h new file mode 100644 index 0000000..d66f527 --- /dev/null +++ b/oemcrypto/util/include/oemcrypto_rsa_key.h @@ -0,0 +1,376 @@ +// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. +// +// Reference implementation utilities of OEMCrypto APIs +// +#ifndef WVOEC_UTIL_RSA_KEY_H_ +#define WVOEC_UTIL_RSA_KEY_H_ + +#include +#include + +#include +#include +#include + +#include + +#include "OEMCryptoCENC.h" + +namespace wvoec { +namespace util { +enum RsaFieldSize { + kRsaFieldUnknown = 0, + kRsa2048Bit = 2048, + kRsa3072Bit = 3084 +}; + +// Identifies the RSA signature algorithm to be used when signing +// messages or verifying message signatures. +// The two standard signing algorithms specified by PKCS1 RSA V2.1 +// are RSASSA-PKCS1 and RSASSA-PSS. Each require agreement on a +// set of options. For OEMCrypto, only one set of options are agreed +// upon for each RSA signature scheme. CAST receivers specify a +// special implementation of PKCS1 where the message is already +// digested and encoded when provided. +enum RsaSignatureAlgorithm { + // RSASSA-PSS with default options: + // Hash algorithm: SHA-1 + // MGF: MGF1 with SHA-1 + // Salt length: 20 bytes + // Trailer field: 0xbc + kRsaPssDefault = 0, + // RSASSA-PKCS1 for CAST receivers. + // Assumes message is already digested & encoded. Max message length + // is 83 bytes. + kRsaPkcs1Cast = 1 +}; + +// Returns the string representation of the provided RSA field size. +// Intended for logging purposes. +std::string RsaFieldSizeToString(RsaFieldSize field_size); + +// Compares two OpenSSL/BoringSSL RSA keys to see if their public RSA +// components are matching. +// This function assumes both keys are valid. +// Returns true if they are matching, false otherwise. +bool RsaKeysAreMatchingPair(const RSA* public_key, const RSA* private_key); + +class RsaPrivateKey; + +class RsaPublicKey { + public: + // Creates a new public key equivalent of the provided private key. + static std::unique_ptr New(const RsaPrivateKey& private_key); + + // Creates an RSA public key from a native OpenSSL/BoringSSL RSA key handle. + // Ownership of the handle is NOT transferred. + static std::unique_ptr FromSslHandle( + const RSA* rsa_handle, uint32_t allowed_schemes = kSign_RSASSA_PSS); + + // Loads a serialized RSA public key. + // The provided |buffer| must contain a valid ASN.1 DER encoded + // SubjectPublicKey. This API will reject any RSA key that is not + // approximately to 2048bits or 3072bits. + // + // buffer: SubjectPublicKeyInfo = { + // algorithm: AlgorithmIdentifier = { + // algorithm: OID = rsaEncryption, + // parameters: NULL = null + // }, + // subjectPublicKey: BIT STRING = ... -- ASN.1 DER encoded RSAPublicKey + // } + // + // Failure will occur if the provided |buffer| does not contain a + // valid SubjectPublicKey, or if the specified curve is not + // supported. + static std::unique_ptr Load(const uint8_t* buffer, + size_t length); + static std::unique_ptr Load(const std::string& buffer); + static std::unique_ptr Load(const std::vector& buffer); + + // Loads a serialized RSA private key, but only converting the public key. + static std::unique_ptr LoadPrivateKeyInfo(const uint8_t* buffer, + size_t length); + static std::unique_ptr LoadPrivateKeyInfo( + const std::string& buffer); + static std::unique_ptr LoadPrivateKeyInfo( + const std::vector& buffer); + + RsaFieldSize field_size() const { return field_size_; } + uint32_t allowed_schemes() const { return allowed_schemes_; } + const RSA* GetRsaKey() const { return key_; } + + // Checks if the provided |private_key| is the RSA private key of this + // public key. + bool IsMatchingPrivateKey(const RsaPrivateKey& private_key) const; + + // Serializes the public key into an ASN.1 DER encoded SubjectPublicKey + // representation. + // On success, |buffer_size| is populated with the number of bytes + // written to |buffer|, and OEMCrypto_SUCCESS is returned. + // If the provided |buffer_size| is too small, + // OEMCrypto_ERROR_SHORT_BUFFER is returned and |buffer_size| is set + // to the required buffer size. + OEMCryptoResult Serialize(uint8_t* buffer, size_t* buffer_size) const; + // Same as above, except directly returns the serialized key. + // Returns an empty vector on error. + std::vector Serialize() const; + + // Verifies the |signature| matches the provided |message| by the + // private equivalent of this public key. + // The signature algorithm can be specified via the |algorithm| field. + // See RsaSignatureAlgorithm for details on each algorithm. + // + // Returns: + // OEMCrypto_SUCCESS if signature is valid + // OEMCrypto_ERROR_SIGNATURE_FAILURE if the signature is invalid + // OEMCrypto_ERROR_UNKNOWN_FAILURE if any error occurs + OEMCryptoResult VerifySignature( + const uint8_t* message, size_t message_length, const uint8_t* signature, + size_t signature_length, + RsaSignatureAlgorithm algorithm = kRsaPssDefault) const; + OEMCryptoResult VerifySignature( + const std::string& message, const std::string& signature, + RsaSignatureAlgorithm algorithm = kRsaPssDefault) const; + OEMCryptoResult VerifySignature( + const std::vector& message, + const std::vector& signature, + RsaSignatureAlgorithm algorithm = kRsaPssDefault) const; + + // Encrypts the OEMCrypto session key used for deriving other keys. + // On success, |enc_session_key_size| is populated with the number + // of bytes written to |enc_session_key|, and OEMCrypto_SUCCESS is + // returned. If the provided |enc_session_key_size| is too small, + // OEMCrypto_ERROR_SHORT_BUFFER is returned and + // |enc_session_key_size| is set to the required buffer size. + OEMCryptoResult EncryptSessionKey(const uint8_t* session_key, + size_t session_key_size, + uint8_t* enc_session_key, + size_t* enc_session_key_size) const; + // Same as above, except directly returns the encrypted key. + std::vector EncryptSessionKey( + const std::vector& session_key) const; + std::vector EncryptSessionKey(const std::string& session_key) const; + + // Encrypts the OEMCrypto encryption key used for encrypting the + // DRM private key. + // On success, |enc_encryption_key_size| is populated with the + // number of bytes written to |enc_encryption_key|, and + // OEMCrypto_SUCCESS is returned. + // If the provided |enc_encryption_key_size| is too small, + // OEMCrypto_ERROR_SHORT_BUFFER is returned and + // |enc_encryption_key_size| is set to the required buffer size. + OEMCryptoResult EncryptEncryptionKey(const uint8_t* encryption_key, + size_t encryption_key_size, + uint8_t* enc_encryption_key, + size_t* enc_encryption_key_size) const; + // Same as above, except directly returns the encrypted key. + std::vector EncryptEncryptionKey( + const std::vector& encryption_key) const; + std::vector EncryptEncryptionKey( + const std::string& encryption_key) const; + + ~RsaPublicKey(); + + RsaPublicKey(const RsaPublicKey&) = delete; + RsaPublicKey(RsaPublicKey&&) = delete; + const RsaPublicKey& operator=(const RsaPublicKey&) = delete; + RsaPublicKey& operator=(RsaPublicKey&&) = delete; + + private: + RsaPublicKey() {} + + // Initializes the public key object using the provided |buffer|. + // In case of any failure, false is return and the key should be + // discarded. + bool InitFromSubjectPublicKeyInfo(const uint8_t* buffer, size_t length); + bool InitFromPrivateKeyInfo(const uint8_t* buffer, size_t length); + // Initializes the public key object from a private. + bool InitFromPrivateKey(const RsaPrivateKey& private_key); + // Initializes the public key object from an existing + // OpenSSL/BoringSSL RSA key handle. The RSA key must be + // initialized and |allowed_schemes| must be a valid value. + bool InitFromSslHandle(const RSA* rsa_handle, uint32_t allowed_schemes); + + // Signature specialization functions. + OEMCryptoResult VerifySignaturePss(const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length) const; + OEMCryptoResult VerifySignaturePkcs1Cast(const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length) const; + + // RSAES-OAEP encrypt. + OEMCryptoResult EncryptOaep(const uint8_t* message, size_t message_size, + uint8_t* enc_message, + size_t* enc_message_length) const; + + // OpenSSL/BoringSSL implementation of an RSA key. + // Will only include components of an RSA public key. + RSA* key_ = nullptr; + uint32_t allowed_schemes_ = 0; + RsaFieldSize field_size_ = kRsaFieldUnknown; +}; // class RsaPublicKey + +class RsaPrivateKey { + public: + // Creates a new, pseudorandom RSA private key. + static std::unique_ptr New(RsaFieldSize field_size); + + // Loads a serialized RSA private key. + // The provided |buffer| must contain a valid ASN.1 DER encoded + // PrivateKeyInfo (RFC 5208). + // + // buffer: PrivateKeyInfo = { + // version: INTEGER = v1(0), + // privateKeyAlgorithm: OID = rsaEncryption, + // privateKey: OCTET STRING = ..., + // -- BER encoding of RSAPrivateKey (RFC 3447) + // attributes: Attributes = ... -- Optional, not used by OEMCrypto + // } + // Note: If the public key is not included, then it is computed from + // the private. + // + // Failure will occur if the provided |buffer| does not contain a + // valid RSAPrivateKey, or if the specified curve is not supported. + static std::unique_ptr Load(const uint8_t* buffer, + size_t length); + static std::unique_ptr Load(const std::string& buffer); + static std::unique_ptr Load( + const std::vector& buffer); + + // Creates a new RSA public key of this private key. + // Equivalent to calling RsaPublicKey::New with this private + // key. + std::unique_ptr MakePublicKey() const; + + RsaFieldSize field_size() const { return field_size_; } + uint32_t allowed_schemes() const { return allowed_schemes_; } + const RSA* GetRsaKey() const { return key_; } + + // Checks if the provided |public_key| is the RSA public key of this + // private key. + bool IsMatchingPublicKey(const RsaPublicKey& public_key) const; + + // Serializes the private key into an ASN.1 DER encoded X + // representation. + // On success, |buffer_size| is populated with the number of bytes + // written to |buffer|, and OEMCrypto_SUCCESS is returned. + // If the provided |buffer_size| is too small, + // OEMCrypto_ERROR_SHORT_BUFFER is returned and |buffer_size| is + // set to the required buffer size. + OEMCryptoResult Serialize(uint8_t* buffer, size_t* buffer_size) const; + // Same as above, except directly returns the serialized key. + // Returns an empty vector on error. + std::vector Serialize() const; + + // Signs the provided |message| using the RSA signing algorithm + // specified by |algorithm|. See RsaSignatureAlgorithm for + // details on each algorithm. + // + // On success, |signature_length| is populated with the number of + // bytes written to |signature|, and OEMCrypto_SUCCESS is returned. + // If the provided |signature_length| is too small, + // OEMCrypto_ERROR_SHORT_BUFFER is returned and |signature_length| + // is set to the required signature size. + OEMCryptoResult GenerateSignature(const uint8_t* message, + size_t message_length, + RsaSignatureAlgorithm algorithm, + uint8_t* signature, + size_t* signature_length) const; + // Same as above, except directly returns the serialized signature. + // Returns an empty vector on error. + std::vector GenerateSignature( + const std::vector& message, + RsaSignatureAlgorithm algorithm = kRsaPssDefault) const; + std::vector GenerateSignature( + const std::string& message, + RsaSignatureAlgorithm algorithm = kRsaPssDefault) const; + // Returns an upper bound for the signature size. May be larger than + // the actual signature generated by GenerateSignature(). + size_t SignatureSize() const; + + // Decrypts the OEMCrypto session key used for deriving other keys. + // On success, |session_key_size| is populated with the number of + // bytes written to |session_key|, and OEMCrypto_SUCCESS is returned. + // If the provided |session_key_size| is too small, + // OEMCrypto_ERROR_SHORT_BUFFER is returned and |session_key_size| + // is set to the required buffer size. + OEMCryptoResult DecryptSessionKey(const uint8_t* enc_session_key, + size_t enc_session_key_size, + uint8_t* session_key, + size_t* session_key_size) const; + // Same as above, except directly returns the decrypted key. + std::vector DecryptSessionKey( + const std::vector& enc_session_key) const; + std::vector DecryptSessionKey( + const std::string& enc_session_key) const; + // Returns the byte length of the symmetric key that would be derived + // by DecryptSessionKey(). + size_t SessionKeyLength() const; + + // Decrypts the OEMCrypto encryption key used for decrypting DRM + // private key. + // On success, |encryption_key_size| is populated with the number of + // bytes written to |encryption_key|, and OEMCrypto_SUCCESS is + // returned. + // If the provided |encryption_key_size| is too small, + // OEMCrypto_ERROR_SHORT_BUFFER is returned and |encryption_key_size| + // is set to the required buffer size. + OEMCryptoResult DecryptEncryptionKey(const uint8_t* enc_encryption_key, + size_t enc_encryption_key_size, + uint8_t* encryption_key, + size_t* encryption_key_size) const; + // Same as above, except directly returns the decrypted key. + std::vector DecryptEncryptionKey( + const std::vector& enc_encryption_key) const; + std::vector DecryptEncryptionKey( + const std::string& enc_encryption_key) const; + + ~RsaPrivateKey(); + + RsaPrivateKey(const RsaPrivateKey&) = delete; + RsaPrivateKey(RsaPrivateKey&&) = delete; + const RsaPrivateKey& operator=(const RsaPrivateKey&) = delete; + RsaPrivateKey& operator=(RsaPrivateKey&&) = delete; + + private: + RsaPrivateKey() {} + + // Initializes the public key object using the provided |buffer|. + // In case of any failure, false is return and the key should be + // discarded. + bool InitFromPrivateKeyInfo(const uint8_t* buffer, size_t length); + // Generates a new key based on the provided field size. + bool InitFromFieldSize(RsaFieldSize field_size); + + // Signature specialization functions. + OEMCryptoResult GenerateSignaturePss(const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length) const; + OEMCryptoResult GenerateSignaturePkcs1Cast(const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length) const; + + // RSAES-OAEP decrypt. + OEMCryptoResult DecryptOaep(const uint8_t* enc_message, + size_t enc_message_size, uint8_t* message, + size_t expected_message_length) const; + + // OpenSSL/BoringSSL implementation of an RSA key. + // Will include all components of an RSA private key. + RSA* key_ = nullptr; + uint32_t allowed_schemes_ = 0; + // Set true if the deserialized key contained an allowed schemes. + bool explicit_schemes_ = false; + RsaFieldSize field_size_ = kRsaFieldUnknown; +}; // class RsaPrivateKey +} // namespace util +} // namespace wvoec +#endif // WVOEC_UTIL_RSA_KEY_H_ diff --git a/oemcrypto/util/include/scoped_object.h b/oemcrypto/util/include/scoped_object.h new file mode 100644 index 0000000..115b617 --- /dev/null +++ b/oemcrypto/util/include/scoped_object.h @@ -0,0 +1,70 @@ +// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. +// +// Reference implementation utilities of OEMCrypto APIs +// +#ifndef WVOEC_UTIL_SCOPED_OBJECT_H_ +#define WVOEC_UTIL_SCOPED_OBJECT_H_ + +namespace wvoec { +namespace util { +// A generic wrapper around pointer. This allows for automatic +// memory clean up when the ScopedObject variable goes out of scope. +// This is intended to be used with OpenSSL/BoringSSL structs. +template +class ScopedObject { + public: + ScopedObject() : ptr_(nullptr) {} + ScopedObject(Type* ptr) : ptr_(ptr) {} + ~ScopedObject() { + if (ptr_) { + Destructor(ptr_); + ptr_ = nullptr; + } + } + + // Copy construction and assignment are not allowed. + ScopedObject(const ScopedObject& other) = delete; + ScopedObject& operator=(const ScopedObject& other) = delete; + + // Move construction and assignment are allowed. + ScopedObject(ScopedObject&& other) : ptr_(other.ptr_) { + other.ptr_ = nullptr; + } + ScopedObject& operator=(ScopedObject&& other) { + if (ptr_) { + Destructor(ptr_); + } + ptr_ = other.ptr_; + other.ptr_ = nullptr; + return *this; + } + + explicit operator bool() const { return ptr_ != nullptr; } + + Type& operator*() { return *ptr_; } + Type* get() const { return ptr_; } + Type* operator->() const { return ptr_; } + + // Releasing the pointer will remove the responsibility of the + // ScopedObject to clean up the pointer. + Type* release() { + Type* temp = ptr_; + ptr_ = nullptr; + return temp; + } + + void reset(Type* ptr = nullptr) { + if (ptr_) { + Destructor(ptr_); + } + ptr_ = ptr; + } + + private: + Type* ptr_ = nullptr; +}; +} // namespace util +} // namespace wvoec +#endif // WVOEC_UTIL_SCOPED_OBJECT_H_ diff --git a/oemcrypto/util/include/wvcrc32.h b/oemcrypto/util/include/wvcrc32.h new file mode 100644 index 0000000..3d913ef --- /dev/null +++ b/oemcrypto/util/include/wvcrc32.h @@ -0,0 +1,22 @@ +// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine +// License Agreement. +// +// Compute CRC32/MPEG2 Checksum. Needed for verification of WV Keybox. +// +#ifndef WVOEC_UTIL_WVCRC32_H_ +#define WVOEC_UTIL_WVCRC32_H_ + +#include + +namespace wvoec { +namespace util { +uint32_t wvcrc32(const uint8_t* p_begin, size_t i_count); +uint32_t wvcrc32Init(); +uint32_t wvcrc32Cont(const uint8_t* p_begin, size_t i_count, uint32_t prev_crc); + +// Convert to network byte order +uint32_t wvcrc32n(const uint8_t* p_begin, size_t i_count); +} // namespace util +} // namespace wvoec +#endif // WVOEC_UTIL_WVCRC32_H_ diff --git a/oemcrypto/util/oec_ref_util.gyp b/oemcrypto/util/oec_ref_util.gyp new file mode 100644 index 0000000..c2c688c --- /dev/null +++ b/oemcrypto/util/oec_ref_util.gyp @@ -0,0 +1,20 @@ +# Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary +# source code may only be used and distributed under the Widevine +# License Agreement. +{ + 'variables': { + 'oemcrypto_dir': '..', + 'util_dir': '../../util', + }, + 'targets': [ + { + 'target_name': 'oec_ref_util', + 'type': 'static_library', + 'standalone_static_library': 1, + 'hard_dependency': 1, + 'includes': [ + 'oec_ref_util.gypi', + ], + }, + ], +} diff --git a/oemcrypto/util/oec_ref_util.gypi b/oemcrypto/util/oec_ref_util.gypi new file mode 100644 index 0000000..e238fda --- /dev/null +++ b/oemcrypto/util/oec_ref_util.gypi @@ -0,0 +1,32 @@ +# Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary +# source code may only be used and distributed under the Widevine +# License Agreement. +{ + 'variables': { + 'privacy_crypto_impl%': 'boringssl', + 'boringssl_libcrypto_path%': '../../third_party/boringssl/boringssl.gyp:crypto', + }, + 'include_dirs': [ + '<(oemcrypto_dir)/include', + '<(oemcrypto_dir)/util/include', + '<(util_dir)/include', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(oemcrypto_dir)/include', + '<(oemcrypto_dir)/util/include', + ], + }, + 'sources': [ + '<(oemcrypto_dir)/util/src/cmac.cpp', + '<(oemcrypto_dir)/util/src/oemcrypto_drm_key.cpp', + '<(oemcrypto_dir)/util/src/oemcrypto_ecc_key.cpp', + '<(oemcrypto_dir)/util/src/oemcrypto_key_deriver.cpp', + '<(oemcrypto_dir)/util/src/oemcrypto_oem_cert.cpp', + '<(oemcrypto_dir)/util/src/oemcrypto_rsa_key.cpp', + '<(oemcrypto_dir)/util/src/wvcrc.cpp', + ], + 'includes': [ + '../../util/libcrypto_dependency.gypi', + ], +} diff --git a/oemcrypto/util/oec_ref_util_unittests.gypi b/oemcrypto/util/oec_ref_util_unittests.gypi new file mode 100644 index 0000000..fd50119 --- /dev/null +++ b/oemcrypto/util/oec_ref_util_unittests.gypi @@ -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_dirs': [ + '<(oemcrypto_dir)/include', + '<(oemcrypto_dir)/util/include', + '<(oemcrypto_dir)/util/test', + '<(util_dir)/include', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(oemcrypto_dir)/include', + '<(oemcrypto_dir)/util/include', + ], + }, + 'sources': [ + '<(oemcrypto_dir)/util/test/cmac_unittest.cpp', + '<(oemcrypto_dir)/util/test/oem_cert_test.cpp', + '<(oemcrypto_dir)/util/test/oemcrypto_ecc_key_unittest.cpp', + '<(oemcrypto_dir)/util/test/oemcrypto_oem_cert_unittest.cpp', + '<(oemcrypto_dir)/util/test/oemcrypto_ref_test_utils.cpp', + '<(oemcrypto_dir)/util/test/oemcrypto_rsa_key_unittest.cpp', + '<(oemcrypto_dir)/util/test/oemcrypto_wvcrc32_unittest.cpp', + ], +} diff --git a/oemcrypto/util/src/cmac.cpp b/oemcrypto/util/src/cmac.cpp new file mode 100644 index 0000000..e22e729 --- /dev/null +++ b/oemcrypto/util/src/cmac.cpp @@ -0,0 +1,173 @@ +// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. +// +// Reference implementation utilities of OEMCrypto APIs +// +#include "cmac.h" + +#include + +#include "log.h" +#include "scoped_object.h" + +namespace wvoec { +namespace util { +namespace { +using ScopedCmacCtx = ScopedObject; +constexpr size_t kAes128KeySize = 16; +constexpr size_t kAes256KeySize = 32; +constexpr size_t kCmacOutputSize = 16; + +// Gets the appropriate AES block cipher for the CMAC algortihm +// based on the key size. +// Ownership of the pointer returned by this function is retained by +// the OpenSSL/BoringSSL framework. +const EVP_CIPHER* KeySizeToCipher(size_t key_size) { + switch (key_size) { + case kAes128KeySize: + return EVP_aes_128_cbc(); + case kAes256KeySize: + return EVP_aes_256_cbc(); + } + LOGE("Unexpected key size: size = %zu", key_size); + return nullptr; +} +} // namespace + +// static +std::unique_ptr Cmac::Create(const uint8_t* key, size_t key_size) { + std::unique_ptr cmac; + if (key == nullptr) { + LOGE("CMAC key is null"); + return cmac; + } + if (key_size != kAes128KeySize && key_size != kAes256KeySize) { + LOGE("Invalid CMAC key size: size = %zu", key_size); + return cmac; + } + cmac.reset(new Cmac()); + if (!cmac->Init(key, key_size)) { + cmac.reset(); + } + return cmac; +} + +// static +std::unique_ptr Cmac::Create(const std::vector& key) { + if (key.empty()) { + LOGE("CMAC key is empty"); + return std::unique_ptr(); + } + return Create(key.data(), key.size()); +} + +bool Cmac::Init(const uint8_t* key, size_t key_size) { + const EVP_CIPHER* const cipher = KeySizeToCipher(key_size); + if (cipher == nullptr) { + LOGE("Failed to get block cipher for CMAC"); + return false; + } + ScopedCmacCtx ctx(CMAC_CTX_new()); + if (!ctx) { + LOGE("Failed allocate CMAC CTX"); + return false; + } + if (!CMAC_Init(ctx.get(), key, key_size, cipher, nullptr)) { + LOGE("Failed to initialize CMAC CTX"); + return false; + } + ctx_ = ctx.release(); + ready_ = true; + return true; +} + +bool Cmac::Update(const uint8_t* data, size_t data_length) { + if (data == nullptr) { + LOGE("Data is null"); + return false; + } + if (data_length == 0) { + return true; + } + if (!ready_) { + LOGE("CMAC must be reset before updating"); + return false; + } + if (!CMAC_Update(ctx_, data, data_length)) { + LOGE("Failed to update CMAC CTX"); + ready_ = false; + return false; + } + return true; +} + +bool Cmac::Update(const std::vector& data) { + return Update(data.data(), data.size()); +} + +bool Cmac::Update(uint8_t datum) { return Update(&datum, 1); } + +bool Cmac::Finalize(std::vector* mac) { + if (mac == nullptr) { + LOGE("Output MAC buffer is null"); + return false; + } + mac->clear(); + return FinalizeAppend(mac); +} + +bool Cmac::FinalizeAppend(std::vector* mac) { + if (mac == nullptr) { + LOGE("Output MAC buffer is null"); + return false; + } + if (!ready_) { + LOGE("CMAC must be reset before finalizing"); + return false; + } + const size_t end = mac->size(); + size_t mac_size = kCmacOutputSize; + mac->resize(end + mac_size); + if (!CMAC_Final(ctx_, &mac->at(end), &mac_size)) { + LOGE("Failed to finalize CMAC CTX"); + mac->resize(end); + ready_ = false; + return false; + } + ready_ = false; + return true; +} + +#ifdef OPENSSL_IS_BORINGSSL +// BoringSSL allows for resetting a CMAC context explicitly, whereas +// OpenSSL does so by reinitializing using all nulls/zeros. This +// causes segfaults on systems using BoringSSL. +void Cmac::Reset() { + if (!CMAC_Reset(ctx_)) { + LOGE("Failed to reset CMAC CTX"); + ready_ = false; + } else { + ready_ = true; + } +} +#else // OpenSSL is OpenSSL +void Cmac::Reset() { + if (!CMAC_Init(ctx_, nullptr, 0, nullptr, nullptr)) { + LOGE("Failed to reset CMAC CTX"); + ready_ = false; + } else { + ready_ = true; + } +} +#endif + +Cmac::~Cmac() { + if (ctx_ != nullptr) { + CMAC_CTX_free(ctx_); + ctx_ = nullptr; + } + ready_ = false; +} +} // namespace util +} // namespace wvoec diff --git a/oemcrypto/util/src/oemcrypto_drm_key.cpp b/oemcrypto/util/src/oemcrypto_drm_key.cpp new file mode 100644 index 0000000..14a35fe --- /dev/null +++ b/oemcrypto/util/src/oemcrypto_drm_key.cpp @@ -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. +// +// Reference implementation utilities of OEMCrypto APIs +// +#include "oemcrypto_drm_key.h" + +#include + +#include "OEMCryptoCENC.h" +#include "log.h" + +namespace wvoec { +namespace util { +// static +std::unique_ptr DrmPrivateKey::Create( + std::shared_ptr&& rsa_key) { + if (!rsa_key) { + LOGE("No RSA key provided"); + return std::unique_ptr(); + } + std::unique_ptr drm_key(new DrmPrivateKey()); + drm_key->rsa_key_ = std::move(rsa_key); + return drm_key; +} + +// static +std::unique_ptr DrmPrivateKey::Create( + std::unique_ptr&& rsa_key) { + if (!rsa_key) { + LOGE("No RSA key provided"); + return std::unique_ptr(); + } + std::unique_ptr drm_key(new DrmPrivateKey()); + drm_key->rsa_key_ = std::move(rsa_key); + return drm_key; +} + +// static +std::unique_ptr DrmPrivateKey::Create( + std::shared_ptr&& ecc_key) { + if (!ecc_key) { + LOGE("No ECC key provided"); + return std::unique_ptr(); + } + std::unique_ptr drm_key(new DrmPrivateKey()); + drm_key->ecc_key_ = std::move(ecc_key); + return drm_key; +} + +// static +std::unique_ptr DrmPrivateKey::Create( + std::unique_ptr&& ecc_key) { + if (!ecc_key) { + LOGE("No ECC key provided"); + return std::unique_ptr(); + } + std::unique_ptr drm_key(new DrmPrivateKey()); + drm_key->ecc_key_ = std::move(ecc_key); + return drm_key; +} + +OEMCryptoResult DrmPrivateKey::GetSessionKey( + const uint8_t* key_source, size_t key_source_size, + std::vector* session_key) const { + if (session_key == nullptr) { + LOGE("Output session key is null"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // RSA -> Decrypt session key. + if (rsa_key_) { + if (!(rsa_key_->allowed_schemes() & kSign_RSASSA_PSS)) { + LOGE("RSA key cannot be used for session key decryption"); + return OEMCrypto_ERROR_INVALID_KEY; + } + size_t session_key_size = rsa_key_->SessionKeyLength(); + session_key->resize(session_key_size); + const OEMCryptoResult res = rsa_key_->DecryptSessionKey( + key_source, key_source_size, session_key->data(), &session_key_size); + if (res != OEMCrypto_SUCCESS) { + session_key->clear(); + return res; + } + session_key->resize(session_key_size); + return OEMCrypto_SUCCESS; + } + // ECC -> ECDH. + // Step 1: Parse |key_source| as ECC key. + std::unique_ptr ephemeral_ecc_key = + EccPublicKey::Load(key_source, key_source_size); + if (!ephemeral_ecc_key) { + LOGE("Failed to load server's ephemeral ECC key"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 2: Derive session key. + size_t session_key_size = ecc_key_->SessionKeyLength(); + session_key->resize(session_key_size); + const OEMCryptoResult res = ecc_key_->DeriveSessionKey( + *ephemeral_ecc_key, session_key->data(), &session_key_size); + if (res != OEMCrypto_SUCCESS) { + session_key->clear(); + return res; + } + session_key->resize(session_key_size); + return OEMCrypto_SUCCESS; +} + +std::vector DrmPrivateKey::GetSessionKey( + const std::vector& key_source) const { + // RSA -> Decrypt session key. + if (rsa_key_) { + if (!(rsa_key_->allowed_schemes() & kSign_RSASSA_PSS)) { + LOGE("RSA key cannot be used for session key decryption"); + return std::vector(); + } + return rsa_key_->DecryptSessionKey(key_source); + } + // ECC -> ECDH. + // Step 1: Parse |key_source| as ECC key. + std::unique_ptr ephemeral_ecc_key = + EccPublicKey::Load(key_source); + if (!ephemeral_ecc_key) { + LOGE("Failed to load server's ephemeral ECC key"); + return std::vector(); + } + // Step 2: Derive session key. + return ecc_key_->DeriveSessionKey(*ephemeral_ecc_key); +} + +std::vector DrmPrivateKey::GetEncryptionKey( + const std::vector& key_source) const { + if (!rsa_key_) { + LOGE("Only RSA DRM keys can derive an encryption key"); + return std::vector(); + } + return rsa_key_->DecryptEncryptionKey(key_source); +} + +OEMCryptoResult DrmPrivateKey::GenerateSignature( + const uint8_t* message, size_t message_length, uint8_t* signature, + size_t* signature_length) const { + if (rsa_key_) { + return rsa_key_->GenerateSignature(message, message_length, kRsaPssDefault, + signature, signature_length); + } + return ecc_key_->GenerateSignature(message, message_length, signature, + signature_length); +} + +std::vector DrmPrivateKey::GenerateSignature( + const std::vector& message) const { + if (rsa_key_) { + return rsa_key_->GenerateSignature(message, kRsaPssDefault); + } + return ecc_key_->GenerateSignature(message); +} + +size_t DrmPrivateKey::SignatureSize() const { + if (rsa_key_) { + return rsa_key_->SignatureSize(); + } + return ecc_key_->SignatureSize(); +} + +OEMCryptoResult DrmPrivateKey::GenerateRsaSignature( + const uint8_t* message, size_t message_length, uint8_t* signature, + size_t* signature_length) const { + if (!rsa_key_) { + LOGE("Only RSA DRM keys can generate PKCS1 signatures"); + return OEMCrypto_ERROR_INVALID_KEY; + } + return rsa_key_->GenerateSignature(message, message_length, kRsaPkcs1Cast, + signature, signature_length); +} + +std::vector DrmPrivateKey::GenerateRsaSignature( + const std::vector& message) const { + if (!rsa_key_) { + LOGE("Only RSA DRM keys can generate PKCS1 signatures"); + return std::vector(); + } + return rsa_key_->GenerateSignature(message, kRsaPkcs1Cast); +} +} // namespace util +} // namespace wvoec diff --git a/oemcrypto/util/src/oemcrypto_ecc_key.cpp b/oemcrypto/util/src/oemcrypto_ecc_key.cpp new file mode 100644 index 0000000..e226ed3 --- /dev/null +++ b/oemcrypto/util/src/oemcrypto_ecc_key.cpp @@ -0,0 +1,931 @@ +// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. +// +// Reference implementation utilities of OEMCrypto APIs +// +#include "oemcrypto_ecc_key.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "scoped_object.h" + +namespace wvoec { +namespace util { +namespace { +// Estimated max size (in bytes) of a serialized ECC key (public or +// private). These values are based on rough calculations for +// secp521r1 (largest of the supported curves) and should be slightly +// larger needed. +constexpr size_t kPrivateKeySize = 250; +constexpr size_t kPublicKeySize = 164; + +// 256 bit key, intended to be used with CMAC-AES-256. +constexpr size_t kEccSessionKeySize = 32; + +using ScopedBigNum = ScopedObject; +using ScopedBigNumCtx = ScopedObject; +using ScopedBio = ScopedObject; +using ScopedEcKey = ScopedObject; +using ScopedEvpMdCtx = ScopedObject; +using ScopedEvpPkey = ScopedObject; +using ScopedPrivateKeyInfo = + ScopedObject; +using ScopedSigPoint = ScopedObject; + +const EC_GROUP* GetEcGroup(EccCurve curve) { + // Creating a named EC_GROUP is an expensive operation, and they + // are always used in a manner which does not transfer ownership. + // Maintaining a process-wide set of supported EC groups reduces + // the overhead of group operations. + static std::mutex group_mutex; + static EC_GROUP* group_256 = nullptr; + static EC_GROUP* group_384 = nullptr; + static EC_GROUP* group_521 = nullptr; + std::lock_guard group_lock(group_mutex); + switch (curve) { + case kEccSecp256r1: { + if (group_256 == nullptr) { + LOGD("Creating secp256r1 group"); + // The curve secp256r1 was originally named prime256v1 + // in the X9.62 specification. + group_256 = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); + assert(group_256 != nullptr); + } + return group_256; + } + case kEccSecp384r1: { + if (group_384 == nullptr) { + LOGD("Creating secp384r1 group"); + group_384 = EC_GROUP_new_by_curve_name(NID_secp384r1); + assert(group_384 != nullptr); + } + return group_384; + } + case kEccSecp521r1: { + if (group_521 == nullptr) { + LOGD("Creating secp521r1 group"); + group_521 = EC_GROUP_new_by_curve_name(NID_secp521r1); + assert(group_521 != nullptr); + } + return group_521; + } + default: + LOGE("Cannot get EC group for unknown curve: curve = %d", + static_cast(curve)); + return nullptr; + } +} + +// Determines which of the supported ECC curves the provided |key| +// belongs to. +// +// This is intended to be used on keys that have been deserialized +// from an ASN.1 structure which may have contained a key which is +// supported by OpenSSL/BoringSSL but not necessarily by OEMCrypto. +// +// If the key group is unknown to OEMCrypto or if an error occurs, +// kEccCurveUnknown is returned. +EccCurve GetCurveFromKeyGroup(const EC_KEY* key) { + ScopedBigNumCtx ctx(BN_CTX_new()); + if (!ctx) { + LOGE("Failed to allocate BN ctx"); + return kEccCurveUnknown; + } + const EC_GROUP* group = EC_KEY_get0_group(key); + if (group == nullptr) { + LOGE("Provided key does not have a group"); + return kEccCurveUnknown; + } + int rc = EC_GROUP_cmp(group, GetEcGroup(kEccSecp256r1), ctx.get()); + if (rc == 0) { + return kEccSecp256r1; + } + if (rc == -1) { + LOGE("Error occurred while checking against secp256r1"); + return kEccCurveUnknown; + } + + rc = EC_GROUP_cmp(group, GetEcGroup(kEccSecp384r1), ctx.get()); + if (rc == 0) { + return kEccSecp384r1; + } + if (rc == -1) { + LOGE("Error occurred while checking against secp384r1"); + return kEccCurveUnknown; + } + + rc = EC_GROUP_cmp(group, GetEcGroup(kEccSecp521r1), ctx.get()); + if (rc == 0) { + return kEccSecp521r1; + } + if (rc == -1) { + LOGE("Error occurred while checking against secp521r1"); + return kEccCurveUnknown; + } + + LOGW("Unsupported curve group"); + return kEccCurveUnknown; +} + +// Compares the public EC points of both keys to see if they are the +// equal. +// Both |public_key| and |private_key| must be of the same group. +bool IsMatchingKeyPair(const EC_KEY* public_key, const EC_KEY* private_key) { + ScopedBigNumCtx ctx(BN_CTX_new()); + if (!ctx) { + LOGE("Failed to allocate BN ctx"); + return false; + } + // Returns: 1 if not equal, 0 if equal, -1 if error. + const int res = EC_POINT_cmp(EC_KEY_get0_group(public_key), + EC_KEY_get0_public_key(public_key), + EC_KEY_get0_public_key(private_key), ctx.get()); + if (res == -1) { + LOGE("Error occurred comparing keys"); + } + return res == 0; +} + +// Performs a SHA2 digest on the provided |message| and outputs the +// computed hash to |digest|. +// The digest algorithm used depends on which curve is used. +// - secp256r1 -> SHA-256 +// - secp384r1 -> SHA-384 +// - secp521r1 -> SHA-512 +// This function assumes that all parameters are valid. +// Returns true on success, false otherwise. +bool DigestMessage(EccCurve curve, const uint8_t* message, size_t message_size, + std::vector* digest) { + const EVP_MD* md_engine = nullptr; + switch (curve) { + case kEccSecp256r1: { + md_engine = EVP_sha256(); + break; + } + case kEccSecp384r1: { + md_engine = EVP_sha384(); + break; + } + case kEccSecp521r1: { + md_engine = EVP_sha512(); + break; + } + case kEccCurveUnknown: + // This case is to suppress compiler warnings. It will never + // occur. + break; + } + if (md_engine == nullptr) { + LOGE("Failed to get MD engine: curve = %d", static_cast(curve)); + return false; + } + + ScopedEvpMdCtx md_ctx(EVP_MD_CTX_new()); + if (!md_ctx) { + LOGE("Failed to create MD CTX"); + return false; + } + if (!EVP_DigestInit_ex(md_ctx.get(), md_engine, nullptr)) { + LOGE("Failed to init MD CTX"); + return false; + } + if (message_size > 0 && + !EVP_DigestUpdate(md_ctx.get(), message, message_size)) { + LOGE("Failed to update"); + return false; + } + digest->resize(EVP_MD_CTX_size(md_ctx.get()), 0); + const int res = EVP_DigestFinal_ex(md_ctx.get(), digest->data(), nullptr); + if (!res) { + LOGE("Failed to finalize"); + return false; + } + 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. +void* WidevineEccKdf(const void* secret, size_t secret_length, void* key, + size_t* key_size) { + if (*key_size < kEccSessionKeySize) { + LOGE("Output buffer is too small: required = %zu, size = %zu", + kEccSessionKeySize, *key_size); + return nullptr; + } + std::vector digest; + if (!DigestMessage(kEccSecp256r1 /* SHA-256 */, + reinterpret_cast(secret), secret_length, + &digest)) { + LOGE("Cannot derive key: Failed to hash secret"); + return nullptr; + } + if (digest.size() != kEccSessionKeySize) { + LOGE("Unexpected hash size: actual = %zu, expected = %zu", digest.size(), + kEccSessionKeySize); + return nullptr; + } + *key_size = kEccSessionKeySize; + memcpy(key, digest.data(), *key_size); + return key; +} + +void OpensslFreeU8(uint8_t* ptr) { OPENSSL_free(ptr); } + +// Internal ECC public key serialization. +OEMCryptoResult SerializeEccPublicKey(const EC_KEY* key, uint8_t* buffer, + size_t* buffer_size) { + if (buffer_size == nullptr) { + LOGE("Output buffer size is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (buffer == nullptr && *buffer_size > 0) { + LOGE("Output buffer is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + uint8_t* der_key_raw = nullptr; + const int der_res = i2d_EC_PUBKEY( + const_cast(key) /* Does not get modified */, &der_key_raw); + if (der_res < 0) { + LOGE("Public key serialization failed"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + ScopedObject der_key(der_key_raw); + der_key_raw = nullptr; + if (!der_key) { + LOGE("Encoded key is unexpectedly null"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (der_res == 0) { + LOGE("Unexpected DER encoded size"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const size_t required_size = static_cast(der_res); + if (buffer == nullptr || *buffer_size < required_size) { + *buffer_size = required_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + memcpy(buffer, der_key.get(), required_size); + *buffer_size = required_size; + return OEMCrypto_SUCCESS; +} + +std::vector SerializeEccPublicKey(const EC_KEY* key) { + size_t key_size = kPublicKeySize; + std::vector key_data(key_size, 0); + const OEMCryptoResult res = + SerializeEccPublicKey(key, key_data.data(), &key_size); + if (res != OEMCrypto_SUCCESS) { + LOGE("Failed to serialize public key: result = %d", static_cast(res)); + key_data.clear(); + } else { + key_data.resize(key_size); + } + return key_data; +} + +bool ParseEccPrivateKeyInfo(const uint8_t* buffer, size_t length, + ScopedEcKey* key, EccCurve* curve) { + if (length == 0) { + LOGE("Public key is too small: length = %zu", length); + return false; + } + ScopedBio bio(BIO_new_mem_buf(buffer, static_cast(length))); + if (!bio) { + LOGE("Failed to allocate BIO buffer"); + return false; + } + // Step 1: Deserializes PKCS8 PrivateKeyInfo containing an ECC key. + ScopedPrivateKeyInfo priv_info( + d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), nullptr)); + if (!priv_info) { + LOGE("Failed to parse private key"); + return false; + } + // Step 2: Convert to EC_KEY. + ScopedEvpPkey pkey(EVP_PKCS82PKEY(priv_info.get())); + if (!pkey) { + LOGE("Failed to convert PKCS8 to EVP"); + return false; + } + const int key_type = EVP_PKEY_base_id(pkey.get()); + if (key_type != EVP_PKEY_EC) { + LOGE("Decoded private key is not ECC"); + return false; + } + key->reset(EVP_PKEY_get1_EC_KEY(pkey.get())); + if (!*key) { + LOGE("Failed to get ECC key"); + return false; + } + // Step 3: Verify key parameters and curve family. + const int check = EC_KEY_check_key(key->get()); + if (check == 0) { + LOGE("ECC key parameters are invalid"); + return false; + } else if (check == -1) { + LOGE("Failed to check ECC key"); + return false; + } + *curve = GetCurveFromKeyGroup(key->get()); + if (*curve == kEccCurveUnknown) { + LOGE("Failed to determine key group"); + return false; + } + // Required flags for IETF compliance. + EC_KEY_set_asn1_flag(key->get(), OPENSSL_EC_NAMED_CURVE); + EC_KEY_set_conv_form(key->get(), POINT_CONVERSION_UNCOMPRESSED); + return true; +} +} // namespace + +std::string EccCurveToString(EccCurve curve) { + switch (curve) { + case kEccSecp256r1: + return "secp256r1"; + case kEccSecp384r1: + return "secp384r1"; + case kEccSecp521r1: + return "secp521r1"; + case kEccCurveUnknown: + return "Unknown"; + } + return "Unknown(" + std::to_string(static_cast(curve)) + ")"; +} + +// static +std::unique_ptr EccPublicKey::New( + const EccPrivateKey& private_key) { + std::unique_ptr key(new EccPublicKey()); + if (!key->InitFromPrivateKey(private_key)) { + LOGE("Failed to initialize public key from private key"); + key.reset(); + } + return key; +} + +// static +std::unique_ptr EccPublicKey::Load(const uint8_t* buffer, + size_t length) { + if (buffer == nullptr) { + LOGE("Provided public key buffer is null"); + return nullptr; + } + if (length == 0) { + LOGE("Provided public key buffer is zero length"); + return nullptr; + } + std::unique_ptr key(new EccPublicKey()); + if (!key->InitFromSubjectPublicKeyInfo(buffer, length)) { + LOGE("Failed to initialize public key from SubjectPublicKeyInfo"); + key.reset(); + } + return key; +} + +// static +std::unique_ptr EccPublicKey::Load(const std::string& buffer) { + if (buffer.empty()) { + LOGE("Provided public key buffer is empty"); + return std::unique_ptr(); + } + return Load(reinterpret_cast(buffer.data()), buffer.size()); +} + +// static +std::unique_ptr EccPublicKey::Load( + const std::vector& buffer) { + if (buffer.empty()) { + LOGE("Provided public key buffer is empty"); + return std::unique_ptr(); + } + return Load(buffer.data(), buffer.size()); +} + +// static +std::unique_ptr EccPublicKey::LoadPrivateKeyInfo( + const uint8_t* buffer, size_t length) { + if (buffer == nullptr) { + LOGE("Provided public key buffer is null"); + return nullptr; + } + if (length == 0) { + LOGE("Provided public key buffer is zero length"); + return nullptr; + } + std::unique_ptr key(new EccPublicKey()); + if (!key->InitFromPrivateKeyInfo(buffer, length)) { + LOGE("Failed to initialize public key from PrivateKeyInfo"); + key.reset(); + } + return key; +} + +// static +std::unique_ptr EccPublicKey::LoadPrivateKeyInfo( + const std::string& buffer) { + if (buffer.empty()) { + LOGE("Provided public key buffer is empty"); + return std::unique_ptr(); + } + return LoadPrivateKeyInfo(reinterpret_cast(buffer.data()), + buffer.size()); +} + +// static +std::unique_ptr EccPublicKey::LoadPrivateKeyInfo( + const std::vector& buffer) { + if (buffer.empty()) { + LOGE("Provided public key buffer is empty"); + return std::unique_ptr(); + } + return LoadPrivateKeyInfo(buffer.data(), buffer.size()); +} + +bool EccPublicKey::IsMatchingPrivateKey( + const EccPrivateKey& private_key) const { + if (private_key.curve() != curve_) { + return false; + } + return IsMatchingKeyPair(GetEcKey(), private_key.GetEcKey()); +} + +OEMCryptoResult EccPublicKey::Serialize(uint8_t* buffer, + size_t* buffer_size) const { + return SerializeEccPublicKey(key_, buffer, buffer_size); +} + +std::vector EccPublicKey::Serialize() const { + return SerializeEccPublicKey(key_); +} + +OEMCryptoResult EccPublicKey::VerifySignature(const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length) const { + if (signature == nullptr || signature_length == 0) { + LOGE("Signature is missing"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (message == nullptr && message_length > 0) { + LOGE("Bad message data"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + // Step 1: Parse signature. + const uint8_t* tp = signature; + ScopedSigPoint sig_point(d2i_ECDSA_SIG(nullptr, &tp, signature_length)); + if (!sig_point) { + LOGE("Failed to parse signature"); + // Most likely an invalid signature than an OpenSSL error. + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + // Step 2: Hash message + std::vector digest; + if (!DigestMessage(curve_, message, message_length, &digest)) { + LOGE("Failed to digest message"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 3: Verify signature + const int res = ECDSA_do_verify( + digest.data(), static_cast(digest.size()), sig_point.get(), key_); + if (res == -1) { + LOGE("Error occurred checking signature"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (res == 0) { + LOGD("Signature did not match"); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult EccPublicKey::VerifySignature( + const std::string& message, const std::string& signature) const { + if (signature.empty()) { + LOGE("Signature should not be empty"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + return VerifySignature( + reinterpret_cast(message.data()), message.size(), + reinterpret_cast(signature.data()), signature.size()); +} + +OEMCryptoResult EccPublicKey::VerifySignature( + const std::vector& message, + const std::vector& signature) const { + if (signature.empty()) { + LOGE("Signature should not be empty"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + return VerifySignature(message.data(), message.size(), signature.data(), + signature.size()); +} + +EccPublicKey::~EccPublicKey() { + if (key_ != nullptr) { + EC_KEY_free(key_); + key_ = nullptr; + } + curve_ = kEccCurveUnknown; +} + +bool EccPublicKey::InitFromSubjectPublicKeyInfo(const uint8_t* buffer, + size_t length) { + // Deserialize SubjectPublicKeyInfo + const uint8_t* tp = buffer; + ScopedEcKey key(d2i_EC_PUBKEY(nullptr, &tp, length)); + if (!key) { + LOGE("Failed to parse ECC key"); + return false; + } + // Verify key parameters and curve family. + const int check = EC_KEY_check_key(key.get()); + if (check == 0) { + LOGE("ECC key parameters are invalid"); + return false; + } else if (check == -1) { + LOGE("Failed to check ECC key"); + return false; + } + curve_ = GetCurveFromKeyGroup(key.get()); + if (curve_ == kEccCurveUnknown) { + LOGE("Failed to determine key group"); + return false; + } + // Required flags for IETF compliance. + EC_KEY_set_asn1_flag(key.get(), OPENSSL_EC_NAMED_CURVE); + EC_KEY_set_conv_form(key.get(), POINT_CONVERSION_UNCOMPRESSED); + key_ = key.release(); + return true; +} + +bool EccPublicKey::InitFromPrivateKeyInfo(const uint8_t* buffer, + size_t length) { + ScopedEcKey private_key; + if (!ParseEccPrivateKeyInfo(buffer, length, &private_key, &curve_)) { + return false; + } + // TODO(sigquit): Strip private information. + key_ = private_key.release(); + return true; +} + +bool EccPublicKey::InitFromPrivateKey(const EccPrivateKey& private_key) { + ScopedEcKey key(EC_KEY_new()); + if (!key) { + LOGE("Failed to allocate key"); + return false; + } + if (!EC_KEY_set_group(key.get(), EC_KEY_get0_group(private_key.GetEcKey()))) { + LOGE("Failed to set group"); + return false; + } + if (!EC_KEY_set_public_key(key.get(), + EC_KEY_get0_public_key(private_key.GetEcKey()))) { + LOGE("Failed to set public point"); + return false; + } + curve_ = private_key.curve(); + // Required flags for IETF compliance. + EC_KEY_set_asn1_flag(key.get(), OPENSSL_EC_NAMED_CURVE); + EC_KEY_set_conv_form(key.get(), POINT_CONVERSION_UNCOMPRESSED); + key_ = key.release(); + return true; +} + +// static +std::unique_ptr EccPrivateKey::New(EccCurve curve) { + std::unique_ptr key(new EccPrivateKey()); + if (!key->InitFromCurve(curve)) { + LOGE("Failed to initialize private key from curve"); + key.reset(); + } + return key; +} + +// static +std::unique_ptr EccPrivateKey::Load(const uint8_t* buffer, + size_t length) { + if (buffer == nullptr) { + LOGE("Provided private key buffer is null"); + return nullptr; + } + if (length == 0) { + LOGE("Provided private key buffer is zero length"); + return nullptr; + } + std::unique_ptr key(new EccPrivateKey()); + if (!key->InitFromPrivateKeyInfo(buffer, length)) { + LOGE("Failed to initialize private key from PrivateKeyInfo"); + key.reset(); + } + return key; +} + +// static +std::unique_ptr EccPrivateKey::Load(const std::string& buffer) { + if (buffer.empty()) { + LOGE("Provided private key buffer is empty"); + return std::unique_ptr(); + } + return Load(reinterpret_cast(buffer.data()), buffer.size()); +} + +// static +std::unique_ptr EccPrivateKey::Load( + const std::vector& buffer) { + if (buffer.empty()) { + LOGE("Provided private key buffer is empty"); + return std::unique_ptr(); + } + return Load(buffer.data(), buffer.size()); +} + +std::unique_ptr EccPrivateKey::MakePublicKey() const { + return EccPublicKey::New(*this); +} + +bool EccPrivateKey::IsMatchingPublicKey(const EccPublicKey& public_key) const { + if (public_key.curve() != curve_) { + return false; + } + return IsMatchingKeyPair(public_key.GetEcKey(), GetEcKey()); +} + +OEMCryptoResult EccPrivateKey::Serialize(uint8_t* buffer, + size_t* buffer_size) const { + if (buffer_size == nullptr) { + LOGE("Output buffer size is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (buffer == nullptr && *buffer_size > 0) { + LOGE("Output buffer is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + // Step 1: Convert EC_KEY key to EVP. + ScopedEvpPkey pkey(EVP_PKEY_new()); + if (!pkey) { + LOGE("Failed to allocate EVP"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!EVP_PKEY_set1_EC_KEY(pkey.get(), key_)) { + LOGE("Failed to set EVP ECC key"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 2: Convert ECC EVP to PKCS8 format. + ScopedPrivateKeyInfo priv_info(EVP_PKEY2PKCS8(pkey.get())); + if (!priv_info) { + LOGE("Failed to convert ECC key to PKCS8 info"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 3: Serialize PKCS8 to DER encoding. + ScopedBio bio(BIO_new(BIO_s_mem())); + if (!bio) { + LOGE("Failed to allocate IO buffer for ECC key"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!i2d_PKCS8_PRIV_KEY_INFO_bio(bio.get(), priv_info.get())) { + LOGE("Failed to serialize ECC key"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 4: Determine key size and copy. + char* key_ptr = nullptr; + const long key_size = BIO_get_mem_data(bio.get(), &key_ptr); + if (key_size < 0) { + LOGE("Failed to get ECC key size"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (key_ptr == nullptr) { + LOGE("Encoded key is unexpectedly null"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const size_t required_size = static_cast(key_size); + if (*buffer_size < required_size) { + *buffer_size = required_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *buffer_size = required_size; + memcpy(buffer, key_ptr, required_size); + return OEMCrypto_SUCCESS; +} + +std::vector EccPrivateKey::Serialize() const { + size_t key_size = kPrivateKeySize; + std::vector key_data(key_size, 0); + const OEMCryptoResult res = Serialize(key_data.data(), &key_size); + if (res != OEMCrypto_SUCCESS) { + LOGE("Failed to serialize private key: result = %d", static_cast(res)); + key_data.clear(); + } else { + key_data.resize(key_size); + } + return key_data; +} + +OEMCryptoResult EccPrivateKey::SerializeAsPublicKey(uint8_t* buffer, + size_t* buffer_size) const { + return SerializeEccPublicKey(key_, buffer, buffer_size); +} + +std::vector EccPrivateKey::SerializeAsPublicKey() const { + return SerializeEccPublicKey(key_); +} + +OEMCryptoResult EccPrivateKey::GenerateSignature( + const uint8_t* message, size_t message_length, uint8_t* signature, + size_t* signature_length) const { + if (signature_length == nullptr) { + LOGE("Output signature size is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (signature == nullptr && *signature_length > 0) { + LOGE("Output signature is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (message == nullptr && message_length > 0) { + LOGE("Invalid message data"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + const size_t expected_signature_length = ECDSA_size(key_); + if (*signature_length < expected_signature_length) { + *signature_length = expected_signature_length; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + // Step 1: Hash message. + std::vector digest; + if (!DigestMessage(curve_, message, message_length, &digest)) { + LOGE("Failed to digest message"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 2: Generate signature point. + ScopedSigPoint sig_point( + ECDSA_do_sign(digest.data(), static_cast(digest.size()), key_)); + if (!sig_point) { + LOGE("Failed to perform ECDSA"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 3: Serialize + std::vector temp(expected_signature_length); + uint8_t* sig_ptr = temp.data(); + const int res = i2d_ECDSA_SIG(sig_point.get(), &sig_ptr); + if (res <= 0) { + LOGE("Failed to serialize signature"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const size_t required_size = static_cast(res); + if (signature == nullptr || *signature_length < required_size) { + *signature_length = required_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + memcpy(signature, temp.data(), required_size); + *signature_length = required_size; + return OEMCrypto_SUCCESS; +} + +std::vector EccPrivateKey::GenerateSignature( + const std::string& message) const { + size_t signature_size = SignatureSize(); + std::vector signature(signature_size, 0); + const OEMCryptoResult res = + GenerateSignature(reinterpret_cast(message.data()), + message.size(), signature.data(), &signature_size); + if (res != OEMCrypto_SUCCESS) { + LOGE("Failed to generate signature: result = %d", static_cast(res)); + signature.clear(); + } else { + signature.resize(signature_size); + } + return signature; +} + +std::vector EccPrivateKey::GenerateSignature( + const std::vector& message) const { + size_t signature_size = SignatureSize(); + std::vector signature(signature_size, 0); + const OEMCryptoResult res = GenerateSignature( + message.data(), message.size(), signature.data(), &signature_size); + if (res != OEMCrypto_SUCCESS) { + LOGE("Failed to generate signature: result = %d", static_cast(res)); + signature.clear(); + } else { + signature.resize(signature_size); + } + return signature; +} + +size_t EccPrivateKey::SignatureSize() const { return ECDSA_size(key_); } + +OEMCryptoResult EccPrivateKey::DeriveSessionKey( + const EccPublicKey& public_key, uint8_t* session_key, + size_t* session_key_size) const { + if (public_key.curve() != curve_) { + LOGE("Incompatible ECC keys: public = %s, private = %s", + EccCurveToString(public_key.curve()).c_str(), + EccCurveToString(curve_).c_str()); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (session_key_size == nullptr) { + LOGE("Output session key size buffer is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (session_key == nullptr && *session_key_size > 0) { + LOGE("Output session key buffer is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (*session_key_size < kEccSessionKeySize) { + *session_key_size = kEccSessionKeySize; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + const int res = ECDH_compute_key( + session_key, kEccSessionKeySize, + EC_KEY_get0_public_key(public_key.GetEcKey()), key_, WidevineEccKdf); + if (res < 0) { + LOGE("ECDH error occurred"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (static_cast(res) != kEccSessionKeySize) { + LOGE("Unexpected key size: size = %d", res); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + *session_key_size = kEccSessionKeySize; + return OEMCrypto_SUCCESS; +} + +std::vector EccPrivateKey::DeriveSessionKey( + const EccPublicKey& public_key) const { + size_t session_key_size = kEccSessionKeySize; + std::vector session_key(session_key_size, 0); + const OEMCryptoResult res = + DeriveSessionKey(public_key, session_key.data(), &session_key_size); + if (res != OEMCrypto_SUCCESS) { + LOGE("Failed to derive session key: result = %d", static_cast(res)); + session_key.clear(); + } else { + session_key.resize(session_key_size); + } + return session_key; +} + +size_t EccPrivateKey::SessionKeyLength() const { return kEccSessionKeySize; } + +EccPrivateKey::~EccPrivateKey() { + if (key_ != nullptr) { + EC_KEY_free(key_); + key_ = nullptr; + } + curve_ = kEccCurveUnknown; +} + +bool EccPrivateKey::InitFromPrivateKeyInfo(const uint8_t* buffer, + size_t length) { + ScopedEcKey key; + if (!ParseEccPrivateKeyInfo(buffer, length, &key, &curve_)) return false; + key_ = key.release(); + return true; +} + +bool EccPrivateKey::InitFromCurve(EccCurve curve) { + const EC_GROUP* group = GetEcGroup(curve); + if (group == nullptr) { + LOGE("Failed to get ECC group"); + return false; + } + ScopedEcKey key(EC_KEY_new()); + if (!key) { + LOGE("Failed to allocate key"); + return false; + } + if (!EC_KEY_set_group(key.get(), group)) { + LOGE("Failed to set group"); + return false; + } + // Generate random key. + if (!EC_KEY_generate_key(key.get())) { + LOGE("Failed to generate random key"); + return false; + } + curve_ = curve; + // Required flags for IETF compliance. + EC_KEY_set_asn1_flag(key.get(), OPENSSL_EC_NAMED_CURVE); + EC_KEY_set_conv_form(key.get(), POINT_CONVERSION_UNCOMPRESSED); + key_ = key.release(); + return true; +} +} // namespace util +} // namespace wvoec diff --git a/oemcrypto/util/src/oemcrypto_key_deriver.cpp b/oemcrypto/util/src/oemcrypto_key_deriver.cpp new file mode 100644 index 0000000..2d65a29 --- /dev/null +++ b/oemcrypto/util/src/oemcrypto_key_deriver.cpp @@ -0,0 +1,154 @@ +// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. +// +// Reference implementation utilities of OEMCrypto APIs +// +#include "oemcrypto_key_deriver.h" + +#include "log.h" + +namespace wvoec { +namespace util { +namespace { +bool Derive128KeyAppend(Cmac* cmac, uint8_t counter, const uint8_t* ctx, + size_t ctx_size, std::vector* derived_key) { + cmac->Reset(); + if (!cmac->Update(counter)) { + return false; + } + if (!cmac->Update(ctx, ctx_size)) { + return false; + } + if (!cmac->FinalizeAppend(derived_key)) { + return false; + } + return true; +} + +bool Derive128Key(Cmac* cmac, uint8_t counter, const uint8_t* ctx, + size_t ctx_size, std::vector* derived_key) { + derived_key->clear(); + return Derive128KeyAppend(cmac, counter, ctx, ctx_size, derived_key); +} + +bool Derive256Key(Cmac* cmac, uint8_t counter_base, const uint8_t* ctx, + size_t ctx_size, std::vector* derived_key) { + derived_key->clear(); + if (!Derive128KeyAppend(cmac, counter_base, ctx, ctx_size, derived_key)) { + return false; + } + return Derive128KeyAppend(cmac, counter_base + 1, ctx, ctx_size, derived_key); +} +} // namespace + +// static +std::unique_ptr KeyDeriver::Create(const uint8_t* key, + size_t key_size) { + if (key == nullptr) { + LOGE("Key deriver key is null"); + return std::unique_ptr(); + } + std::unique_ptr key_deriver(new KeyDeriver()); + if (!key_deriver->Init(key, key_size)) { + key_deriver.reset(); + } + return key_deriver; +} + +// static +std::unique_ptr KeyDeriver::Create( + const std::vector& key) { + if (key.empty()) { + LOGE("Key deriver key is empty"); + return std::unique_ptr(); + } + return Create(key.data(), key.size()); +} + +bool KeyDeriver::Init(const uint8_t* key, size_t key_size) { + cmac_ = Cmac::Create(key, key_size); + if (!cmac_) { + LOGE("Failed to create CMAC for key deriver"); + return false; + } + return true; +} + +bool KeyDeriver::DeriveServerMacKey(const uint8_t* mac_key_context, + size_t mac_key_context_size, + std::vector* mac_key_server) { + if (mac_key_context == nullptr) { + LOGE("Server MAC key context is null"); + return false; + } + if (mac_key_server == nullptr) { + LOGE("Output server MAC key buffer is null"); + return false; + } + return Derive256Key(cmac_.get(), 0x01, mac_key_context, mac_key_context_size, + mac_key_server); +} + +bool KeyDeriver::DeriveServerMacKey(const std::vector& mac_key_context, + std::vector* mac_key_server) { + if (mac_key_context.empty()) { + LOGE("Server MAC key context is empty"); + return false; + } + return DeriveServerMacKey(mac_key_context.data(), mac_key_context.size(), + mac_key_server); +} + +bool KeyDeriver::DeriveClientMacKey(const uint8_t* mac_key_context, + size_t mac_key_context_size, + std::vector* mac_key_client) { + if (mac_key_context == nullptr) { + LOGE("Client MAC key context is null"); + return false; + } + if (mac_key_client == nullptr) { + LOGE("Output client MAC key buffer is null"); + return false; + } + return Derive256Key(cmac_.get(), 0x03, mac_key_context, mac_key_context_size, + mac_key_client); +} + +bool KeyDeriver::DeriveClientMacKey(const std::vector& mac_key_context, + std::vector* mac_key_client) { + if (mac_key_context.empty()) { + LOGE("Client MAC key context is empty"); + return false; + } + return DeriveClientMacKey(mac_key_context.data(), mac_key_context.size(), + mac_key_client); +} + +bool KeyDeriver::DeriveEncryptionKey(const uint8_t* enc_key_context, + size_t enc_key_context_size, + std::vector* enc_key) { + if (enc_key_context == nullptr) { + LOGE("Encryption key context is null"); + return false; + } + if (enc_key == nullptr) { + LOGE("Output encryption key buffer is null"); + return false; + } + return Derive128Key(cmac_.get(), 0x01, enc_key_context, enc_key_context_size, + enc_key); +} + +bool KeyDeriver::DeriveEncryptionKey( + const std::vector& enc_key_context, + std::vector* enc_key) { + if (enc_key_context.empty()) { + LOGE("Encryption key context is empty"); + return false; + } + return DeriveEncryptionKey(enc_key_context.data(), enc_key_context.size(), + enc_key); +} +} // namespace util +} // namespace wvoec diff --git a/oemcrypto/util/src/oemcrypto_oem_cert.cpp b/oemcrypto/util/src/oemcrypto_oem_cert.cpp new file mode 100644 index 0000000..1741d44 --- /dev/null +++ b/oemcrypto/util/src/oemcrypto_oem_cert.cpp @@ -0,0 +1,234 @@ +// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. +// +// Reference implementation utilities of OEMCrypto APIs +// +#include "oemcrypto_oem_cert.h" + +#include + +#include +#include +#include + +#include "log.h" +#include "oemcrypto_rsa_key.h" +#include "scoped_object.h" + +namespace wvoec { +namespace util { +namespace { +using ScopedCertificate = ScopedObject; +using ScopedEvpKey = ScopedObject; +using ScopedPkcs7 = ScopedObject; + +constexpr size_t kExpectedCertCount = 2; // Leaf and intermediate. +constexpr int kDeviceCertIndex = 0; + +// Checks that the |public_key| from an X.509 certificate is the +// correct public key of the serialized |private_key_data|. +OEMCryptoResult VerifyRsaKey(const RSA* public_key, + const std::vector& private_key_data) { + if (public_key == nullptr) { + LOGE("RSA key is null"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + std::unique_ptr private_key = + RsaPrivateKey::Load(private_key_data); + if (!private_key) { + LOGE("Failed to parse provided RSA private key"); + return OEMCrypto_ERROR_INVALID_KEY; + } + if (!RsaKeysAreMatchingPair(public_key, private_key->GetRsaKey())) { + LOGE("OEM certificate keys do not match"); + return OEMCrypto_ERROR_INVALID_KEY; + } + return OEMCrypto_SUCCESS; +} +} // namespace + +// This utility class encapsulates the minimum functionality of an +// OEM Public Certificate required to verify a device's OEM Public +// Certificate. +class OemPublicCertificate { + public: + // Loads a PKCS #7 signedData message with certificate chain. + // Minimum validation is performed. Only checks that the + // device's public key is of a known type (RSA). + static std::unique_ptr Load(const uint8_t* public_cert, + size_t public_cert_size) { + std::unique_ptr oem_public_cert; + if (public_cert == nullptr) { + LOGE("Public cert buffer is null"); + return oem_public_cert; + } + if (public_cert_size == 0) { + LOGE("Public cert buffer is empty"); + return oem_public_cert; + } + oem_public_cert.reset(new OemPublicCertificate()); + if (!oem_public_cert->InitFromBuffer(public_cert, public_cert_size)) { + oem_public_cert.reset(); + } + return oem_public_cert; + } + + OemCertificate::KeyType key_type() const { return key_type_; } + const std::vector& cert_data() const { return cert_data_; } + + const RSA* GetPublicRsaKey() const { + return EVP_PKEY_get0_RSA(device_public_key_.get()); + } + + ~OemPublicCertificate() = default; + + OemPublicCertificate(const OemPublicCertificate&) = delete; + OemPublicCertificate(OemPublicCertificate&&) = delete; + const OemPublicCertificate& operator=(const OemPublicCertificate&) = delete; + OemPublicCertificate& operator=(OemPublicCertificate&&) = delete; + + private: + OemPublicCertificate() {} + + bool InitFromBuffer(const uint8_t* public_cert, size_t public_cert_size) { + // Step 1: Parse the PKCS7 certificate chain as signedData. + const uint8_t* public_cert_ptr = public_cert; + pkcs7_.reset(d2i_PKCS7(nullptr, &public_cert_ptr, public_cert_size)); + if (!pkcs7_) { + LOGE("Failed to parse PKCS#7 certificate chain"); + return false; + } + if (!PKCS7_type_is_signed(pkcs7_.get())) { + LOGE("OEM Public Certificate is not PKCS#7 signed data"); + return false; + } + PKCS7_SIGNED* signed_data = pkcs7_->d.sign; + // Step 2: Get the leaf certificate. + const size_t cert_count = + static_cast(sk_X509_num(signed_data->cert)); + if (cert_count != kExpectedCertCount) { + LOGE("Unexpected number of certificates: expected = %zu, actual = %zu", + kExpectedCertCount, cert_count); + return false; + } + X509* leaf_cert = sk_X509_value(signed_data->cert, kDeviceCertIndex); + // Step 3a: Get the device's public key. + device_public_key_.reset(X509_get_pubkey(leaf_cert)); + if (!device_public_key_) { + LOGE("Device X.509 certificate is missing a public key"); + return false; + } + // Step 3b: Check key type. + if (EVP_PKEY_get0_RSA(device_public_key_.get()) == nullptr) { + LOGE("Device public key is not RSA"); + return false; + } + key_type_ = OemCertificate::kRsa; + cert_data_.assign(public_cert, public_cert + public_cert_size); + return true; + } + + OemCertificate::KeyType key_type_ = OemCertificate::kNone; + // OpenSSL/BoringSSL's implementation of PKCS7 objects. + ScopedPkcs7 pkcs7_; + ScopedEvpKey device_public_key_; + std::vector cert_data_; +}; + +// ===== ===== ===== OEM Certificate ===== ===== ===== + +// static +std::unique_ptr OemCertificate::Create( + const uint8_t* private_key_data, size_t private_key_size, + const uint8_t* public_cert_data, size_t public_cert_size) { + std::unique_ptr oem_cert; + // Step 1: Verify public cert is well-formed. + std::unique_ptr oem_public_cert = + OemPublicCertificate::Load(public_cert_data, public_cert_size); + if (!oem_public_cert) { + LOGE("Invalid OEM Public Certificate"); + return oem_cert; + } + // Step 2: Verify private key is well-formed. + switch (oem_public_cert->key_type()) { + case kRsa: { + std::unique_ptr oem_private_key = + RsaPrivateKey::Load(private_key_data, private_key_size); + if (!oem_private_key) { + LOGE("Invalid OEM Private Key"); + return oem_cert; + } + } break; + case kNone: // Suppress compiler warnings. + return oem_cert; + } + // Step 3: Copy over data. + oem_cert.reset(new OemCertificate()); + oem_cert->private_key_.assign(private_key_data, + private_key_data + private_key_size); + oem_cert->public_cert_ = std::move(oem_public_cert); + return oem_cert; +} + +// static +std::unique_ptr OemCertificate::Create( + const std::vector& private_key, + const std::vector& public_cert) { + if (private_key.empty()) { + LOGE("Private key buffer is empty"); + return std::unique_ptr(); + } + if (public_cert.empty()) { + LOGE("Public cert buffer is empty"); + return std::unique_ptr(); + } + return Create(private_key.data(), private_key.size(), public_cert.data(), + public_cert.size()); +} + +OemCertificate::KeyType OemCertificate::key_type() const { + return public_cert_->key_type(); +} + +OEMCryptoResult OemCertificate::GetPublicCertificate( + uint8_t* public_cert, size_t* public_cert_length) const { + if (public_cert_length == nullptr) { + LOGE("Output |public_cert_length| is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (public_cert == nullptr && *public_cert_length > 0) { + LOGE("Output |public_cert| is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + const std::vector& cert_data = public_cert_->cert_data(); + if (*public_cert_length < cert_data.size()) { + *public_cert_length = cert_data.size(); + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *public_cert_length = cert_data.size(); + memcpy(public_cert, cert_data.data(), cert_data.size()); + return OEMCrypto_SUCCESS; +} + +const std::vector& OemCertificate::GetPublicCertificate() const { + return public_cert_->cert_data(); +} + +OEMCryptoResult OemCertificate::IsCertificateValid() const { + switch (key_type()) { + case kRsa: + return VerifyRsaKey(public_cert_->GetPublicRsaKey(), private_key_); + case kNone: // Suppress compiler warnings. + break; + } + LOGE("Unexpected error key type: type = %d", static_cast(key_type())); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +// Constructor and destructor do not perform anything special, but +// must be declared within a scope which defines OemPublicCertificate. +OemCertificate::OemCertificate() {} +OemCertificate::~OemCertificate() {} +} // namespace util +} // namespace wvoec diff --git a/oemcrypto/util/src/oemcrypto_rsa_key.cpp b/oemcrypto/util/src/oemcrypto_rsa_key.cpp new file mode 100644 index 0000000..f77b2c1 --- /dev/null +++ b/oemcrypto/util/src/oemcrypto_rsa_key.cpp @@ -0,0 +1,1287 @@ +// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. +// +// Reference implementation utilities of OEMCrypto APIs +// +#include "oemcrypto_rsa_key.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "OEMCryptoCENC.h" +#include "log.h" +#include "oemcrypto_types.h" +#include "scoped_object.h" + +namespace wvoec { +namespace util { +namespace { +// Estimated size of an RSA private key. +// The private key constists of: +// n : Modulos : byteSize(n) ~= bitSize(n)/8 +// e : Public exponent : 4 bytes +// d : Private exponent : ~byteSize(n) +// p : Prime 1 : ~byteSize(n)/2 +// q : Prime 2 : ~byteSize(n)/2 +// d (mod p-1) : Exponent 1 : ~byteSize(n)/2 +// d (mod q-1) : Exponent 2 : ~byteSize(n)/2 +// q^-1 (mod p) : Coefficient : ~byteSize(n)/2 +// And the ASN.1 tags for each component (roughly 25 bytes). +constexpr size_t kPrivateKeySize = (3072 / 8) * 5 + 25; +// Estimated size of an RSA public key. +// The public key constists of: +// The private key constists of: +// n : Modulos : byteSize(n) ~= bitSize(n)/8 +// e : Public exponent : 4 bytes +// And the ASN.1 tags + outer structure. +constexpr size_t kPublicKeySize = (3072 / 8) + 100; + +// 128 bit key, intended to be used with CMAC-AES-128. +constexpr size_t kRsaSessionKeySize = 16; +// Encryption key used by OEMCrypto session for encrypting and +// decrypting data. +constexpr size_t kEncryptionKeySize = wvoec::KEY_SIZE; + +// Salt length used by OEMCrypto's RSASSA-PSS implementation. +// See description of kRsaPssDefault for more information. +constexpr size_t kPssSaltLength = 20; +// Requirement of CAST receivers. +constexpr size_t kRsaPkcs1CastMaxMessageSize = 83; + +using ScopedBigNum = ScopedObject; +using ScopedBio = ScopedObject; +using ScopedEvpMdCtx = ScopedObject; +using ScopedEvpPkey = ScopedObject; +using ScopedPrivateKeyInfo = + ScopedObject; +using ScopedRsaKey = ScopedObject; + +// Estimates the RSA rough field size from the real bit size of the +// RSA modulos. The actual bit length could vary by a few bits. +RsaFieldSize RealBitSizeToFieldSize(int bits) { + if (bits > 1800 && bits < 2200) { + return kRsa2048Bit; + } + if (bits > 2800 && bits < 3200) { + return kRsa3072Bit; + } + return kRsaFieldUnknown; +} + +bool IsValidAllowedSchemes(uint32_t allowed_schemes) { + static constexpr uint32_t kAllSchemesMask = + kSign_RSASSA_PSS | kSign_PKCS1_Block1; + return (allowed_schemes & kAllSchemesMask) != 0; +} + +bool ParseRsaPrivateKeyInfo(const uint8_t* buffer, size_t length, + ScopedRsaKey* key, uint32_t* allowed_schemes, + bool* explicit_schemes, RsaFieldSize* field_size) { + if (length < 8) { + LOGE("Public key is too small: length = %zu", length); + return false; + } + ScopedBio bio; + // Check allowed scheme type. + if (!memcmp("SIGN", buffer, 4)) { + uint32_t allowed_schemes_bno; + memcpy(&allowed_schemes_bno, reinterpret_cast(&buffer[4]), + 4); + *allowed_schemes = ntohl(allowed_schemes_bno); + if (!IsValidAllowedSchemes(*allowed_schemes)) { + LOGE("Invalid allowed schemes value: allowed_schemes = %08x", + *allowed_schemes); + return false; + } + bio.reset(BIO_new_mem_buf(&buffer[8], static_cast(length - 8))); + *explicit_schemes = true; + } else { + *allowed_schemes = kSign_RSASSA_PSS; + bio.reset(BIO_new_mem_buf(buffer, static_cast(length))); + *explicit_schemes = false; + } + if (!bio) { + LOGE("Failed to allocate BIO buffer"); + return false; + } + // Step 1: Deserializes PKCS8 PrivateKeyInfo containing an RSA key. + ScopedPrivateKeyInfo priv_info( + d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), nullptr)); + if (!priv_info) { + LOGE("Failed to parse private key"); + return false; + } + // Step 2: Convert to RSA key. + ScopedEvpPkey pkey(EVP_PKCS82PKEY(priv_info.get())); + if (!pkey) { + LOGE("Failed to convert PKCS8 to EVP"); + return false; + } + const int key_type = EVP_PKEY_base_id(pkey.get()); + if (key_type != EVP_PKEY_RSA) { + LOGE("Decoded private key is not RSA"); + return false; + } + key->reset(EVP_PKEY_get1_RSA(pkey.get())); + if (!*key) { + LOGE("Failed to get RSA key"); + return false; + } + // Step 3: Verify key parameters and field width. + const int check = RSA_check_key(key->get()); + if (check == 0) { + LOGE("RSA key parameters are invalid"); + return false; + } else if (check == -1) { + LOGE("Failed to check RSA key"); + return false; + } + const int bits = RSA_bits(key->get()); + *field_size = RealBitSizeToFieldSize(bits); + if (*field_size == kRsaFieldUnknown) { + LOGE("Unsupported RSA key size: bits = %d", bits); + return false; + } + return true; +} + +void OpensslFreeU8(uint8_t* ptr) { OPENSSL_free(ptr); } +} // namespace + +std::string RsaFieldSizeToString(RsaFieldSize field_size) { + switch (field_size) { + case kRsa2048Bit: + return "RSA-2048"; + case kRsa3072Bit: + return "RSA-3072"; + case kRsaFieldUnknown: + return "Unknown"; + } + return "Unknown(" + std::to_string(static_cast(field_size)) + ")"; +} + +bool RsaKeysAreMatchingPair(const RSA* public_key, const RSA* private_key) { + if (public_key == nullptr) { + LOGE("Public key is null"); + return false; + } + if (private_key == nullptr) { + LOGE("Private key is null"); + return false; + } + // Step 1: Extract public key components. + const BIGNUM* public_n = nullptr; + const BIGNUM* public_e = nullptr; + const BIGNUM* d = nullptr; + RSA_get0_key(public_key, &public_n, &public_e, &d); + if (public_n == nullptr || public_e == nullptr) { + LOGE("Failed to get RSA public key components"); + return false; + } + // Step 2: Extract private key components. + const BIGNUM* private_n = nullptr; + const BIGNUM* private_e = nullptr; + RSA_get0_key(private_key, &private_n, &private_e, &d); + if (private_n == nullptr || private_e == nullptr) { + LOGE("Failed to get RSA private key components"); + return false; + } + // Step 3: Compare RSA components. + if (BN_cmp(public_n, private_n)) { + LOGD("RSA modulos do not match"); + return false; + } + if (BN_cmp(public_e, private_e)) { + LOGD("RSA exponents do not match"); + return false; + } + return true; +} + +// ===== ===== ===== RSA Public Key ===== ===== ===== + +// static +std::unique_ptr RsaPublicKey::New( + const RsaPrivateKey& private_key) { + std::unique_ptr key(new RsaPublicKey()); + if (!key->InitFromPrivateKey(private_key)) { + LOGE("Failed to initialize public key from private key"); + key.reset(); + } + return key; +} + +// static +std::unique_ptr RsaPublicKey::FromSslHandle( + const RSA* rsa_handle, uint32_t allowed_schemes) { + if (rsa_handle == nullptr) { + LOGE("Provided OpenSSL/BoringSSL RSA key is null"); + return nullptr; + } + if (!IsValidAllowedSchemes(allowed_schemes)) { + LOGE("Invalid |allowed_schemes| value: allowed_schemes = %08x", + allowed_schemes); + return nullptr; + } + std::unique_ptr key(new RsaPublicKey()); + if (!key->InitFromSslHandle(rsa_handle, allowed_schemes)) { + LOGE("Failed to initialize public key from OpenSSL/BoringSSL RSA handle"); + key.reset(); + } + return key; +} + +// static +std::unique_ptr RsaPublicKey::Load(const uint8_t* buffer, + size_t length) { + if (buffer == nullptr) { + LOGE("Provided public key buffer is null"); + return nullptr; + } + if (length == 0) { + LOGE("Provided public key buffer is zero length"); + return nullptr; + } + std::unique_ptr key(new RsaPublicKey()); + if (!key->InitFromSubjectPublicKeyInfo(buffer, length)) { + LOGE("Failed to initialize public key from SubjectPublicKeyInfo"); + key.reset(); + } + return key; +} + +// static +std::unique_ptr RsaPublicKey::Load(const std::string& buffer) { + if (buffer.empty()) { + LOGE("Provided public key buffer is empty"); + return nullptr; + } + return Load(reinterpret_cast(buffer.data()), buffer.size()); +} + +// static +std::unique_ptr RsaPublicKey::Load( + const std::vector& buffer) { + if (buffer.empty()) { + LOGE("Provided public key buffer is empty"); + return nullptr; + } + return Load(buffer.data(), buffer.size()); +} + +// static +std::unique_ptr RsaPublicKey::LoadPrivateKeyInfo( + const uint8_t* buffer, size_t length) { + if (buffer == nullptr) { + LOGE("Provided public key buffer is null"); + return nullptr; + } + if (length == 0) { + LOGE("Provided public key buffer is zero length"); + return nullptr; + } + std::unique_ptr key(new RsaPublicKey()); + if (!key->InitFromPrivateKeyInfo(buffer, length)) { + LOGE("Failed to initialize public key from PrivateKeyInfo"); + key.reset(); + } + return key; +} + +// static +std::unique_ptr RsaPublicKey::LoadPrivateKeyInfo( + const std::string& buffer) { + if (buffer.empty()) { + LOGE("Provided public key buffer is empty"); + return nullptr; + } + return LoadPrivateKeyInfo(reinterpret_cast(buffer.data()), + buffer.size()); +} + +// static +std::unique_ptr RsaPublicKey::LoadPrivateKeyInfo( + const std::vector& buffer) { + if (buffer.empty()) { + LOGE("Provided public key buffer is empty"); + return nullptr; + } + return LoadPrivateKeyInfo(buffer.data(), buffer.size()); +} + +bool RsaPublicKey::IsMatchingPrivateKey( + const RsaPrivateKey& private_key) const { + if (private_key.field_size() != field_size_) { + return false; + } + return RsaKeysAreMatchingPair(GetRsaKey(), private_key.GetRsaKey()); +} + +OEMCryptoResult RsaPublicKey::Serialize(uint8_t* buffer, + size_t* buffer_size) const { + if (buffer_size == nullptr) { + LOGE("Output buffer size is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (buffer == nullptr && *buffer_size > 0) { + LOGE("Output buffer is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + uint8_t* der_key_raw = nullptr; + const int der_res = i2d_RSA_PUBKEY(key_, &der_key_raw); + if (der_res < 0) { + LOGE("Public key serialization failed"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + ScopedObject der_key(der_key_raw); + der_key_raw = nullptr; + if (!der_key) { + LOGE("Encoded key is unexpectedly null"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (der_res == 0) { + LOGE("Unexpected DER encoded size"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const size_t required_size = static_cast(der_res); + if (buffer == nullptr || *buffer_size < required_size) { + *buffer_size = required_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + memcpy(buffer, der_key.get(), required_size); + *buffer_size = required_size; + return OEMCrypto_SUCCESS; +} + +std::vector RsaPublicKey::Serialize() const { + size_t key_size = kPublicKeySize; + std::vector key_data(key_size, 0); + const OEMCryptoResult res = Serialize(key_data.data(), &key_size); + if (res != OEMCrypto_SUCCESS) { + LOGE("Failed to serialize public key: result = %d", static_cast(res)); + key_data.clear(); + } else { + key_data.resize(key_size); + } + return key_data; +} + +OEMCryptoResult RsaPublicKey::VerifySignature( + const uint8_t* message, size_t message_length, const uint8_t* signature, + size_t signature_length, RsaSignatureAlgorithm algorithm) const { + if (signature == nullptr || signature_length == 0) { + LOGE("Signature is missing"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (message == nullptr && message_length > 0) { + LOGE("Bad message data"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + switch (algorithm) { + case kRsaPssDefault: + return VerifySignaturePss(message, message_length, signature, + signature_length); + case kRsaPkcs1Cast: + return VerifySignaturePkcs1Cast(message, message_length, signature, + signature_length); + } + LOGE("Unknown RSA signature algorithm: %d", static_cast(algorithm)); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +OEMCryptoResult RsaPublicKey::VerifySignature( + const std::string& message, const std::string& signature, + RsaSignatureAlgorithm algorithm) const { + if (signature.empty()) { + LOGE("Signature should not be empty"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + return VerifySignature(reinterpret_cast(message.data()), + message.size(), + reinterpret_cast(signature.data()), + signature.size(), algorithm); +} + +OEMCryptoResult RsaPublicKey::VerifySignature( + const std::vector& message, const std::vector& signature, + RsaSignatureAlgorithm algorithm) const { + if (signature.empty()) { + LOGE("Signature should not be empty"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + return VerifySignature(message.data(), message.size(), signature.data(), + signature.size(), algorithm); +} + +OEMCryptoResult RsaPublicKey::EncryptSessionKey( + const uint8_t* session_key, size_t session_key_size, + uint8_t* enc_session_key, size_t* enc_session_key_size) const { + if (session_key == nullptr) { + LOGE("Session key is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (session_key_size != kRsaSessionKeySize) { + LOGE("Unexpected session key size: expected = %zu, actual = %zu", + kRsaSessionKeySize, session_key_size); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (enc_session_key_size == nullptr) { + LOGE("Output encrypted session key size is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (enc_session_key == nullptr && *enc_session_key_size > 0) { + LOGE("Output encrypted session key is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + return EncryptOaep(session_key, session_key_size, enc_session_key, + enc_session_key_size); +} + +std::vector RsaPublicKey::EncryptSessionKey( + const std::vector& session_key) const { + if (session_key.empty()) { + LOGE("Session key is empty"); + return std::vector(); + } + size_t enc_session_key_size = static_cast(RSA_size(key_)); + std::vector enc_session_key(enc_session_key_size); + const OEMCryptoResult res = + EncryptSessionKey(session_key.data(), session_key.size(), + enc_session_key.data(), &enc_session_key_size); + if (res != OEMCrypto_SUCCESS) { + LOGE("Failed to encrypt session key: result = %d", static_cast(res)); + enc_session_key.clear(); + } else { + enc_session_key.resize(enc_session_key_size); + } + return enc_session_key; +} + +std::vector RsaPublicKey::EncryptSessionKey( + const std::string& session_key) const { + if (session_key.empty()) { + LOGE("Session key is empty"); + return std::vector(); + } + size_t enc_session_key_size = static_cast(RSA_size(key_)); + std::vector enc_session_key(enc_session_key_size); + const OEMCryptoResult res = EncryptSessionKey( + reinterpret_cast(session_key.data()), session_key.size(), + enc_session_key.data(), &enc_session_key_size); + if (res != OEMCrypto_SUCCESS) { + LOGE("Failed to encrypt session key: result = %d", static_cast(res)); + enc_session_key.clear(); + } else { + enc_session_key.resize(enc_session_key_size); + } + return enc_session_key; +} + +OEMCryptoResult RsaPublicKey::EncryptEncryptionKey( + const uint8_t* encryption_key, size_t encryption_key_size, + uint8_t* enc_encryption_key, size_t* enc_encryption_key_size) const { + if (encryption_key == nullptr) { + LOGE("Encryption key is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (encryption_key_size != kEncryptionKeySize) { + LOGE("Unexpected encryption key size: expected = %zu, actual = %zu", + kEncryptionKeySize, encryption_key_size); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (enc_encryption_key_size == nullptr) { + LOGE("Output encrypted encryption key size is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (enc_encryption_key == nullptr && *enc_encryption_key_size > 0) { + LOGE("Output encrypted encryption key is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + return EncryptOaep(encryption_key, encryption_key_size, enc_encryption_key, + enc_encryption_key_size); +} + +std::vector RsaPublicKey::EncryptEncryptionKey( + const std::vector& encryption_key) const { + if (encryption_key.empty()) { + LOGE("Session key is empty"); + return std::vector(); + } + size_t enc_encryption_key_size = static_cast(RSA_size(key_)); + std::vector enc_encryption_key(enc_encryption_key_size); + const OEMCryptoResult res = + EncryptSessionKey(encryption_key.data(), encryption_key.size(), + enc_encryption_key.data(), &enc_encryption_key_size); + if (res != OEMCrypto_SUCCESS) { + LOGE("Failed to encrypt encryption key: result = %d", + static_cast(res)); + enc_encryption_key.clear(); + } else { + enc_encryption_key.resize(enc_encryption_key_size); + } + return enc_encryption_key; +} + +std::vector RsaPublicKey::EncryptEncryptionKey( + const std::string& encryption_key) const { + if (encryption_key.empty()) { + LOGE("Session key is empty"); + return std::vector(); + } + size_t enc_encryption_key_size = static_cast(RSA_size(key_)); + std::vector enc_encryption_key(enc_encryption_key_size); + const OEMCryptoResult res = + EncryptSessionKey(reinterpret_cast(encryption_key.data()), + encryption_key.size(), enc_encryption_key.data(), + &enc_encryption_key_size); + if (res != OEMCrypto_SUCCESS) { + LOGE("Failed to encrypt encryption key: result = %d", + static_cast(res)); + enc_encryption_key.clear(); + } else { + enc_encryption_key.resize(enc_encryption_key_size); + } + return enc_encryption_key; +} + +RsaPublicKey::~RsaPublicKey() { + if (key_ != nullptr) { + RSA_free(key_); + key_ = nullptr; + } + allowed_schemes_ = 0; + field_size_ = kRsaFieldUnknown; +} + +bool RsaPublicKey::InitFromSubjectPublicKeyInfo(const uint8_t* buffer, + size_t length) { + // Step 1: Deserialize SubjectPublicKeyInfo as RSA key. + const uint8_t* tp = buffer; + ScopedRsaKey key(d2i_RSA_PUBKEY(nullptr, &tp, length)); + if (!key) { + LOGE("Failed to parse key"); + return false; + } + // Step 2: Verify key. + const int bits = RSA_bits(key.get()); + field_size_ = RealBitSizeToFieldSize(bits); + if (field_size_ == kRsaFieldUnknown) { + LOGE("Unsupported RSA key size: bits = %d", bits); + return false; + } + allowed_schemes_ = kSign_RSASSA_PSS; + key_ = key.release(); + return true; +} + +bool RsaPublicKey::InitFromPrivateKeyInfo(const uint8_t* buffer, + size_t length) { + ScopedRsaKey private_key; + bool explicit_schemes = false; + if (!ParseRsaPrivateKeyInfo(buffer, length, &private_key, &allowed_schemes_, + &explicit_schemes, &field_size_)) { + return false; + } + // Need to strip the private key information. + return InitFromSslHandle(private_key.get(), allowed_schemes_); +} + +bool RsaPublicKey::InitFromPrivateKey(const RsaPrivateKey& private_key) { + return InitFromSslHandle(private_key.GetRsaKey(), + private_key.allowed_schemes()); +} + +bool RsaPublicKey::InitFromSslHandle(const RSA* rsa_handle, + uint32_t allowed_schemes) { + ScopedRsaKey key(RSA_new()); + if (!key) { + LOGE("Failed to allocate key"); + return false; + } + const BIGNUM* n = nullptr; + const BIGNUM* e = nullptr; + const BIGNUM* d = nullptr; + RSA_get0_key(rsa_handle, &n, &e, &d); + if (n == nullptr || e == nullptr) { + LOGE("Failed to get RSA private key components"); + return false; + } + ScopedBigNum dub_n(BN_dup(n)); + ScopedBigNum dub_e(BN_dup(e)); + if (!dub_n || !dub_e) { + LOGE("Failed to duplicate RSA public key components"); + return false; + } + if (!RSA_set0_key(key.get(), dub_n.get(), dub_e.get(), nullptr)) { + LOGE("Failed to RSA public key components"); + return false; + } + // Ownership has transferred to the RSA key. + dub_n.release(); + dub_e.release(); + + key_ = key.release(); + allowed_schemes_ = allowed_schemes; + const int bits = RSA_bits(rsa_handle); + field_size_ = RealBitSizeToFieldSize(bits); + if (field_size_ == kRsaFieldUnknown) { + LOGE("Unsupported RSA key size: bits = %d", bits); + return false; + } + return true; +} + +OEMCryptoResult RsaPublicKey::VerifySignaturePss( + const uint8_t* message, size_t message_length, const uint8_t* signature, + size_t signature_length) const { + // Step 0: Ensure the signature algorithm is supported by key. + if (!(allowed_schemes_ & kSign_RSASSA_PSS)) { + LOGE("RSA key cannot verify using PSS"); + return OEMCrypto_ERROR_INVALID_KEY; + } + // Step 1: Create a high-level key from RSA key. + ScopedEvpPkey pkey(EVP_PKEY_new()); + if (!pkey) { + LOGE("Failed to allocate PKEY"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!EVP_PKEY_set1_RSA(pkey.get(), key_)) { + LOGE("Failed to set PKEY RSA key"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 2a: Setup a EVP MD CTX for PSS Verification. + ScopedEvpMdCtx md_ctx = EVP_MD_CTX_new(); + if (!md_ctx) { + LOGE("Failed to allocate MD CTX"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + EVP_PKEY_CTX* pkey_ctx = nullptr; // Ownership is maintained by |md_ctx| + int res = EVP_DigestVerifyInit(md_ctx.get(), &pkey_ctx, EVP_sha1(), nullptr, + pkey.get()); + if (res != 1) { + LOGE("Failed to initialize MD CTX for verification"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (pkey_ctx == nullptr) { + LOGE("PKEY CTX is unexpectedly null"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 2b: Configure OEMCrypto RSASSA-PSS options. + res = EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING); + if (res != 1) { + LOGE("Failed to set PSS padding"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + res = EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, kPssSaltLength); + if (res != 1) { + LOGE("Failed to set PSS salt length"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + res = EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, EVP_sha1()); + if (res != 1) { + LOGE("Failed to set PSS MGF1 MD"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 3: Digest message. + if (message_length > 0) { + res = EVP_DigestVerifyUpdate(md_ctx.get(), message, message_length); + if (res != 1) { + LOGE("Failed to update MD"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } + // Step 4: Verify message. + res = EVP_DigestVerifyFinal(md_ctx.get(), signature, signature_length); + if (res < 0) { + LOGE("Failed to perform RSASSA-PSS-VERIFY"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return res ? OEMCrypto_SUCCESS : OEMCrypto_ERROR_SIGNATURE_FAILURE; +} + +OEMCryptoResult RsaPublicKey::VerifySignaturePkcs1Cast( + const uint8_t* message, size_t message_length, const uint8_t* signature, + size_t signature_length) const { + // Step 0: Ensure the signature algorithm is supported by key. + if (!(allowed_schemes_ & kSign_PKCS1_Block1)) { + LOGE("RSA key cannot verify using PKCS1"); + return OEMCrypto_ERROR_INVALID_KEY; + } + if (message_length > kRsaPkcs1CastMaxMessageSize) { + LOGE("Message is too large for CAST PKCS1 signature: size = %zu", + message_length); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + // Step 1: Convert encrypted blob into digest. + std::vector digest(RSA_size(key_)); + const int res = + RSA_public_decrypt(static_cast(signature_length), signature, + digest.data(), key_, RSA_PKCS1_PADDING); + if (res <= 0) { + LOGE("Failed to perform public decryption"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 2: Compare digests. + const size_t digest_size = static_cast(res); + if (digest_size != message_length) { + LOGD("Digest size does not match"); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + digest.resize(digest_size); + for (size_t i = 0; i < digest_size; i++) { + if (message[i] != digest[i]) { + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult RsaPublicKey::EncryptOaep(const uint8_t* message, + size_t message_size, + uint8_t* enc_message, + size_t* enc_message_length) const { + // Step 1: Encrypt using RSAES-OAEP. + std::vector encrypt_buffer(RSA_size(key_)); + const int enc_res = + RSA_public_encrypt(static_cast(message_size), message, + encrypt_buffer.data(), key_, RSA_PKCS1_OAEP_PADDING); + if (enc_res < 0) { + LOGE("Failed to perform RSAES-OAEP encrypt"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 2: Copy encrypted data key. + const size_t enc_size = static_cast(enc_res); + if (*enc_message_length < enc_size) { + *enc_message_length = enc_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *enc_message_length = enc_size; + memcpy(enc_message, encrypt_buffer.data(), enc_size); + return OEMCrypto_SUCCESS; +} + +// ===== ===== ===== RSA Private Key ===== ===== ===== + +// static +std::unique_ptr RsaPrivateKey::New(RsaFieldSize field_size) { + std::unique_ptr key(new RsaPrivateKey()); + if (!key->InitFromFieldSize(field_size)) { + LOGE("Failed to initialize private key from field size"); + key.reset(); + } + return key; +} + +// static +std::unique_ptr RsaPrivateKey::Load(const uint8_t* buffer, + size_t length) { + if (buffer == nullptr) { + LOGE("Provided private key buffer is null"); + return nullptr; + } + if (length == 0) { + LOGE("Provided private key buffer is zero length"); + return nullptr; + } + std::unique_ptr key(new RsaPrivateKey()); + if (!key->InitFromPrivateKeyInfo(buffer, length)) { + LOGE("Failed to initialize private key from PrivateKeyInfo"); + key.reset(); + } + return key; +} + +// static +std::unique_ptr RsaPrivateKey::Load(const std::string& buffer) { + if (buffer.empty()) { + LOGE("Provided private key buffer is empty"); + return std::unique_ptr(); + } + return Load(reinterpret_cast(buffer.data()), buffer.size()); +} + +// static +std::unique_ptr RsaPrivateKey::Load( + const std::vector& buffer) { + if (buffer.empty()) { + LOGE("Provided private key buffer is empty"); + return std::unique_ptr(); + } + return Load(buffer.data(), buffer.size()); +} + +std::unique_ptr RsaPrivateKey::MakePublicKey() const { + return RsaPublicKey::New(*this); +} + +bool RsaPrivateKey::IsMatchingPublicKey(const RsaPublicKey& public_key) const { + return RsaKeysAreMatchingPair(public_key.GetRsaKey(), GetRsaKey()); +} + +OEMCryptoResult RsaPrivateKey::Serialize(uint8_t* buffer, + size_t* buffer_size) const { + if (buffer_size == nullptr) { + LOGE("Output buffer size is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (buffer == nullptr && *buffer_size > 0) { + LOGE("Output buffer is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + // Step 1: Convert RSA key to EVP. + ScopedEvpPkey pkey(EVP_PKEY_new()); + if (!pkey) { + LOGE("Failed to allocate EVP"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!EVP_PKEY_set1_RSA(pkey.get(), key_)) { + LOGE("Failed to set EVP RSA key"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 2: Convert RSA EVP to PKCS8 format. + ScopedPrivateKeyInfo priv_info(EVP_PKEY2PKCS8(pkey.get())); + if (!priv_info) { + LOGE("Failed to convert RSA key to PKCS8 info"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 3: Serialize PKCS8 to DER encoding. + ScopedBio bio(BIO_new(BIO_s_mem())); + if (!bio) { + LOGE("Failed to allocate IO buffer for RSA key"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!i2d_PKCS8_PRIV_KEY_INFO_bio(bio.get(), priv_info.get())) { + LOGE("Failed to serialize RSA key"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 4: Determine key size and copy. + char* key_ptr = nullptr; + const long key_size = BIO_get_mem_data(bio.get(), &key_ptr); + if (key_size < 0) { + LOGE("Failed to get RSA key size"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (key_ptr == nullptr) { + LOGE("Encoded key is unexpectedly null"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const size_t required_size = + static_cast(key_size) + (explicit_schemes_ ? 8 : 0); + if (*buffer_size < required_size) { + *buffer_size = required_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *buffer_size = required_size; + if (explicit_schemes_) { + memcpy(buffer, "SIGN", 4); + const uint32_t allowed_schemes = htonl(allowed_schemes_); + memcpy(&buffer[4], &allowed_schemes, 4); + memcpy(&buffer[8], key_ptr, required_size - 8); + } else { + memcpy(buffer, key_ptr, required_size); + } + return OEMCrypto_SUCCESS; +} + +std::vector RsaPrivateKey::Serialize() const { + size_t key_size = kPrivateKeySize; + std::vector key_data(key_size, 0); + OEMCryptoResult res = Serialize(key_data.data(), &key_size); + if (res == OEMCrypto_ERROR_SHORT_BUFFER) { + LOGD( + "Actual RSA private key size is larger than expected: " + "expected = %zu, actual = %zu", + kPrivateKeySize, key_size); + key_data.resize(key_size); + res = Serialize(key_data.data(), &key_size); + } + if (res != OEMCrypto_SUCCESS) { + LOGE("Failed to serialize private key: result = %d", static_cast(res)); + key_data.clear(); + } else { + key_data.resize(key_size); + } + return key_data; +} + +OEMCryptoResult RsaPrivateKey::GenerateSignature( + const uint8_t* message, size_t message_length, + RsaSignatureAlgorithm algorithm, uint8_t* signature, + size_t* signature_length) const { + if (signature_length == nullptr) { + LOGE("Output signature size is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (signature == nullptr && *signature_length > 0) { + LOGE("Output signature is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (message == nullptr && message_length > 0) { + LOGE("Invalid message data"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + switch (algorithm) { + case kRsaPssDefault: + return GenerateSignaturePss(message, message_length, signature, + signature_length); + case kRsaPkcs1Cast: + return GenerateSignaturePkcs1Cast(message, message_length, signature, + signature_length); + } + LOGE("Unknown RSA signature algorithm: %d", static_cast(algorithm)); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +std::vector RsaPrivateKey::GenerateSignature( + const std::string& message, RsaSignatureAlgorithm algorithm) const { + size_t signature_size = SignatureSize(); + std::vector signature(signature_size); + const OEMCryptoResult res = GenerateSignature( + reinterpret_cast(message.data()), message.size(), + algorithm, signature.data(), &signature_size); + if (res != OEMCrypto_SUCCESS) { + LOGE("Failed to generate signature: result = %d", static_cast(res)); + signature.clear(); + } else { + signature.resize(signature_size); + } + return signature; +} + +std::vector RsaPrivateKey::GenerateSignature( + const std::vector& message, + RsaSignatureAlgorithm algorithm) const { + size_t signature_size = SignatureSize(); + std::vector signature(signature_size, 0); + const OEMCryptoResult res = + GenerateSignature(message.data(), message.size(), algorithm, + signature.data(), &signature_size); + if (res != OEMCrypto_SUCCESS) { + LOGE("Failed to generate signature: result = %d", static_cast(res)); + signature.clear(); + } else { + signature.resize(signature_size); + } + return signature; +} + +size_t RsaPrivateKey::SignatureSize() const { return RSA_size(key_); } + +OEMCryptoResult RsaPrivateKey::DecryptSessionKey( + const uint8_t* enc_session_key, size_t enc_session_key_size, + uint8_t* session_key, size_t* session_key_size) const { + if (enc_session_key == nullptr || enc_session_key_size == 0) { + LOGE("Encrypted session key is %s", enc_session_key ? "empty" : "null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (session_key_size == nullptr) { + LOGE("Output session key size buffer is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (session_key == nullptr && *session_key_size > 0) { + LOGE("Output session key buffer is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (*session_key_size < kRsaSessionKeySize) { + *session_key_size = kRsaSessionKeySize; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + const OEMCryptoResult result = DecryptOaep( + enc_session_key, enc_session_key_size, session_key, kRsaSessionKeySize); + if (result == OEMCrypto_SUCCESS) { + *session_key_size = kRsaSessionKeySize; + } else { + LOGE("Failed to decrypt session key"); + } + return result; +} + +std::vector RsaPrivateKey::DecryptSessionKey( + const std::vector& enc_session_key) const { + size_t session_key_size = kRsaSessionKeySize; + std::vector session_key(session_key_size, 0); + const OEMCryptoResult res = + DecryptSessionKey(enc_session_key.data(), enc_session_key.size(), + session_key.data(), &session_key_size); + if (res != OEMCrypto_SUCCESS) { + session_key.clear(); + } else { + session_key.resize(session_key_size); + } + return session_key; +} + +std::vector RsaPrivateKey::DecryptSessionKey( + const std::string& enc_session_key) const { + size_t session_key_size = kRsaSessionKeySize; + std::vector session_key(session_key_size, 0); + const OEMCryptoResult res = DecryptSessionKey( + reinterpret_cast(enc_session_key.data()), + enc_session_key.size(), session_key.data(), &session_key_size); + if (res != OEMCrypto_SUCCESS) { + session_key.clear(); + } else { + session_key.resize(session_key_size); + } + return session_key; +} + +size_t RsaPrivateKey::SessionKeyLength() const { return kRsaSessionKeySize; } + +OEMCryptoResult RsaPrivateKey::DecryptEncryptionKey( + const uint8_t* enc_encryption_key, size_t enc_encryption_key_size, + uint8_t* encryption_key, size_t* encryption_key_size) const { + if (enc_encryption_key == nullptr || enc_encryption_key_size == 0) { + LOGE("Encrypted encryption key is %s", + enc_encryption_key ? "empty" : "null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (encryption_key_size == nullptr) { + LOGE("Output encryption key size buffer is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (encryption_key == nullptr && *encryption_key_size > 0) { + LOGE("Output encryption key buffer is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (*encryption_key_size < kEncryptionKeySize) { + *encryption_key_size = kEncryptionKeySize; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + const OEMCryptoResult result = + DecryptOaep(enc_encryption_key, enc_encryption_key_size, encryption_key, + kEncryptionKeySize); + if (result == OEMCrypto_SUCCESS) { + *encryption_key_size = kEncryptionKeySize; + } else { + LOGE("Failed to decrypt encryption key"); + } + return result; +} + +std::vector RsaPrivateKey::DecryptEncryptionKey( + const std::vector& enc_encryption_key) const { + size_t encryption_key_size = kEncryptionKeySize; + std::vector encryption_key(encryption_key_size, 0); + const OEMCryptoResult res = + DecryptEncryptionKey(enc_encryption_key.data(), enc_encryption_key.size(), + encryption_key.data(), &encryption_key_size); + if (res != OEMCrypto_SUCCESS) { + encryption_key.clear(); + } else { + encryption_key.resize(encryption_key_size); + } + return encryption_key; +} + +std::vector RsaPrivateKey::DecryptEncryptionKey( + const std::string& enc_encryption_key) const { + size_t encryption_key_size = kEncryptionKeySize; + std::vector encryption_key(encryption_key_size, 0); + const OEMCryptoResult res = DecryptEncryptionKey( + reinterpret_cast(enc_encryption_key.data()), + enc_encryption_key.size(), encryption_key.data(), &encryption_key_size); + if (res != OEMCrypto_SUCCESS) { + encryption_key.clear(); + } else { + encryption_key.resize(encryption_key_size); + } + return encryption_key; +} + +RsaPrivateKey::~RsaPrivateKey() { + if (key_ != nullptr) { + RSA_free(key_); + key_ = nullptr; + } + allowed_schemes_ = 0; + explicit_schemes_ = false; + field_size_ = kRsaFieldUnknown; +} + +bool RsaPrivateKey::InitFromPrivateKeyInfo(const uint8_t* buffer, + size_t length) { + ScopedRsaKey key; + if (!ParseRsaPrivateKeyInfo(buffer, length, &key, &allowed_schemes_, + &explicit_schemes_, &field_size_)) { + return false; + } + key_ = key.release(); + return true; +} + +bool RsaPrivateKey::InitFromFieldSize(RsaFieldSize field_size) { + if (field_size != kRsa2048Bit && field_size != kRsa3072Bit) { + LOGE("Unsupported RSA field size: bits = %d", static_cast(field_size)); + return false; + } + // Step 1: Create exponent. + ScopedBigNum exp(BN_new()); + if (!exp) { + LOGE("Failed to allocate RSA exponent"); + return false; + } + if (!BN_set_word(exp.get(), RSA_F4)) { + LOGE("Failed to set RSA exponent"); + return false; + } + // Step 2: Generate RSA key. + ScopedRsaKey key(RSA_new()); + if (!key) { + LOGE("Failed to allocate RSA key"); + return false; + } + if (!RSA_generate_key_ex(key.get(), static_cast(field_size), exp.get(), + nullptr)) { + LOGE("Failed to generate RSA key"); + return false; + } + key_ = key.release(); + allowed_schemes_ = kSign_RSASSA_PSS; + field_size_ = field_size; + return true; +} + +OEMCryptoResult RsaPrivateKey::GenerateSignaturePss( + const uint8_t* message, size_t message_length, uint8_t* signature, + size_t* signature_length) const { + // Step 0: Ensure the signature algorithm is supported by key. + if (!(allowed_schemes_ & kSign_RSASSA_PSS)) { + LOGE("RSA key cannot sign using PSS"); + return OEMCrypto_ERROR_INVALID_KEY; + } + // Step 1: Create a high-level key from RSA key. + ScopedEvpPkey pkey(EVP_PKEY_new()); + if (!pkey) { + LOGE("Failed to allocate PKEY"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!EVP_PKEY_set1_RSA(pkey.get(), key_)) { + LOGE("Failed to set PKEY RSA key"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 2a: Setup a EVP MD CTX for PSS Signature Generation. + ScopedEvpMdCtx md_ctx(EVP_MD_CTX_new()); + if (!md_ctx) { + LOGE("Failed to allocate MD CTX"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + EVP_PKEY_CTX* pkey_ctx = nullptr; // Ownership is maintained by |md_ctx| + int res = EVP_DigestSignInit(md_ctx.get(), &pkey_ctx, EVP_sha1(), nullptr, + pkey.get()); + if (res != 1) { + LOGE("Failed to initialize MD CTX for signing"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (pkey_ctx == nullptr) { + LOGE("PKEY CTX is unexpectedly null"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 2b: Configure OEMCrypto RSASSA-PSS options. + res = EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING); + if (res != 1) { + LOGE("Failed to set PSS padding"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + res = EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, kPssSaltLength); + if (res != 1) { + LOGE("Failed to set PSS salt length"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + res = EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, EVP_sha1()); + if (res != 1) { + LOGE("Failed to set PSS MGF1 MD"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 3: Digest message. + if (message_length > 0) { + res = EVP_DigestSignUpdate(md_ctx.get(), message, message_length); + if (res != 1) { + LOGE("Failed to update MD"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } + // Step 4a: Determine size of signature. + size_t actual_signature_length = 0; + res = EVP_DigestSignFinal(md_ctx.get(), nullptr, &actual_signature_length); + if (res != 1) { + LOGE("Failed to determine signature length"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (*signature_length < actual_signature_length) { + *signature_length = actual_signature_length; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + // Step 4b: Generate signature. + res = EVP_DigestSignFinal(md_ctx.get(), signature, signature_length); + if (res != 1) { + LOGE("Failed to perform RSASSA-PSS-SIGN"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult RsaPrivateKey::GenerateSignaturePkcs1Cast( + const uint8_t* message, size_t message_length, uint8_t* signature, + size_t* signature_length) const { + // Step 0: Ensure the signature algorithm is supported by key. + if (!(allowed_schemes_ & kSign_PKCS1_Block1)) { + LOGE("RSA key cannot sign PKCS1"); + return OEMCrypto_ERROR_INVALID_KEY; + } + if (message_length > kRsaPkcs1CastMaxMessageSize) { + LOGE("Message is too large for CAST PKCS1 signature: size = %zu", + message_length); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + // Step 1: Ensure signature buffer is large enough. + const size_t expected_signature_size = static_cast(RSA_size(key_)); + if (*signature_length < expected_signature_size) { + *signature_length = expected_signature_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + // Step 2: Encrypt with PKCS1 padding. + const int enc_res = + RSA_private_encrypt(static_cast(message_length), message, signature, + key_, RSA_PKCS1_PADDING); + if (enc_res < 0) { + LOGE("Failed to perform private encryption"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + *signature_length = expected_signature_size; + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult RsaPrivateKey::DecryptOaep( + const uint8_t* enc_message, size_t enc_message_size, uint8_t* message, + size_t expected_message_length) const { + // Step 1: Decrypt using RSAES-OAEP. + std::vector decrypt_buffer(RSA_size(key_)); + const int dec_res = + RSA_private_decrypt(static_cast(enc_message_size), enc_message, + decrypt_buffer.data(), key_, RSA_PKCS1_OAEP_PADDING); + if (dec_res < 0) { + LOGE("Failed to perform RSAES-OAEP decrypt"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (static_cast(dec_res) != expected_message_length) { + LOGE("Unexpected key size: expected = %zu, actual = %d", + expected_message_length, dec_res); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Step 2: Copy decrypted data key. + memcpy(message, decrypt_buffer.data(), expected_message_length); + return OEMCrypto_SUCCESS; +} +} // namespace util +} // namespace wvoec diff --git a/oemcrypto/util/src/wvcrc.cpp b/oemcrypto/util/src/wvcrc.cpp new file mode 100644 index 0000000..097ca70 --- /dev/null +++ b/oemcrypto/util/src/wvcrc.cpp @@ -0,0 +1,88 @@ +// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine +// License Agreement. +// +// Compute CRC32/MPEG2 Checksum. Needed for verification of WV Keybox. +// +#include "platform.h" +#include "wvcrc32.h" + +namespace wvoec { +namespace util { +#define INIT_CRC32 0xffffffff + +uint32_t wvrunningcrc32(const uint8_t* p_begin, size_t i_count, + uint32_t i_crc) { + constexpr uint32_t CRC32[256] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, + 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, + 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, + 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, + 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, + 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, + 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, + 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, + 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, + 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, + 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, + 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, + 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, + 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, + 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, + 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, + 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4}; + + /* Calculate the CRC */ + while (i_count > 0) { + i_crc = (i_crc << 8) ^ CRC32[(i_crc >> 24) ^ ((uint32_t) * p_begin)]; + p_begin++; + i_count--; + } + + return(i_crc); +} + +uint32_t wvcrc32(const uint8_t* p_begin, size_t i_count) { + return(wvrunningcrc32(p_begin, i_count, INIT_CRC32)); +} + +uint32_t wvcrc32Init() { + return INIT_CRC32; +} + +uint32_t wvcrc32Cont(const uint8_t* p_begin, size_t i_count, + uint32_t prev_crc) { + return(wvrunningcrc32(p_begin, i_count, prev_crc)); +} + +uint32_t wvcrc32n(const uint8_t* p_begin, size_t i_count) { + return htonl(wvrunningcrc32(p_begin, i_count, INIT_CRC32)); +} +} // namespace util +} // namespace wvoec diff --git a/oemcrypto/util/test/cmac_unittest.cpp b/oemcrypto/util/test/cmac_unittest.cpp new file mode 100644 index 0000000..75cb611 --- /dev/null +++ b/oemcrypto/util/test/cmac_unittest.cpp @@ -0,0 +1,129 @@ +// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. +// +// Reference implementation utilities of OEMCrypto APIs +// +#include + +#include "cmac.h" + +namespace wvoec { +namespace util { +namespace { +// Test vectors are from NIST Special Publication 800-38B for +// CMAC-AES-128 and CMAC-AES-256. The same data chunks are used +// for both tests. +const uint8_t kDataChunk1[] = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a}; +const uint8_t kDataChunk2[] = {0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11}; +const uint8_t kDataChunk3[] = {0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, + 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10}; + +const uint8_t kKey128[16] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}; +const uint8_t kKey256[32] = {0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, + 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, + 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, + 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4}; +} // namespace + +TEST(OEMCryptoCmacTest, BadInit) { + // If the parameters to the Create() function are invalid, the + // Cmac instance is not created. + std::unique_ptr cmac = Cmac::Create(nullptr, sizeof(kKey128)); + EXPECT_FALSE(cmac); + cmac = Cmac::Create(nullptr, sizeof(kKey256)); + EXPECT_FALSE(cmac); + cmac = Cmac::Create(kKey128, 0); + EXPECT_FALSE(cmac); + cmac = Cmac::Create(kKey256, (sizeof(kKey128) + sizeof(kKey256)) / 2); + EXPECT_FALSE(cmac); +} + +TEST(OEMCryptoCmacTest, CmacAes128) { + std::unique_ptr cmac = Cmac::Create(kKey128, sizeof(kKey128)); + ASSERT_TRUE(cmac); + + std::vector digest; + ASSERT_TRUE(cmac->Finalize(&digest)); + const std::vector kExpectedEmptyDigest = { + 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, + 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46}; + EXPECT_EQ(kExpectedEmptyDigest, digest); + cmac->Reset(); + + ASSERT_TRUE(cmac->Update(kDataChunk1, sizeof(kDataChunk1))); + ASSERT_TRUE(cmac->Finalize(&digest)); + const std::vector kExpectedDigest1 = { + 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, + 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c}; + EXPECT_EQ(kExpectedDigest1, digest); + cmac->Reset(); + + ASSERT_TRUE(cmac->Update(kDataChunk1, sizeof(kDataChunk1))); + ASSERT_TRUE(cmac->Update(kDataChunk2, sizeof(kDataChunk2))); + ASSERT_TRUE(cmac->Finalize(&digest)); + const std::vector kExpectedDigest2 = { + 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, + 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27}; + EXPECT_EQ(kExpectedDigest2, digest); + cmac->Reset(); + + ASSERT_TRUE(cmac->Update(kDataChunk1, sizeof(kDataChunk1))); + for (size_t i = 0; i < sizeof(kDataChunk2); i++) { + ASSERT_TRUE(cmac->Update(kDataChunk2[i])) << " i = " << i; + } + ASSERT_TRUE(cmac->Update(kDataChunk3, sizeof(kDataChunk3))); + ASSERT_TRUE(cmac->Finalize(&digest)); + const std::vector kExpectedDigest3 = { + 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, + 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe}; + EXPECT_EQ(kExpectedDigest3, digest); +} + +TEST(OEMCryptoCmacTest, CmacAes256) { + std::unique_ptr cmac = Cmac::Create(kKey256, sizeof(kKey256)); + ASSERT_TRUE(cmac); + + std::vector digest; + ASSERT_TRUE(cmac->Finalize(&digest)); + const std::vector kExpectedEmptyDigest = { + 0x02, 0x89, 0x62, 0xf6, 0x1b, 0x7b, 0xf8, 0x9e, + 0xfc, 0x6b, 0x55, 0x1f, 0x46, 0x67, 0xd9, 0x83}; + EXPECT_EQ(kExpectedEmptyDigest, digest); + cmac->Reset(); + + ASSERT_TRUE(cmac->Update(kDataChunk1, sizeof(kDataChunk1))); + ASSERT_TRUE(cmac->Finalize(&digest)); + const std::vector kExpectedDigest1 = { + 0x28, 0xa7, 0x02, 0x3f, 0x45, 0x2e, 0x8f, 0x82, + 0xbd, 0x4b, 0xf2, 0x8d, 0x8c, 0x37, 0xc3, 0x5c}; + EXPECT_EQ(kExpectedDigest1, digest); + cmac->Reset(); + + ASSERT_TRUE(cmac->Update(kDataChunk1, sizeof(kDataChunk1))); + ASSERT_TRUE(cmac->Update(kDataChunk2, sizeof(kDataChunk2))); + ASSERT_TRUE(cmac->Finalize(&digest)); + const std::vector kExpectedDigest2 = { + 0xaa, 0xf3, 0xd8, 0xf1, 0xde, 0x56, 0x40, 0xc2, + 0x32, 0xf5, 0xb1, 0x69, 0xb9, 0xc9, 0x11, 0xe6}; + EXPECT_EQ(kExpectedDigest2, digest); + cmac->Reset(); + + ASSERT_TRUE(cmac->Update(kDataChunk1, sizeof(kDataChunk1))); + for (size_t i = 0; i < sizeof(kDataChunk2); i++) { + ASSERT_TRUE(cmac->Update(kDataChunk2[i])) << " i = " << i; + } + ASSERT_TRUE(cmac->Update(kDataChunk3, sizeof(kDataChunk3))); + ASSERT_TRUE(cmac->Finalize(&digest)); + const std::vector kExpectedDigest3 = { + 0xe1, 0x99, 0x21, 0x90, 0x54, 0x9f, 0x6e, 0xd5, + 0x69, 0x6a, 0x2c, 0x05, 0x6c, 0x31, 0x54, 0x10}; + EXPECT_EQ(kExpectedDigest3, digest); +} +} // namespace util +} // namespace wvoec diff --git a/oemcrypto/util/test/oem_cert_test.cpp b/oemcrypto/util/test/oem_cert_test.cpp new file mode 100644 index 0000000..234d31c --- /dev/null +++ b/oemcrypto/util/test/oem_cert_test.cpp @@ -0,0 +1,532 @@ +// 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 "oem_cert.h" + +namespace wvoec_ref { +namespace { +const uint32_t kTestOemSystemId = 7913; + +// clang-format off + +// OEM Certificate private key. +// RSA Private-Key: (2048 bit, 2 primes). +const uint8_t kTestOemPrivateKey[] = { + 0x30, 0x82, 0x04, 0xbd, 0x02, 0x01, 0x00, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x04, 0xa7, 0x30, 0x82, 0x04, 0xa3, 0x02, 0x01, + 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa8, 0x75, + 0xd5, 0x3d, 0xd3, 0xf3, 0x59, 0xd1, 0x63, 0x0f, + 0x5d, 0x5f, 0x2c, 0xaf, 0x80, 0x4e, 0x9a, 0xef, + 0x9a, 0x8f, 0x88, 0x37, 0xe5, 0x56, 0xf8, 0x66, + 0xcb, 0xa6, 0x61, 0xab, 0xc1, 0xa9, 0x04, 0xc2, + 0x12, 0x9d, 0xa6, 0x0c, 0x2c, 0xcb, 0x42, 0x09, + 0xd0, 0x36, 0xf0, 0x85, 0x01, 0xdf, 0xd5, 0xbd, + 0xaf, 0x82, 0xbb, 0x25, 0xa1, 0x61, 0x17, 0xfe, + 0xa1, 0x65, 0x34, 0xda, 0x91, 0xee, 0x91, 0x46, + 0xd6, 0x63, 0x47, 0x6a, 0xa5, 0x32, 0x62, 0xe2, + 0x4c, 0x7c, 0xf7, 0x76, 0x59, 0xe1, 0x2b, 0x47, + 0x8d, 0x1c, 0xe6, 0xa0, 0xbd, 0xc3, 0xc9, 0x36, + 0x0e, 0x90, 0x75, 0xba, 0x1c, 0xbd, 0xca, 0x85, + 0x0a, 0x4e, 0xcc, 0xfe, 0x91, 0x3f, 0x22, 0x42, + 0x96, 0xae, 0xa0, 0x87, 0x82, 0x63, 0x3b, 0x22, + 0x54, 0xbc, 0x28, 0xa3, 0x45, 0x4b, 0x34, 0x12, + 0x4e, 0xeb, 0x04, 0x4d, 0x29, 0xb3, 0x05, 0x62, + 0x0d, 0x51, 0x16, 0x46, 0x98, 0x21, 0xc8, 0x59, + 0x96, 0x53, 0x43, 0x38, 0x95, 0x1a, 0x42, 0x94, + 0x8b, 0x49, 0x5d, 0x1b, 0x8a, 0xd5, 0x82, 0x6a, + 0x32, 0x6f, 0x0e, 0x5a, 0x85, 0x3b, 0xd5, 0x42, + 0xc0, 0x4c, 0x34, 0x43, 0x7c, 0x4f, 0xef, 0xca, + 0x02, 0x99, 0x38, 0xf9, 0xa5, 0xc0, 0xd8, 0x1d, + 0xe7, 0x9e, 0x8c, 0x4d, 0x9c, 0x40, 0xcd, 0x4b, + 0x5e, 0x44, 0x37, 0x8d, 0xc5, 0x96, 0xd4, 0xf7, + 0xb3, 0x37, 0x4c, 0x2e, 0x2d, 0x30, 0xca, 0x97, + 0x39, 0xed, 0xe8, 0x73, 0xc5, 0xe7, 0xcb, 0x95, + 0xf0, 0x84, 0x4a, 0x5a, 0x9e, 0x13, 0x19, 0x5f, + 0x98, 0xe5, 0xbe, 0x31, 0x5b, 0xff, 0xed, 0x29, + 0x26, 0xc4, 0x93, 0x54, 0x49, 0x84, 0xd2, 0xeb, + 0x21, 0x40, 0x19, 0x5f, 0xf7, 0x32, 0x67, 0x93, + 0xe0, 0xda, 0x77, 0xfc, 0xda, 0x5e, 0xc4, 0x5b, + 0x95, 0x2e, 0x46, 0xf3, 0xce, 0xfd, 0x02, 0x03, + 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x01, 0x00, + 0x9e, 0x6d, 0x82, 0xc8, 0x0c, 0xc6, 0xb5, 0xd7, + 0xa7, 0xb3, 0xd1, 0x7a, 0x2a, 0x8a, 0x3a, 0xbe, + 0xb2, 0x13, 0x58, 0x66, 0x58, 0x13, 0x49, 0x4a, + 0x0b, 0x7e, 0x91, 0x53, 0xbe, 0x53, 0x4b, 0x63, + 0xeb, 0x27, 0xa1, 0x5e, 0x45, 0xc4, 0xf9, 0x73, + 0x86, 0x7d, 0xb8, 0x25, 0x92, 0xf9, 0x63, 0x93, + 0xe0, 0x6d, 0xed, 0xdb, 0xa2, 0xa9, 0x77, 0x25, + 0xda, 0xed, 0x0b, 0x58, 0x24, 0xe6, 0xd1, 0x8b, + 0x6d, 0x71, 0x13, 0x3a, 0x76, 0xf5, 0xa2, 0xba, + 0xca, 0x28, 0x4d, 0x0a, 0xd1, 0xa7, 0xaa, 0x4b, + 0x8a, 0xea, 0x55, 0x99, 0xb2, 0x83, 0xc5, 0x33, + 0x95, 0xcd, 0x92, 0xd0, 0xe5, 0x06, 0xcc, 0xf4, + 0xe8, 0xbb, 0x49, 0xc0, 0x66, 0x25, 0x9a, 0xef, + 0xa7, 0x06, 0xbc, 0xb3, 0x2a, 0x21, 0x86, 0xcc, + 0x4f, 0xd6, 0xaf, 0x9d, 0xed, 0x11, 0xef, 0x9f, + 0x14, 0x2f, 0x8b, 0xac, 0x96, 0x75, 0x03, 0x1a, + 0xe4, 0x5c, 0x48, 0x81, 0x3a, 0x4b, 0x21, 0x6e, + 0xad, 0xb3, 0x27, 0x51, 0xe9, 0x35, 0xbe, 0xed, + 0x42, 0x5f, 0x8f, 0x83, 0xf0, 0x99, 0xb0, 0xaf, + 0xa9, 0x9c, 0x2f, 0xee, 0x5f, 0xee, 0x39, 0x2b, + 0x1d, 0xb0, 0xb1, 0xf8, 0x7b, 0x69, 0x38, 0x68, + 0xae, 0xa0, 0x36, 0x2a, 0xf5, 0xed, 0x96, 0xfa, + 0x7c, 0x1c, 0x59, 0x29, 0xbf, 0xb3, 0x9e, 0x14, + 0x97, 0x06, 0xc2, 0x40, 0x30, 0x00, 0x6a, 0x95, + 0xd3, 0x86, 0x86, 0xb9, 0x4c, 0xf5, 0x51, 0xa3, + 0x6d, 0x5a, 0xd1, 0x46, 0x43, 0x24, 0xa4, 0xa9, + 0x59, 0xcf, 0xa2, 0xa7, 0x4e, 0x50, 0x7a, 0xa3, + 0x14, 0xe4, 0x4e, 0x32, 0x4d, 0xd4, 0xc2, 0xcf, + 0x2d, 0x74, 0xfb, 0x51, 0x34, 0x98, 0x68, 0xc3, + 0xd2, 0xb1, 0xd9, 0x38, 0x94, 0x91, 0x28, 0xb1, + 0x69, 0x9a, 0xbf, 0xbf, 0x1a, 0xdf, 0xd3, 0xb6, + 0x21, 0x38, 0x94, 0x1b, 0x81, 0x00, 0xb5, 0x39, + 0x02, 0x81, 0x81, 0x00, 0xdb, 0xe4, 0x83, 0xc5, + 0x7a, 0xe0, 0xcf, 0xeb, 0x07, 0x37, 0xfb, 0xf6, + 0xfe, 0xb3, 0x62, 0x72, 0x86, 0xd0, 0x12, 0x96, + 0x9d, 0xf4, 0x93, 0x90, 0xdc, 0xf6, 0xc6, 0x03, + 0x0c, 0x46, 0xb8, 0x66, 0x60, 0xf3, 0x46, 0x5b, + 0xab, 0x9f, 0x9d, 0x81, 0xac, 0x26, 0x8f, 0xd7, + 0xa3, 0xbd, 0x16, 0xbb, 0xb4, 0x4e, 0xf6, 0xc0, + 0x12, 0xb6, 0x99, 0x4a, 0xf5, 0xc1, 0x6c, 0x40, + 0x72, 0x18, 0x71, 0x02, 0x65, 0x77, 0xb1, 0xfb, + 0xec, 0x19, 0xbb, 0x8c, 0x03, 0xea, 0x7b, 0x17, + 0x63, 0xc9, 0xb9, 0x3b, 0x10, 0x56, 0x19, 0xef, + 0x86, 0x69, 0x4d, 0x61, 0x03, 0xac, 0x30, 0x65, + 0x63, 0xe5, 0xe1, 0x0f, 0xd6, 0xf6, 0x5b, 0xc9, + 0x7c, 0xde, 0x9b, 0x26, 0xca, 0x98, 0xda, 0x0c, + 0x5b, 0x6f, 0x91, 0x88, 0xbf, 0x98, 0xc3, 0xbc, + 0x72, 0x21, 0xa0, 0x07, 0x0a, 0x5e, 0xc7, 0x61, + 0x4a, 0xb3, 0x32, 0xc7, 0x02, 0x81, 0x81, 0x00, + 0xc4, 0x1f, 0x4a, 0x23, 0xa6, 0x0b, 0xb9, 0xd5, + 0xc7, 0xe9, 0x1a, 0x24, 0xe8, 0x2b, 0xf2, 0x1f, + 0x17, 0xc3, 0xe6, 0x14, 0x76, 0x71, 0x11, 0x76, + 0x8f, 0xfc, 0x66, 0xb8, 0x8c, 0xe2, 0xf5, 0x46, + 0x89, 0x0d, 0xd7, 0xe3, 0x56, 0x19, 0xd7, 0x1a, + 0xfe, 0x1c, 0xd4, 0x0f, 0x8b, 0x72, 0xd1, 0x20, + 0xb0, 0xa4, 0xbe, 0x6b, 0x6b, 0x01, 0x57, 0xe8, + 0x6b, 0xdf, 0x89, 0x55, 0x3f, 0x10, 0x41, 0x63, + 0xb0, 0x62, 0xa5, 0x7f, 0x4f, 0xe7, 0x42, 0x54, + 0xe4, 0x0e, 0x55, 0xae, 0xa2, 0x53, 0x3d, 0xb8, + 0x4a, 0xff, 0xeb, 0xe2, 0x8a, 0x71, 0x17, 0x54, + 0x20, 0x05, 0x51, 0xed, 0xae, 0xb0, 0xca, 0x7e, + 0xc6, 0xd7, 0x09, 0xa8, 0x39, 0x88, 0xac, 0x7f, + 0x4b, 0xe0, 0x49, 0xd5, 0x6c, 0x89, 0xf0, 0xbc, + 0xe4, 0xe9, 0xb0, 0x29, 0x73, 0x6c, 0x55, 0x7d, + 0x8f, 0xbe, 0x32, 0x31, 0x14, 0xd2, 0xec, 0x1b, + 0x02, 0x81, 0x80, 0x67, 0xf6, 0x55, 0x5a, 0xa3, + 0xaa, 0xf0, 0x82, 0x75, 0x2a, 0x41, 0xe5, 0x58, + 0x2c, 0x65, 0xaa, 0x32, 0x14, 0xe4, 0x04, 0xf3, + 0xef, 0x33, 0x69, 0x75, 0x1e, 0xf3, 0x25, 0x73, + 0xc3, 0x67, 0xe1, 0x77, 0x8a, 0xed, 0x43, 0xe0, + 0x13, 0x99, 0xfb, 0x39, 0xf2, 0x0d, 0x65, 0xed, + 0x93, 0x33, 0xd1, 0x51, 0x01, 0x58, 0x66, 0x1d, + 0x32, 0xd9, 0xac, 0xf8, 0x1e, 0x17, 0xd9, 0x2c, + 0x58, 0x63, 0xed, 0xb7, 0x1d, 0x6d, 0x37, 0xe7, + 0x3b, 0x8f, 0x51, 0x36, 0x74, 0xc0, 0xf7, 0xa1, + 0x05, 0x39, 0x9f, 0x34, 0x2d, 0x11, 0x1c, 0x0e, + 0xd7, 0x70, 0x6f, 0x22, 0xb6, 0x61, 0x37, 0x3e, + 0x90, 0xeb, 0xe4, 0x7a, 0x44, 0x85, 0xc6, 0xf0, + 0x53, 0xaa, 0xd5, 0x1f, 0x4a, 0x3f, 0x25, 0x42, + 0x81, 0xb0, 0x34, 0x10, 0x29, 0xe0, 0xb9, 0x12, + 0xd8, 0xd4, 0xf9, 0x1f, 0x2d, 0x0a, 0x64, 0xf4, + 0x55, 0x5e, 0xf7, 0x02, 0x81, 0x80, 0x3d, 0x06, + 0x1f, 0x63, 0x88, 0x3f, 0x0d, 0xcb, 0xdf, 0x30, + 0x40, 0xda, 0x4b, 0x03, 0xa1, 0x8a, 0xdb, 0x32, + 0x31, 0x5d, 0x1c, 0x9d, 0x81, 0xf9, 0x8a, 0x43, + 0xd7, 0x12, 0x85, 0x83, 0xf9, 0x1d, 0xc1, 0x77, + 0x75, 0x3d, 0x5f, 0x85, 0x1a, 0xd1, 0x63, 0x50, + 0x45, 0x0b, 0xb1, 0x30, 0x40, 0xb2, 0x13, 0x44, + 0xaf, 0x9b, 0x6c, 0xe8, 0x36, 0x1a, 0x33, 0xb6, + 0x92, 0x5c, 0xdc, 0x0a, 0x8a, 0xce, 0x22, 0x0c, + 0x0f, 0xc2, 0xd5, 0x71, 0xf7, 0xc9, 0xc2, 0x4c, + 0x53, 0x8c, 0xcb, 0x25, 0x6b, 0x86, 0xf4, 0x8f, + 0x3d, 0x2e, 0x78, 0x35, 0x48, 0x34, 0xfc, 0xe1, + 0xaa, 0xe4, 0x71, 0xfe, 0xc0, 0x83, 0x42, 0x0b, + 0x97, 0x0d, 0xa9, 0x19, 0x45, 0xd3, 0x36, 0x20, + 0xcb, 0xd8, 0x84, 0xb5, 0x47, 0x1a, 0xff, 0x7f, + 0x57, 0x39, 0x0e, 0x99, 0x1e, 0xe0, 0xba, 0xe1, + 0x4b, 0x6c, 0xca, 0x35, 0xf7, 0x11, 0x02, 0x81, + 0x80, 0x5a, 0x59, 0xf0, 0x8e, 0x07, 0x9c, 0x1a, + 0x83, 0x2b, 0x28, 0xc4, 0x82, 0xcc, 0xb7, 0x9e, + 0x41, 0xa5, 0x15, 0xa2, 0x37, 0xa7, 0x0c, 0x77, + 0x09, 0x73, 0xf5, 0xb1, 0x3f, 0x7a, 0x55, 0x9f, + 0x15, 0x90, 0xa3, 0x5e, 0x6e, 0x19, 0x46, 0x6d, + 0x1f, 0x28, 0x64, 0xad, 0xa0, 0xb5, 0xca, 0x7d, + 0x06, 0x44, 0x88, 0xae, 0x2b, 0x80, 0x21, 0x84, + 0x3d, 0x8d, 0xa5, 0x09, 0x4f, 0xa1, 0xd9, 0x7d, + 0x7c, 0x9a, 0x8b, 0x64, 0x8f, 0xf6, 0xa5, 0xce, + 0x56, 0x65, 0xba, 0xcb, 0x30, 0x38, 0x53, 0xf8, + 0x1b, 0x89, 0x8f, 0xdf, 0xb5, 0xc3, 0x86, 0x3c, + 0x24, 0xef, 0xbc, 0xc0, 0xfc, 0x6c, 0xa4, 0xc6, + 0x74, 0xbb, 0xeb, 0x79, 0xa9, 0x8e, 0xe5, 0x25, + 0xc0, 0x0a, 0xe5, 0x90, 0x08, 0x43, 0x82, 0xec, + 0x17, 0x5e, 0x9e, 0xa2, 0x05, 0xc5, 0x03, 0x20, + 0x12, 0xf7, 0x86, 0x2e, 0x7e, 0x8e, 0x11, 0xf7, + 0x14 +}; + +const size_t kTestOemPrivateKeySize = sizeof(kTestOemPrivateKey); + +// OEM Certificate public cert data. +// Leaf (device) certificate: +// version: 2 +// serialNumber: 61412119066269531030714093834591281593 +// signature algorithm: sha256WithRSAEncryption (1.2.840.113549.1.1.11) +// issuer: +// C=US, ST=WA, L=Kirkland, O=Google, OU=Widevine, CN=system id: 7913 +// validity: +// notBefore: Jan 27 15:55:47 2021 GMT +// notAfter: Jan 25 15:55:47 2031 GMT +// subject: +// CN=7913-leaf, C=US, ST=WA, L=Kirkland, O=Google, OU=Widevine +// key: RSA Public key +// extensions: +// - object: Widevine System Id (1.3.6.1.4.1.11129.4.1.1) +// critical: BOOL ABSENT +// value: 02 02 1e e9 (7913) +// sig_alg: sha256WithRSAEncryption (1.2.840.113549.1.1.11) +// signature: ... +// +// Intermediate (manufacturer) certificate: +// version: 2 +// serialNumber: 4911737327617104025470773024969385060 +// signature: sha256WithRSAEncryption (1.2.840.113549.1.1.11) +// issuer: +// C=US, ST=Washington, L=Kirkland, O=Google, OU=Widevine, +// CN=widevine.com/oem-root-prod +// validity: +// notBefore: Nov 18 01:13:35 2017 GMT +// notAfter: Nov 18 01:13:13 2027 GMT +// subject: +// C=US, ST=WA, L=Kirkland, O=Google, OU=Widevine, CN=system id: 7913 +// key: RSA Public key +// extensions: +// - object: X509v3 Basic Constraints (2.5.29.19) +// critical: TRUE +// value: 30 06 01 01 ff 02 01 00 +// - object: X509v3 Key Usage (2.5.29.15) +// critical: TRUE +// value: 03 02 02 04 +// - object: X509v3 Subject Key Identifier (2.5.29.14) +// critical: BOOL ABSENT +// value: +// 04 14 4b cb df aa 02 de 8d c3 e7 e5 85 db 2e 8a be 75 6b 8a 67-58 +// - object: X509v3 Authority Key Identifier (2.5.29.35) +// critical: BOOL ABSENT +// value: ... +// - object: Widevine System Id (1.3.6.1.4.1.11129.4.1.1) +// critical: BOOL ABSENT +// value: 02 02 1e e9 (7913) +// sig_alg: sha256WithRSAEncryption (1.2.840.113549.1.1.11) +// signature: ... +const uint8_t kTestOemPublicCert[] = { + 0x30, 0x82, 0x09, 0x28, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, + 0x82, 0x09, 0x19, 0x30, 0x82, 0x09, 0x15, 0x02, + 0x01, 0x01, 0x31, 0x00, 0x30, 0x0b, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, + 0x01, 0xa0, 0x82, 0x08, 0xfd, 0x30, 0x82, 0x03, + 0x70, 0x30, 0x82, 0x02, 0x58, 0xa0, 0x03, 0x02, + 0x01, 0x02, 0x02, 0x10, 0x2e, 0x33, 0x8b, 0x3d, + 0x69, 0x18, 0x4c, 0xf7, 0x78, 0x2e, 0x3b, 0x3b, + 0x43, 0xb4, 0x21, 0xb9, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x30, 0x6b, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x0c, 0x02, 0x57, 0x41, 0x31, + 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, + 0x0c, 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, + 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, + 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57, 0x69, + 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x18, + 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, + 0x0f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20, + 0x69, 0x64, 0x3a, 0x20, 0x37, 0x39, 0x31, 0x33, + 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x31, + 0x32, 0x37, 0x31, 0x35, 0x35, 0x35, 0x34, 0x37, + 0x5a, 0x17, 0x0d, 0x33, 0x31, 0x30, 0x31, 0x32, + 0x35, 0x31, 0x35, 0x35, 0x35, 0x34, 0x37, 0x5a, + 0x30, 0x65, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x0c, 0x09, 0x37, 0x39, 0x31, + 0x33, 0x2d, 0x6c, 0x65, 0x61, 0x66, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, 0x57, 0x41, + 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, + 0x07, 0x0c, 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, + 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, + 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57, + 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x30, + 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, + 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xa8, 0x75, 0xd5, 0x3d, 0xd3, 0xf3, 0x59, 0xd1, + 0x63, 0x0f, 0x5d, 0x5f, 0x2c, 0xaf, 0x80, 0x4e, + 0x9a, 0xef, 0x9a, 0x8f, 0x88, 0x37, 0xe5, 0x56, + 0xf8, 0x66, 0xcb, 0xa6, 0x61, 0xab, 0xc1, 0xa9, + 0x04, 0xc2, 0x12, 0x9d, 0xa6, 0x0c, 0x2c, 0xcb, + 0x42, 0x09, 0xd0, 0x36, 0xf0, 0x85, 0x01, 0xdf, + 0xd5, 0xbd, 0xaf, 0x82, 0xbb, 0x25, 0xa1, 0x61, + 0x17, 0xfe, 0xa1, 0x65, 0x34, 0xda, 0x91, 0xee, + 0x91, 0x46, 0xd6, 0x63, 0x47, 0x6a, 0xa5, 0x32, + 0x62, 0xe2, 0x4c, 0x7c, 0xf7, 0x76, 0x59, 0xe1, + 0x2b, 0x47, 0x8d, 0x1c, 0xe6, 0xa0, 0xbd, 0xc3, + 0xc9, 0x36, 0x0e, 0x90, 0x75, 0xba, 0x1c, 0xbd, + 0xca, 0x85, 0x0a, 0x4e, 0xcc, 0xfe, 0x91, 0x3f, + 0x22, 0x42, 0x96, 0xae, 0xa0, 0x87, 0x82, 0x63, + 0x3b, 0x22, 0x54, 0xbc, 0x28, 0xa3, 0x45, 0x4b, + 0x34, 0x12, 0x4e, 0xeb, 0x04, 0x4d, 0x29, 0xb3, + 0x05, 0x62, 0x0d, 0x51, 0x16, 0x46, 0x98, 0x21, + 0xc8, 0x59, 0x96, 0x53, 0x43, 0x38, 0x95, 0x1a, + 0x42, 0x94, 0x8b, 0x49, 0x5d, 0x1b, 0x8a, 0xd5, + 0x82, 0x6a, 0x32, 0x6f, 0x0e, 0x5a, 0x85, 0x3b, + 0xd5, 0x42, 0xc0, 0x4c, 0x34, 0x43, 0x7c, 0x4f, + 0xef, 0xca, 0x02, 0x99, 0x38, 0xf9, 0xa5, 0xc0, + 0xd8, 0x1d, 0xe7, 0x9e, 0x8c, 0x4d, 0x9c, 0x40, + 0xcd, 0x4b, 0x5e, 0x44, 0x37, 0x8d, 0xc5, 0x96, + 0xd4, 0xf7, 0xb3, 0x37, 0x4c, 0x2e, 0x2d, 0x30, + 0xca, 0x97, 0x39, 0xed, 0xe8, 0x73, 0xc5, 0xe7, + 0xcb, 0x95, 0xf0, 0x84, 0x4a, 0x5a, 0x9e, 0x13, + 0x19, 0x5f, 0x98, 0xe5, 0xbe, 0x31, 0x5b, 0xff, + 0xed, 0x29, 0x26, 0xc4, 0x93, 0x54, 0x49, 0x84, + 0xd2, 0xeb, 0x21, 0x40, 0x19, 0x5f, 0xf7, 0x32, + 0x67, 0x93, 0xe0, 0xda, 0x77, 0xfc, 0xda, 0x5e, + 0xc4, 0x5b, 0x95, 0x2e, 0x46, 0xf3, 0xce, 0xfd, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x16, 0x30, + 0x14, 0x30, 0x12, 0x06, 0x0a, 0x2b, 0x06, 0x01, + 0x04, 0x01, 0xd6, 0x79, 0x04, 0x01, 0x01, 0x04, + 0x04, 0x02, 0x02, 0x1e, 0xe9, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, + 0x00, 0x04, 0x9e, 0xbb, 0x41, 0x3e, 0x1b, 0x35, + 0xca, 0x24, 0xe3, 0xa7, 0x62, 0xf7, 0xbf, 0x54, + 0x88, 0x1d, 0xa3, 0x6c, 0x30, 0x1f, 0xb7, 0xe4, + 0x2b, 0x76, 0x54, 0x4f, 0xb1, 0x30, 0xba, 0x86, + 0x21, 0x12, 0xc3, 0xd9, 0x25, 0xfd, 0x94, 0xab, + 0x3c, 0x9d, 0x4d, 0xbc, 0x2b, 0xdc, 0x68, 0x01, + 0x8d, 0x59, 0x4f, 0x81, 0xe8, 0x87, 0xbe, 0x05, + 0x75, 0x3e, 0x24, 0xd9, 0xf1, 0x55, 0xe3, 0x56, + 0x75, 0xa4, 0x7f, 0xb4, 0x50, 0x41, 0x4b, 0x88, + 0x0e, 0x8f, 0x6e, 0x10, 0x13, 0x25, 0x24, 0xda, + 0x4c, 0x97, 0x0a, 0xdc, 0xbf, 0x2a, 0x1d, 0x4d, + 0x02, 0xb3, 0x4b, 0x6a, 0x96, 0xe6, 0x0e, 0xd1, + 0x61, 0x80, 0x78, 0xab, 0xf0, 0x7b, 0x8b, 0x1e, + 0x99, 0x48, 0x84, 0x31, 0xd2, 0xaf, 0x23, 0x32, + 0x01, 0x5e, 0x11, 0x55, 0x06, 0x6e, 0x0e, 0xed, + 0x69, 0x7b, 0xb8, 0x41, 0x63, 0x3b, 0x30, 0x05, + 0x1e, 0xcd, 0x3e, 0x49, 0xb8, 0x0c, 0x83, 0x50, + 0x26, 0x4e, 0xd8, 0x97, 0x9d, 0x89, 0x30, 0x15, + 0xb9, 0x20, 0x20, 0xba, 0x5d, 0x8b, 0xad, 0x3d, + 0x7f, 0x52, 0xcd, 0x1e, 0xc0, 0x0e, 0xe1, 0xdc, + 0x21, 0x9b, 0xb0, 0x89, 0xa0, 0x20, 0xc6, 0x8c, + 0x56, 0x39, 0xc3, 0x40, 0x30, 0x40, 0x52, 0x2f, + 0x3b, 0x87, 0x96, 0xe2, 0x4d, 0x80, 0x57, 0xb3, + 0xb3, 0xe0, 0xb7, 0x5c, 0x55, 0x6a, 0x0f, 0x44, + 0x91, 0xe8, 0x98, 0xb3, 0xb4, 0xd3, 0x07, 0x8e, + 0x85, 0x8e, 0xc0, 0xcb, 0x85, 0x92, 0x0e, 0xca, + 0x7c, 0x03, 0x23, 0xa0, 0x1f, 0x4e, 0x75, 0x01, + 0x95, 0x61, 0x2c, 0x21, 0x2f, 0x4f, 0x6d, 0x55, + 0xa3, 0xcc, 0x23, 0x62, 0x7f, 0xcb, 0x0a, 0x8a, + 0x8b, 0x82, 0xb5, 0x8c, 0x8a, 0xaa, 0x88, 0xb4, + 0x65, 0x82, 0x6d, 0x12, 0x4a, 0x06, 0xe7, 0xde, + 0xcd, 0x3c, 0xf9, 0x1c, 0x02, 0x9c, 0xf8, 0x21, + 0x20, 0x30, 0x82, 0x05, 0x85, 0x30, 0x82, 0x03, + 0x6d, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, + 0x03, 0xb1, 0xf7, 0x58, 0xdf, 0x1d, 0xe3, 0x25, + 0x00, 0x0b, 0x10, 0x3d, 0xd5, 0xe6, 0xe4, 0x64, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, + 0x7e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, + 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, + 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, + 0x74, 0x6f, 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, + 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, 0x69, + 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, + 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, + 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31, + 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x0c, 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, + 0x6e, 0x65, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x0c, 0x1a, 0x77, 0x69, 0x64, + 0x65, 0x76, 0x69, 0x6e, 0x65, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x6f, 0x65, 0x6d, 0x2d, 0x72, 0x6f, + 0x6f, 0x74, 0x2d, 0x70, 0x72, 0x6f, 0x64, 0x30, + 0x1e, 0x17, 0x0d, 0x31, 0x37, 0x31, 0x31, 0x31, + 0x38, 0x30, 0x31, 0x31, 0x33, 0x33, 0x35, 0x5a, + 0x17, 0x0d, 0x32, 0x37, 0x31, 0x31, 0x31, 0x38, + 0x30, 0x31, 0x31, 0x33, 0x31, 0x33, 0x5a, 0x30, + 0x6b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, + 0x02, 0x57, 0x41, 0x31, 0x11, 0x30, 0x0f, 0x06, + 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, 0x69, + 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, + 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, + 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31, + 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x0c, 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, + 0x6e, 0x65, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x0c, 0x0f, 0x73, 0x79, 0x73, + 0x74, 0x65, 0x6d, 0x20, 0x69, 0x64, 0x3a, 0x20, + 0x37, 0x39, 0x31, 0x33, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xae, 0xc8, 0x71, + 0xae, 0x08, 0x0c, 0x06, 0x06, 0x2d, 0x81, 0x7c, + 0xa9, 0x8b, 0xb3, 0xd6, 0x66, 0xe4, 0xf6, 0x08, + 0x5e, 0x5a, 0x75, 0xe8, 0x74, 0x61, 0x7a, 0x88, + 0xca, 0x85, 0x14, 0x0d, 0x58, 0xa4, 0x09, 0x19, + 0x6c, 0x60, 0xc9, 0xad, 0x91, 0x1c, 0xbf, 0x04, + 0xb3, 0x47, 0x10, 0x63, 0x7f, 0x02, 0x58, 0xc2, + 0x1e, 0xbd, 0xcc, 0x07, 0x77, 0xaa, 0x7e, 0x14, + 0xa8, 0xc2, 0x01, 0xcd, 0xe8, 0x46, 0x60, 0x53, + 0x6f, 0x2f, 0xda, 0x17, 0x2d, 0x4d, 0x9d, 0x0e, + 0x5d, 0xb5, 0x50, 0x95, 0xae, 0xab, 0x6e, 0x43, + 0xe3, 0xb0, 0x00, 0x12, 0xb4, 0x05, 0x82, 0x4a, + 0x2b, 0x14, 0x63, 0x0d, 0x1f, 0x06, 0x12, 0xaa, + 0xe1, 0x9d, 0xe7, 0xba, 0xda, 0xe3, 0xfc, 0x7c, + 0x6c, 0x73, 0xae, 0x56, 0xf8, 0xab, 0xf7, 0x51, + 0x93, 0x31, 0xef, 0x8f, 0xe4, 0xb6, 0x01, 0x2c, + 0xeb, 0x7b, 0xe4, 0xd8, 0xb3, 0xea, 0x70, 0x37, + 0x89, 0x05, 0xa9, 0x51, 0x57, 0x72, 0x98, 0x9e, + 0xa8, 0x46, 0xdb, 0xeb, 0x7a, 0x38, 0x2b, 0x2f, + 0xc0, 0x27, 0xb7, 0xc2, 0xe1, 0x9a, 0x17, 0xdf, + 0xf5, 0xd6, 0x9c, 0xd5, 0x8c, 0xb8, 0x66, 0x42, + 0xd5, 0x04, 0x1e, 0x7c, 0x36, 0x4c, 0x1e, 0x3e, + 0x45, 0x51, 0x4d, 0x41, 0x72, 0x22, 0x53, 0x3d, + 0xf4, 0x57, 0x7c, 0x6c, 0x33, 0x34, 0x24, 0x45, + 0xdf, 0x84, 0x87, 0x4a, 0xa6, 0xcb, 0x7c, 0x03, + 0xa3, 0xaa, 0x8e, 0x2d, 0x82, 0x01, 0x27, 0x87, + 0x74, 0x82, 0x1a, 0xbc, 0x0f, 0x76, 0x69, 0xab, + 0xe0, 0x4e, 0x70, 0xbe, 0x37, 0xfc, 0xc8, 0x2c, + 0x91, 0x17, 0x4f, 0xd5, 0x26, 0x3b, 0x7b, 0x90, + 0xb5, 0x2d, 0x64, 0xba, 0xf7, 0xd2, 0x8a, 0xb4, + 0x8f, 0x38, 0x9d, 0x8e, 0xba, 0xe7, 0x5c, 0x52, + 0xf1, 0x0a, 0xb8, 0xc0, 0x1b, 0xb6, 0xb1, 0x70, + 0x7e, 0x47, 0x59, 0x94, 0x59, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x82, 0x01, 0x10, 0x30, 0x82, + 0x01, 0x0c, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, + 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, + 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, + 0x04, 0x04, 0x03, 0x02, 0x02, 0x04, 0x30, 0x1d, + 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, + 0x14, 0x4b, 0xcb, 0xdf, 0xaa, 0x02, 0xde, 0x8d, + 0xc3, 0xe7, 0xe5, 0x85, 0xdb, 0x2e, 0x8a, 0xbe, + 0x75, 0x6b, 0x8a, 0x67, 0x58, 0x30, 0x81, 0xb2, + 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x81, 0xaa, + 0x30, 0x81, 0xa7, 0x80, 0x14, 0x04, 0x94, 0x66, + 0xaa, 0xf9, 0x61, 0x89, 0xb6, 0xdb, 0xb5, 0xf7, + 0x13, 0x38, 0x3d, 0x62, 0x84, 0xb8, 0x18, 0x0a, + 0x8f, 0xa1, 0x81, 0x83, 0xa4, 0x81, 0x80, 0x30, + 0x7e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, + 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, + 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, + 0x74, 0x6f, 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, + 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, 0x69, + 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, + 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, + 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31, + 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x0c, 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, + 0x6e, 0x65, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x0c, 0x1a, 0x77, 0x69, 0x64, + 0x65, 0x76, 0x69, 0x6e, 0x65, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x6f, 0x65, 0x6d, 0x2d, 0x72, 0x6f, + 0x6f, 0x74, 0x2d, 0x70, 0x72, 0x6f, 0x64, 0x82, + 0x09, 0x00, 0xdf, 0x86, 0x05, 0x31, 0x01, 0xbe, + 0x9a, 0x9a, 0x30, 0x12, 0x06, 0x0a, 0x2b, 0x06, + 0x01, 0x04, 0x01, 0xd6, 0x79, 0x04, 0x01, 0x01, + 0x04, 0x04, 0x02, 0x02, 0x1e, 0xe9, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, + 0x01, 0x00, 0x61, 0x3f, 0x2f, 0x43, 0xe4, 0xbe, + 0x66, 0x34, 0xef, 0x92, 0x06, 0xe9, 0x88, 0xba, + 0x6a, 0x1d, 0x4f, 0x54, 0x5a, 0x97, 0xb1, 0x75, + 0xd7, 0x93, 0xf8, 0x45, 0xc6, 0x83, 0x92, 0x36, + 0xfd, 0x55, 0xa9, 0x21, 0x0b, 0xdc, 0xf6, 0xae, + 0x11, 0xdc, 0x62, 0x21, 0x44, 0xbd, 0x04, 0x1d, + 0x58, 0x2c, 0x03, 0xf8, 0xe4, 0xe2, 0x1e, 0xba, + 0xe6, 0xdd, 0x19, 0xdd, 0x56, 0xfd, 0xce, 0x06, + 0x73, 0x5f, 0x94, 0x1e, 0xb6, 0x03, 0xdb, 0x3d, + 0x7b, 0xab, 0xab, 0x72, 0x64, 0x7b, 0xde, 0x7d, + 0x4d, 0xcf, 0x7e, 0xf0, 0x91, 0x29, 0xc1, 0x77, + 0x13, 0xc2, 0x6f, 0x80, 0xab, 0x7a, 0xa8, 0xce, + 0xb0, 0x1c, 0x2a, 0xc5, 0x9c, 0xfb, 0x0b, 0xe5, + 0x9f, 0x9c, 0x1b, 0xc9, 0x4b, 0x58, 0xdf, 0x96, + 0x18, 0xf7, 0x67, 0x67, 0x89, 0xa4, 0xe9, 0x14, + 0x48, 0xac, 0xfa, 0x9d, 0x86, 0x2a, 0xeb, 0x75, + 0x2c, 0x2b, 0xbf, 0x63, 0x7d, 0xc7, 0x4e, 0x7e, + 0xad, 0x39, 0x2d, 0xb4, 0x7c, 0x07, 0xa5, 0x5a, + 0xe8, 0x3a, 0xd4, 0xf5, 0x0c, 0x4f, 0xf3, 0xa2, + 0x9c, 0x3c, 0x32, 0xed, 0x9d, 0x4b, 0x49, 0x05, + 0xbc, 0x1f, 0xa0, 0x13, 0xe6, 0xdd, 0x82, 0x79, + 0x06, 0x31, 0x3b, 0xc6, 0x97, 0xec, 0x8d, 0xaa, + 0x4f, 0xef, 0x14, 0x3c, 0x21, 0xf6, 0x72, 0xb2, + 0x09, 0x42, 0xc7, 0x74, 0xfe, 0xef, 0x70, 0xbd, + 0xe9, 0x85, 0x41, 0x30, 0x0b, 0xb3, 0x6b, 0x59, + 0x0c, 0x0f, 0x11, 0x75, 0xd4, 0xbb, 0xb1, 0xdf, + 0xb1, 0xdf, 0xb3, 0xfa, 0xb3, 0x3a, 0x43, 0x17, + 0x7d, 0x8a, 0x82, 0xae, 0xa2, 0x07, 0xf8, 0x83, + 0x51, 0xfb, 0x16, 0xfb, 0x64, 0xb6, 0x46, 0xda, + 0xbe, 0x32, 0x2b, 0xc0, 0xee, 0x78, 0x2a, 0x84, + 0xa9, 0x54, 0x0a, 0xf9, 0x2d, 0x61, 0x65, 0xde, + 0xa5, 0x97, 0x66, 0x79, 0x02, 0xf8, 0x97, 0x17, + 0xe2, 0xd4, 0x9f, 0x9e, 0xac, 0xcc, 0xae, 0x99, + 0x9a, 0x03, 0x04, 0xbb, 0x45, 0xfe, 0xb2, 0xf5, + 0x80, 0xba, 0xbf, 0xdd, 0x24, 0xe5, 0xe6, 0x1e, + 0x5d, 0x36, 0xa5, 0x87, 0x0c, 0xdf, 0x60, 0x81, + 0x6f, 0xb7, 0x5f, 0xb9, 0x1f, 0xca, 0x75, 0x3c, + 0x1a, 0x63, 0xb0, 0xeb, 0xe6, 0x95, 0x86, 0x0d, + 0xae, 0xa6, 0xc9, 0x2a, 0x94, 0xf1, 0xd0, 0xbe, + 0x75, 0xc8, 0xf8, 0x07, 0xd7, 0x88, 0xff, 0xec, + 0xf9, 0xcd, 0x49, 0xc6, 0xfe, 0x4d, 0x7f, 0x44, + 0x1e, 0xd8, 0xaf, 0xa9, 0x72, 0x27, 0x98, 0xe2, + 0x5a, 0x08, 0xea, 0x55, 0xd3, 0xb3, 0xea, 0xdc, + 0x76, 0x69, 0x51, 0x10, 0x01, 0x46, 0x7d, 0x33, + 0x94, 0x9c, 0x94, 0xef, 0xfe, 0x76, 0x1c, 0xc6, + 0xd7, 0x15, 0x53, 0x3e, 0x8d, 0x3d, 0x29, 0x9a, + 0x58, 0x6a, 0xf1, 0x75, 0x9e, 0xea, 0x1b, 0x4c, + 0xf0, 0x47, 0x76, 0xac, 0xc6, 0xa2, 0x32, 0x44, + 0x40, 0xdf, 0xfe, 0xff, 0x9d, 0xf4, 0xe2, 0xc2, + 0xfa, 0xa1, 0x5f, 0x2e, 0x66, 0xe9, 0x97, 0xcb, + 0x27, 0x26, 0x6e, 0x53, 0xe4, 0xe8, 0x86, 0x2c, + 0xea, 0xd3, 0x69, 0x6c, 0x61, 0x4f, 0xfe, 0xc1, + 0xc9, 0x8b, 0x05, 0x92, 0x6f, 0x47, 0x96, 0xce, + 0xf0, 0x33, 0xfa, 0x7c, 0x78, 0x24, 0x9b, 0xd7, + 0x8d, 0x36, 0x56, 0x37, 0x86, 0xbc, 0x72, 0x5a, + 0xf9, 0xb9, 0xb0, 0x93, 0xf0, 0x81, 0x78, 0x10, + 0xf2, 0xb0, 0xc2, 0x79, 0x91, 0x5e, 0xcf, 0xbc, + 0x8c, 0xf2, 0x32, 0x0f, 0xf7, 0x2d, 0x30, 0xd8, + 0x13, 0x77, 0x4f, 0x78, 0x9e, 0x40, 0x8d, 0xe6, + 0x3a, 0x98, 0xb2, 0xaa, 0x13, 0x4d, 0x25, 0x49, + 0x34, 0x6c, 0x80, 0x9e, 0x19, 0x03, 0xdb, 0xcd, + 0xf5, 0xb1, 0x54, 0x74, 0x1b, 0x67, 0x3c, 0x46, + 0xac, 0x3e, 0x5d, 0xa2, 0xd9, 0x13, 0x83, 0x30, + 0xeb, 0x82, 0x3b, 0x06, 0xab, 0x3c, 0x39, 0x7d, + 0xd0, 0x68, 0x31, 0x00 +}; +// clang-format on +} // namespace +const size_t kTestOemPublicCertSize = sizeof(kTestOemPublicCert); + +const uint32_t kOEMSystemId = kTestOemSystemId; + +const uint8_t* kOEMPrivateKey = kTestOemPrivateKey; +const uint8_t* kOEMPublicCert = kTestOemPublicCert; + +const size_t kOEMPrivateKeySize = kTestOemPrivateKeySize; +const size_t kOEMPublicCertSize = kTestOemPublicCertSize; +} // namespace wvoec_ref diff --git a/oemcrypto/util/test/oemcrypto_ecc_key_unittest.cpp b/oemcrypto/util/test/oemcrypto_ecc_key_unittest.cpp new file mode 100644 index 0000000..12838e5 --- /dev/null +++ b/oemcrypto/util/test/oemcrypto_ecc_key_unittest.cpp @@ -0,0 +1,258 @@ +// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. +// +// Reference implementation utilities of OEMCrypto APIs +// +#include + +#include "OEMCryptoCENCCommon.h" +#include "oemcrypto_ecc_key.h" +#include "oemcrypto_ref_test_utils.h" + +namespace wvoec { +namespace util { +constexpr size_t kMessageSize = 4 * 1024; // 4 kB + +class OEMCryptoEccKeyTest : public ::testing::TestWithParam { + public: + void SetUp() override { + key_ = EccPrivateKey::New(GetParam()); + ASSERT_TRUE(key_) << "Key initialization failed: key = " + << EccCurveToString(GetParam()); + } + + void TearDown() override { key_.reset(); } + + protected: + std::unique_ptr key_; +}; + +// Basic verification of ECC private key generation. +TEST_P(OEMCryptoEccKeyTest, KeyProperties) { + const EccCurve expected_curve = GetParam(); + EXPECT_EQ(key_->curve(), expected_curve); + EXPECT_NE(nullptr, key_->GetEcKey()); +} + +// Checks that the private key serialization APIs are compatible +// and performing in a manner that is similar to other OEMCrypto methods +// that retrieve data. +TEST_P(OEMCryptoEccKeyTest, SerializePrivateKey) { + constexpr size_t kInitialBufferSize = 10; // Definitely too small. + size_t buffer_size = kInitialBufferSize; + std::vector buffer(buffer_size); + + EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + key_->Serialize(buffer.data(), &buffer_size)); + EXPECT_GT(buffer_size, kInitialBufferSize); + + buffer.resize(buffer_size); + EXPECT_EQ(OEMCrypto_SUCCESS, key_->Serialize(buffer.data(), &buffer_size)); + buffer.resize(buffer_size); + + const std::vector direct_key_data = key_->Serialize(); + EXPECT_FALSE(direct_key_data.empty()); + ASSERT_EQ(buffer.size(), direct_key_data.size()); + for (size_t i = 0; i < buffer.size(); i++) { + ASSERT_EQ(buffer[i], direct_key_data[i]) << "i = " << i; + } +} + +// Checks that a private key that is serialized can be deserialized and +// reload. Also checks that the serialization of a key produces the +// same data to ensure consistency. +TEST_P(OEMCryptoEccKeyTest, SerializeAndReloadPrivateKey) { + const std::vector key_data = key_->Serialize(); + std::unique_ptr loaded_key = EccPrivateKey::Load(key_data); + ASSERT_TRUE(loaded_key); + + EXPECT_EQ(key_->curve(), loaded_key->curve()); + + const std::vector loaded_key_data = loaded_key->Serialize(); + ASSERT_EQ(key_data.size(), loaded_key_data.size()); + for (size_t i = 0; i < key_data.size(); i++) { + ASSERT_EQ(key_data[i], loaded_key_data[i]) << "i = " << i; + } +} + +// Checks that a private key can be serialized as a public key, and +// that the serialized public key and be reloaded. +TEST_P(OEMCryptoEccKeyTest, SerializePrivateKeyAsPublicKey) { + const std::vector key_data = key_->SerializeAsPublicKey(); + ASSERT_FALSE(key_data.empty()) << "Failed to serialize as public key"; + + auto loaded_key = EccPublicKey::Load(key_data); + ASSERT_TRUE(loaded_key) << "Failed to deserialize public key"; + EXPECT_TRUE(key_->IsMatchingPublicKey(*loaded_key)); + EXPECT_TRUE(loaded_key->IsMatchingPrivateKey(*key_)); +} + +// Checks that a public key can be initialized from a ASN.1 DER encoded +// PrivateKeyInfo message. +TEST_P(OEMCryptoEccKeyTest, SerializePrivateKeyAndReloadAsPublicKey) { + const std::vector key_data = key_->Serialize(); + ASSERT_FALSE(key_data.empty()) << "Failed to serialize as private key"; + + auto key_by_buffer = + EccPublicKey::LoadPrivateKeyInfo(key_data.data(), key_data.size()); + ASSERT_TRUE(key_by_buffer) + << "Failed to deserialize private key into public key"; + EXPECT_TRUE(key_->IsMatchingPublicKey(*key_by_buffer)); + key_by_buffer.reset(); + + auto key_by_vector = EccPublicKey::LoadPrivateKeyInfo(key_data); + ASSERT_TRUE(key_by_vector) + << "Failed to deserialize private key into public key"; + EXPECT_TRUE(key_->IsMatchingPublicKey(*key_by_vector)); + key_by_vector.reset(); + + const std::string key_data_str(key_data.begin(), key_data.end()); + auto key_by_string = EccPublicKey::LoadPrivateKeyInfo(key_data_str); + ASSERT_TRUE(key_by_string) + << "Failed to deserialize private key into public key"; + EXPECT_TRUE(key_->IsMatchingPublicKey(*key_by_string)); +} + +// Checks that a public key can be created from the private key. +TEST_P(OEMCryptoEccKeyTest, DerivePublicKey) { + std::unique_ptr pub_key = key_->MakePublicKey(); + ASSERT_TRUE(pub_key); + EXPECT_TRUE(key_->IsMatchingPublicKey(*pub_key)); +} + +// Checks that a public key that is serialized can be deserialized and +// reload. Also checks that the serialization of a key produces the +// same data to ensure consistency. +TEST_P(OEMCryptoEccKeyTest, SerializePublicKey) { + std::unique_ptr pub_key = key_->MakePublicKey(); + ASSERT_TRUE(pub_key); + + constexpr size_t kInitialBufferSize = 10; // Definitely too small. + size_t buffer_size = kInitialBufferSize; + std::vector buffer(buffer_size); + + EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + pub_key->Serialize(buffer.data(), &buffer_size)); + EXPECT_GT(buffer_size, kInitialBufferSize); + + buffer.resize(buffer_size); + EXPECT_EQ(OEMCrypto_SUCCESS, pub_key->Serialize(buffer.data(), &buffer_size)); + buffer.resize(buffer_size); + + const std::vector direct_key_data = pub_key->Serialize(); + EXPECT_FALSE(direct_key_data.empty()); + ASSERT_EQ(buffer.size(), direct_key_data.size()); + for (size_t i = 0; i < buffer.size(); i++) { + ASSERT_EQ(buffer[i], direct_key_data[i]) << "i = " << i; + } +} + +// Checks that a public key that is serialized can be deserialized and +// reload. Also checks that the serialization of a key produces the +// same data to ensure consistency. +// It is anticipated that OEMCrypto will need to parse the licensing +// server's ephemerial key when deriving the session key. +TEST_P(OEMCryptoEccKeyTest, SerializeAndReloadPublicKey) { + std::unique_ptr pub_key = key_->MakePublicKey(); + ASSERT_TRUE(pub_key); + + const std::vector key_data = pub_key->Serialize(); + + std::unique_ptr loaded_key = EccPublicKey::Load(key_data); + ASSERT_TRUE(loaded_key); + + EXPECT_EQ(pub_key->curve(), loaded_key->curve()); + + const std::vector loaded_key_data = loaded_key->Serialize(); + ASSERT_EQ(key_data.size(), loaded_key_data.size()); + for (size_t i = 0; i < key_data.size(); i++) { + ASSERT_EQ(key_data[i], loaded_key_data[i]) << "i = " << i; + } +} + +// Checks that the ECC signature generating API operates similar to +// existing signature generation functions. +TEST_P(OEMCryptoEccKeyTest, GenerateSignature) { + const std::vector message = RandomData(kMessageSize); + ASSERT_FALSE(message.empty()) << "CdmRandom failed"; + + constexpr size_t kInitialBufferSize = 10; // Definitely too small. + size_t signature_size = kInitialBufferSize; + std::vector signature(signature_size); + EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + key_->GenerateSignature(message.data(), message.size(), + signature.data(), &signature_size)); + EXPECT_GT(signature_size, kInitialBufferSize); + + signature.resize(signature_size); + EXPECT_EQ(OEMCrypto_SUCCESS, + key_->GenerateSignature(message.data(), message.size(), + signature.data(), &signature_size)); + signature.resize(signature_size); + + EXPECT_LE(signature_size, key_->SignatureSize()); +} + +// Checks that ECC signatures can be verified by an ECC public key. +TEST_P(OEMCryptoEccKeyTest, VerifySignature) { + const std::vector message = RandomData(kMessageSize); + ASSERT_FALSE(message.empty()) << "CdmRandom failed"; + + const std::vector signature = key_->GenerateSignature(message); + + std::unique_ptr pub_key = key_->MakePublicKey(); + ASSERT_TRUE(pub_key); + + EXPECT_EQ(OEMCrypto_SUCCESS, pub_key->VerifySignature(message, signature)); + + // Check with different message. + const std::vector message_two = RandomData(kMessageSize); + EXPECT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE, + pub_key->VerifySignature(message_two, signature)); + + // Check with bad signature. + const std::vector bad_signature = RandomData(signature.size()); + EXPECT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE, + pub_key->VerifySignature(message, bad_signature)); +} + +// Verifies the session key exchange protocol used by the licensing +// server. +TEST_P(OEMCryptoEccKeyTest, DeriveSessionKey) { + // Set up Alice. + EccPrivateKey* alice_private_key = key_.get(); + std::unique_ptr alice_public_key = + alice_private_key->MakePublicKey(); + ASSERT_TRUE(alice_public_key); + // Set up Bob. + std::unique_ptr bob_private_key = + EccPrivateKey::New(alice_private_key->curve()); + ASSERT_TRUE(bob_private_key); + std::unique_ptr bob_public_key = + bob_private_key->MakePublicKey(); + ASSERT_TRUE(bob_public_key); + + const size_t session_key_length = alice_private_key->SessionKeyLength(); + EXPECT_EQ(session_key_length, bob_private_key->SessionKeyLength()); + // From Alice's perspective. + const std::vector alice_session_key = + alice_private_key->DeriveSessionKey(*bob_public_key); + + // From Bob's perspective. + const std::vector bob_session_key = + bob_private_key->DeriveSessionKey(*alice_public_key); + + // Both should have the same session key. + ASSERT_EQ(session_key_length, alice_session_key.size()); + ASSERT_EQ(session_key_length, bob_session_key.size()); + for (size_t i = 0; i < session_key_length; i++) { + ASSERT_EQ(alice_session_key[i], bob_session_key[i]) << "i = " << i; + } +} + +INSTANTIATE_TEST_SUITE_P(AllCurves, OEMCryptoEccKeyTest, + ::testing::Values(kEccSecp256r1, kEccSecp384r1, + kEccSecp521r1)); +} // namespace util +} // namespace wvoec diff --git a/oemcrypto/util/test/oemcrypto_oem_cert_unittest.cpp b/oemcrypto/util/test/oemcrypto_oem_cert_unittest.cpp new file mode 100644 index 0000000..2f220e1 --- /dev/null +++ b/oemcrypto/util/test/oemcrypto_oem_cert_unittest.cpp @@ -0,0 +1,124 @@ +// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. +// +// Reference implementation utilities of OEMCrypto APIs +// +#include + +#include "OEMCryptoCENCCommon.h" +#include "oem_cert.h" +#include "oemcrypto_oem_cert.h" +#include "oemcrypto_rsa_key.h" + +namespace wvoec { +namespace util { +using wvoec_ref::kOEMPrivateKey; +using wvoec_ref::kOEMPrivateKeySize; +using wvoec_ref::kOEMPublicCert; +using wvoec_ref::kOEMPublicCertSize; +namespace { +const std::vector kOEMPrivateKeyVector(kOEMPrivateKey, + kOEMPrivateKey + + kOEMPrivateKeySize); + +const std::vector kOEMPublicCertVector(kOEMPublicCert, + kOEMPublicCert + + kOEMPublicCertSize); +} // namespace + +// Creates an OemCertificate wrapper around the built-in reference +// OEM cert. +// Creating the OemCertificate should succeed so long as the data +// is well-formed. +// Validating the OEM cert should succeed (assuming built-in cert+key +// are valid). +TEST(OEMCryptoOemCertTest, CreateFromArray) { + std::unique_ptr oem_cert = OemCertificate::Create( + kOEMPrivateKey, kOEMPrivateKeySize, kOEMPublicCert, kOEMPublicCertSize); + ASSERT_TRUE(oem_cert); + + EXPECT_EQ(OemCertificate::kRsa, oem_cert->key_type()); + + const std::vector private_key = oem_cert->GetPrivateKey(); + EXPECT_EQ(kOEMPrivateKeyVector, private_key); + + size_t public_cert_size = 10; + std::vector public_cert(public_cert_size, 0); + EXPECT_EQ( + OEMCrypto_ERROR_SHORT_BUFFER, + oem_cert->GetPublicCertificate(public_cert.data(), &public_cert_size)); + public_cert.resize(public_cert_size); + EXPECT_EQ(OEMCrypto_SUCCESS, oem_cert->GetPublicCertificate( + public_cert.data(), &public_cert_size)); + EXPECT_EQ(kOEMPublicCertSize, public_cert_size); + EXPECT_EQ(kOEMPublicCertVector, public_cert); + + EXPECT_EQ(OEMCrypto_SUCCESS, oem_cert->IsCertificateValid()); +} + +TEST(OEMCryptoOemCertTest, CreateFromVector) { + std::unique_ptr oem_cert = + OemCertificate::Create(kOEMPrivateKeyVector, kOEMPublicCertVector); + ASSERT_TRUE(oem_cert); + + EXPECT_EQ(OemCertificate::kRsa, oem_cert->key_type()); + + const std::vector private_key = oem_cert->GetPrivateKey(); + EXPECT_EQ(kOEMPrivateKeyVector, private_key); + + const std::vector public_cert = oem_cert->GetPublicCertificate(); + EXPECT_EQ(kOEMPublicCertVector, public_cert); + + EXPECT_EQ(OEMCrypto_SUCCESS, oem_cert->IsCertificateValid()); +} + +// Creation of OemCertificate wrapper should fail if the provided +// key is not well-formed. +TEST(OEMCryptoOemCertTest, CreateWithABadPrivateKey) { + static const uint8_t kBadPrivateKeyData[] = {'n', 'o', 't', ' ', 'a', ' ', + 'p', 'r', 'i', 'v', 'a', 't', + 'e', 'k', 'e', 'y'}; + std::unique_ptr oem_cert = + OemCertificate::Create(kBadPrivateKeyData, sizeof(kBadPrivateKeyData), + kOEMPublicCert, kOEMPublicCertSize); + EXPECT_FALSE(oem_cert); +} + +// Creation of OemCertificate wrapper should fail if the provided +// OEM Public Cert is not well-formed. +TEST(OEMCryptoOemCertTest, CreateWithABadPublicCert) { + static const uint8_t kBadPublicCert[] = {'n', 'o', 't', ' ', 'a', ' ', 'o', + 'e', 'm', ' ', 'p', 'u', 'b', 'l', + 'i', 'c', ' ', 'c', 'e', 'r', 't'}; + std::unique_ptr oem_cert = + OemCertificate::Create(kOEMPrivateKey, kOEMPrivateKeySize, kBadPublicCert, + sizeof(kBadPublicCert)); + EXPECT_FALSE(oem_cert); +} + +// It is possible to create an OEM Certificate using a non-matching +// public-private key pair so long as the key types are the same. +// However, OEM Cert validation should catch the problem. +TEST(OEMCryptoOemCertTest, CreateWithDifferentPrivateRsaKey) { + std::unique_ptr key = RsaPrivateKey::New(kRsa2048Bit); + ASSERT_TRUE(key); + const std::vector private_key = key->Serialize(); + ASSERT_FALSE(private_key.empty()); + + // Creating the OEM Certificate should succeed. + std::unique_ptr oem_cert = + OemCertificate::Create(private_key, kOEMPublicCertVector); + ASSERT_TRUE(oem_cert); + + EXPECT_EQ(OemCertificate::kRsa, oem_cert->key_type()); + + // Validating key should return an error. + OEMCryptoResult status = oem_cert->IsCertificateValid(); + // Test still allows deprecated error. + if (status != OEMCrypto_ERROR_INVALID_RSA_KEY) { + EXPECT_EQ(OEMCrypto_ERROR_INVALID_KEY, status); + } +} +} // namespace util +} // namespace wvoec diff --git a/oemcrypto/util/test/oemcrypto_ref_test_utils.cpp b/oemcrypto/util/test/oemcrypto_ref_test_utils.cpp new file mode 100644 index 0000000..a27b68c --- /dev/null +++ b/oemcrypto/util/test/oemcrypto_ref_test_utils.cpp @@ -0,0 +1,20 @@ +// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. +// +// Reference implementation utilities of OEMCrypto APIs +// +#include "oemcrypto_ref_test_utils.h" + +#include + +#include "cdm_random.h" + +namespace wvoec { +namespace util { +std::vector RandomData(size_t length) { + const std::string data = wvutil::CdmRandom::RandomData(length); + return std::vector(data.begin(), data.end()); +} +} // namespace util +} // namespace wvoec diff --git a/oemcrypto/util/test/oemcrypto_ref_test_utils.h b/oemcrypto/util/test/oemcrypto_ref_test_utils.h new file mode 100644 index 0000000..fccc58f --- /dev/null +++ b/oemcrypto/util/test/oemcrypto_ref_test_utils.h @@ -0,0 +1,20 @@ +// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. +// +// Reference implementation utilities of OEMCrypto APIs +// +#ifndef WVOEC_UTIL_REF_TEST_UTILS_H_ +#define WVOEC_UTIL_REF_TEST_UTILS_H_ + +#include + +#include + +namespace wvoec { +namespace util { +// Returns a vector of random bytes. +std::vector RandomData(size_t length); +} // namespace util +} // namespace wvoec +#endif // WVOEC_UTIL_REF_TEST_UTILS_H_ diff --git a/oemcrypto/util/test/oemcrypto_rsa_key_unittest.cpp b/oemcrypto/util/test/oemcrypto_rsa_key_unittest.cpp new file mode 100644 index 0000000..f2cb96c --- /dev/null +++ b/oemcrypto/util/test/oemcrypto_rsa_key_unittest.cpp @@ -0,0 +1,417 @@ +// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. +// +// Reference implementation utilities of OEMCrypto APIs +// +#include + +#include + +#include + +#include "OEMCryptoCENC.h" +#include "log.h" +#include "oemcrypto_ref_test_utils.h" +#include "oemcrypto_rsa_key.h" + +namespace wvoec { +namespace util { +constexpr size_t kMessageSize = 4 * 1024; // 4 kB +constexpr size_t kCastMessageSize = 83; // Special max size. + +class OEMCryptoRsaKeyTest : public ::testing::TestWithParam { + public: + void SetUp() override { + // RSA key generation is slow (~2 seconds) compared to the + // operations they perform (<50 ms). Each key type is generated + // once and globally stored in serialized form. + // Caching the instance may result in test failures for + // memory-leak detection. + const RsaFieldSize field_size = GetParam(); + std::lock_guard rsa_key_lock(rsa_key_mutex_); + // Use of a switch case is intentional to cause compiler warnings + // if a new field size is introduced without updating the test. + switch (field_size) { + case kRsa2048Bit: { + if (!rsa_2048_key_data_.empty()) { + key_ = RsaPrivateKey::Load(rsa_2048_key_data_); + } + if (!key_) { + key_ = RsaPrivateKey::New(kRsa2048Bit); + } + if (rsa_2048_key_data_.empty() && key_) { + rsa_2048_key_data_ = key_->Serialize(); + } + } break; + case kRsa3072Bit: { + if (!rsa_3072_key_data_.empty()) { + key_ = RsaPrivateKey::Load(rsa_3072_key_data_); + } + if (!key_) { + key_ = RsaPrivateKey::New(kRsa3072Bit); + } + if (rsa_3072_key_data_.empty() && key_) { + rsa_3072_key_data_ = key_->Serialize(); + } + } break; + case kRsaFieldUnknown: // Suppress compiler warnings + LOGE("RSA test was incorrectly instantiation"); + exit(EXIT_FAILURE); + break; + } + ASSERT_TRUE(key_) << "Key initialization failed " + << RsaFieldSizeToString(field_size); + } + + void TearDown() override { key_.reset(); } + + protected: + std::unique_ptr key_; + static std::mutex rsa_key_mutex_; + static std::vector rsa_2048_key_data_; + static std::vector rsa_3072_key_data_; +}; + +std::mutex OEMCryptoRsaKeyTest::rsa_key_mutex_; +std::vector OEMCryptoRsaKeyTest::rsa_2048_key_data_; +std::vector OEMCryptoRsaKeyTest::rsa_3072_key_data_; + +// Basic verification of RSA private key generation. +TEST_P(OEMCryptoRsaKeyTest, KeyProperties) { + const RsaFieldSize expected_field_size = GetParam(); + + EXPECT_EQ(key_->field_size(), expected_field_size); + EXPECT_NE(nullptr, key_->GetRsaKey()); +} + +// Checks that the private key serialization APIs are compatible +// and performing in a manner that is similar to other OEMCrypto methods +// that retrieve data. +TEST_P(OEMCryptoRsaKeyTest, SerializePrivateKey) { + constexpr size_t kInitialBufferSize = 10; // Definitely too small. + size_t buffer_size = kInitialBufferSize; + std::vector buffer(buffer_size); + + EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + key_->Serialize(buffer.data(), &buffer_size)); + EXPECT_GT(buffer_size, kInitialBufferSize); + + buffer.resize(buffer_size); + EXPECT_EQ(OEMCrypto_SUCCESS, key_->Serialize(buffer.data(), &buffer_size)); + buffer.resize(buffer_size); + + const std::vector direct_key_data = key_->Serialize(); + EXPECT_FALSE(direct_key_data.empty()); + ASSERT_EQ(buffer.size(), direct_key_data.size()); + for (size_t i = 0; i < buffer.size(); i++) { + ASSERT_EQ(buffer[i], direct_key_data[i]) << "i = " << std::to_string(i); + } +} + +// Checks that a private key that is serialized can be deserialized and +// reload. Also checks that the serialization of a key produces the +// same data to ensure consistency. +TEST_P(OEMCryptoRsaKeyTest, SerializeAndReloadPrivateKey) { + const std::vector key_data = key_->Serialize(); + std::unique_ptr loaded_key = RsaPrivateKey::Load(key_data); + ASSERT_TRUE(loaded_key); + + EXPECT_EQ(key_->field_size(), loaded_key->field_size()); + + const std::vector loaded_key_data = loaded_key->Serialize(); + ASSERT_EQ(key_data.size(), loaded_key_data.size()); + for (size_t i = 0; i < key_data.size(); i++) { + ASSERT_EQ(key_data[i], loaded_key_data[i]) << "i = " << std::to_string(i); + } +} + +// Checks that a private key with explicitly indicated schemes include +// the scheme fields in the reserialized key. +TEST_P(OEMCryptoRsaKeyTest, SerializeAndReloadPrivateKeyWithAllowedSchemes) { + const std::vector raw_key_data = key_->Serialize(); + std::vector key_data = {'S', 'I', 'G', 'N', 0x00, 0x00, 0x00, 0x03}; + key_data.insert(key_data.end(), raw_key_data.begin(), raw_key_data.end()); + + std::unique_ptr explicit_key = RsaPrivateKey::Load(key_data); + ASSERT_TRUE(explicit_key); + EXPECT_EQ(key_->field_size(), explicit_key->field_size()); + const uint32_t kExpectedSchemes = 0x03; + EXPECT_EQ(explicit_key->allowed_schemes(), kExpectedSchemes); + + const std::vector explicit_key_data = explicit_key->Serialize(); + ASSERT_EQ(key_data.size(), explicit_key_data.size()); + ASSERT_EQ(key_data, explicit_key_data); +} + +// Checks that a public key can be created from the private key. +TEST_P(OEMCryptoRsaKeyTest, DerivePublicKey) { + std::unique_ptr pub_key = key_->MakePublicKey(); + ASSERT_TRUE(pub_key); + + EXPECT_TRUE(key_->IsMatchingPublicKey(*pub_key)); +} + +// Checks that the public key serialization APIs are compatible +// and performing in a manner that is similar to other OEMCrypto methods +// that retrieve data. +TEST_P(OEMCryptoRsaKeyTest, SerializePublicKey) { + std::unique_ptr pub_key = key_->MakePublicKey(); + ASSERT_TRUE(pub_key); + + constexpr size_t kInitialBufferSize = 10; // Definitely too small. + size_t buffer_size = kInitialBufferSize; + std::vector buffer(buffer_size); + + EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + pub_key->Serialize(buffer.data(), &buffer_size)); + EXPECT_GT(buffer_size, kInitialBufferSize); + + buffer.resize(buffer_size); + EXPECT_EQ(OEMCrypto_SUCCESS, pub_key->Serialize(buffer.data(), &buffer_size)); + buffer.resize(buffer_size); + + const std::vector direct_key_data = pub_key->Serialize(); + EXPECT_FALSE(direct_key_data.empty()); + ASSERT_EQ(buffer.size(), direct_key_data.size()); + for (size_t i = 0; i < buffer.size(); i++) { + ASSERT_EQ(buffer[i], direct_key_data[i]) << "i = " << std::to_string(i); + } +} + +// Checks that a public key that is serialized can be deserialized and +// reload. Also checks that the serialization of a key produces the +// same data to ensure consistency. +TEST_P(OEMCryptoRsaKeyTest, SerializeAndReloadPublicKey) { + std::unique_ptr pub_key = key_->MakePublicKey(); + ASSERT_TRUE(pub_key); + + const std::vector key_data = pub_key->Serialize(); + + std::unique_ptr loaded_key = RsaPublicKey::Load(key_data); + ASSERT_TRUE(loaded_key); + + EXPECT_EQ(pub_key->field_size(), loaded_key->field_size()); + EXPECT_EQ(pub_key->allowed_schemes(), loaded_key->allowed_schemes()); + + const std::vector loaded_key_data = loaded_key->Serialize(); + ASSERT_EQ(key_data.size(), loaded_key_data.size()); + for (size_t i = 0; i < key_data.size(); i++) { + ASSERT_EQ(key_data[i], loaded_key_data[i]) << "i = " << std::to_string(i); + } +} + +// Checks that a public key can be initialized from a ASN.1 DER encoded +// PrivateKeyInfo message. +TEST_P(OEMCryptoRsaKeyTest, SerializePrivateKeyAndReloadAsPublicKey) { + const std::vector key_data = key_->Serialize(); + ASSERT_FALSE(key_data.empty()) << "Failed to serialize as private key"; + + auto key_by_buffer = + RsaPublicKey::LoadPrivateKeyInfo(key_data.data(), key_data.size()); + ASSERT_TRUE(key_by_buffer) + << "Failed to deserialize private key into public key"; + EXPECT_TRUE(key_->IsMatchingPublicKey(*key_by_buffer)); + key_by_buffer.reset(); + + auto key_by_vector = RsaPublicKey::LoadPrivateKeyInfo(key_data); + ASSERT_TRUE(key_by_vector) + << "Failed to deserialize private key into public key"; + EXPECT_TRUE(key_->IsMatchingPublicKey(*key_by_vector)); + key_by_vector.reset(); + + const std::string key_data_str(key_data.begin(), key_data.end()); + auto key_by_string = RsaPublicKey::LoadPrivateKeyInfo(key_data_str); + ASSERT_TRUE(key_by_string) + << "Failed to deserialize private key into public key"; + EXPECT_TRUE(key_->IsMatchingPublicKey(*key_by_string)); +} + +// Checks that the RSA signature generating API operates similar to +// existing signature generation functions. +TEST_P(OEMCryptoRsaKeyTest, GenerateSignature) { + const std::vector message = RandomData(kMessageSize); + ASSERT_FALSE(message.empty()) << "CdmRandom failed"; + + constexpr size_t kInitialBufferSize = 10; // Definitely too small. + size_t signature_size = kInitialBufferSize; + std::vector signature(signature_size); + EXPECT_EQ( + OEMCrypto_ERROR_SHORT_BUFFER, + key_->GenerateSignature(message.data(), message.size(), kRsaPssDefault, + signature.data(), &signature_size)); + EXPECT_GT(signature_size, kInitialBufferSize); + + signature.resize(signature_size); + EXPECT_EQ( + OEMCrypto_SUCCESS, + key_->GenerateSignature(message.data(), message.size(), kRsaPssDefault, + signature.data(), &signature_size)); + signature.resize(signature_size); + + EXPECT_LE(signature_size, key_->SignatureSize()); +} + +// Checks that RSA signatures can be verified by an RSA public key. +TEST_P(OEMCryptoRsaKeyTest, VerifySignature) { + const std::vector message = RandomData(kMessageSize); + ASSERT_FALSE(message.empty()) << "CdmRandom failed"; + + const std::vector signature = key_->GenerateSignature(message); + + std::unique_ptr pub_key = key_->MakePublicKey(); + ASSERT_TRUE(pub_key); + + EXPECT_EQ(OEMCrypto_SUCCESS, pub_key->VerifySignature(message, signature)); + + // Check with different message. + const std::vector message_two = RandomData(kMessageSize); + EXPECT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE, + pub_key->VerifySignature(message_two, signature)); + + // Check with bad signature. + const std::vector bad_signature = RandomData(signature.size()); + EXPECT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE, + pub_key->VerifySignature(message, bad_signature)); +} + +// Checks that the special CAST receiver signature scheme works +// to the degree that it is possible to test. +TEST_P(OEMCryptoRsaKeyTest, GenerateAndVerifyRsaSignature) { + // Key must be enabled for PKCS1 Block 1 padding scheme. + // To do so, the key is serialized and the padding scheme is + // added to the key data. + const std::vector key_data = key_->Serialize(); + ASSERT_FALSE(key_data.empty()); + std::vector pkcs_enabled_key_data = { + 'S', 'I', 'G', 'N', 0x00, 0x00, 0x00, kSign_PKCS1_Block1}; + pkcs_enabled_key_data.insert(pkcs_enabled_key_data.end(), key_data.begin(), + key_data.end()); + std::unique_ptr pkcs_enabled_key = + RsaPrivateKey::Load(pkcs_enabled_key_data); + ASSERT_TRUE(pkcs_enabled_key); + + // The actual cast message is a domain specific hash of the message, + // however, random data works for testing purposes. + const std::vector message = RandomData(kCastMessageSize); + + // Generate signature. + constexpr size_t kInitialBufferSize = 10; // Definitely too small. + size_t signature_size = kInitialBufferSize; + std::vector signature(signature_size); + EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + pkcs_enabled_key->GenerateSignature(message.data(), message.size(), + kRsaPkcs1Cast, signature.data(), + &signature_size)); + EXPECT_GT(signature_size, kInitialBufferSize); + signature.resize(signature_size); + EXPECT_EQ(OEMCrypto_SUCCESS, + pkcs_enabled_key->GenerateSignature(message.data(), message.size(), + kRsaPkcs1Cast, signature.data(), + &signature_size)); + signature.resize(signature_size); + + EXPECT_LE(signature_size, pkcs_enabled_key->SignatureSize()); + + // Verify signature. + std::unique_ptr pub_key = pkcs_enabled_key->MakePublicKey(); + ASSERT_TRUE(pub_key); + + EXPECT_EQ(OEMCrypto_SUCCESS, + pub_key->VerifySignature(message, signature, kRsaPkcs1Cast)); +} + +// Verifies the session key exchange protocol used by the licensing +// server. +TEST_P(OEMCryptoRsaKeyTest, ShareSessionKey) { + constexpr size_t kSessionKeySize = 16; + std::unique_ptr public_key = key_->MakePublicKey(); + ASSERT_TRUE(public_key); + + // Generate session key. + const std::vector session_key = RandomData(kSessionKeySize); + ASSERT_FALSE(session_key.empty()); + + // Server's perspective. + constexpr size_t kInitialBufferSize = 10; // Definitely too small. + size_t enc_session_key_size = kInitialBufferSize; + std::vector enc_session_key(enc_session_key_size); + OEMCryptoResult result = public_key->EncryptSessionKey( + session_key.data(), session_key.size(), enc_session_key.data(), + &enc_session_key_size); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, result); + enc_session_key.resize(enc_session_key_size); + result = public_key->EncryptSessionKey(session_key.data(), session_key.size(), + enc_session_key.data(), + &enc_session_key_size); + ASSERT_EQ(OEMCrypto_SUCCESS, result); + enc_session_key.resize(enc_session_key_size); + + // Client's perspective. + size_t received_session_key_size = kInitialBufferSize; + std::vector received_session_key(received_session_key_size); + result = key_->DecryptSessionKey( + enc_session_key.data(), enc_session_key.size(), + received_session_key.data(), &received_session_key_size); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, result); + received_session_key.resize(received_session_key_size); + result = key_->DecryptSessionKey( + enc_session_key.data(), enc_session_key.size(), + received_session_key.data(), &received_session_key_size); + ASSERT_EQ(OEMCrypto_SUCCESS, result); + received_session_key.resize(received_session_key_size); + + // Compare keys. + ASSERT_EQ(session_key.size(), received_session_key.size()); + ASSERT_EQ(session_key, received_session_key); +} + +// Verifies the encryption key exchange protocol used by the licensing +// server. +TEST_P(OEMCryptoRsaKeyTest, ShareEncryptionKey) { + constexpr size_t kEncryptionKeySize = 16; + std::unique_ptr public_key = key_->MakePublicKey(); + ASSERT_TRUE(public_key); + + // Generate session key. + const std::vector encryption_key = RandomData(kEncryptionKeySize); + ASSERT_FALSE(encryption_key.empty()); + + // Server's perspective. + constexpr size_t kInitialBufferSize = 10; // Definitely too small. + size_t enc_encryption_key_size = kInitialBufferSize; + std::vector enc_encryption_key(enc_encryption_key_size); + OEMCryptoResult result = public_key->EncryptEncryptionKey( + encryption_key.data(), encryption_key.size(), enc_encryption_key.data(), + &enc_encryption_key_size); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, result); + enc_encryption_key.resize(enc_encryption_key_size); + result = public_key->EncryptEncryptionKey( + encryption_key.data(), encryption_key.size(), enc_encryption_key.data(), + &enc_encryption_key_size); + ASSERT_EQ(OEMCrypto_SUCCESS, result); + enc_encryption_key.resize(enc_encryption_key_size); + + // Client's perspective. + size_t received_encryption_key_size = kInitialBufferSize; + std::vector received_encryption_key(received_encryption_key_size); + result = key_->DecryptEncryptionKey( + enc_encryption_key.data(), enc_encryption_key.size(), + received_encryption_key.data(), &received_encryption_key_size); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, result); + received_encryption_key.resize(received_encryption_key_size); + result = key_->DecryptEncryptionKey( + enc_encryption_key.data(), enc_encryption_key.size(), + received_encryption_key.data(), &received_encryption_key_size); + ASSERT_EQ(OEMCrypto_SUCCESS, result); + received_encryption_key.resize(received_encryption_key_size); + + // Compare keys. + ASSERT_EQ(encryption_key.size(), received_encryption_key.size()); + ASSERT_EQ(encryption_key, received_encryption_key); +} + +INSTANTIATE_TEST_SUITE_P(AllFieldSizes, OEMCryptoRsaKeyTest, + ::testing::Values(kRsa2048Bit, kRsa3072Bit)); +} // namespace util +} // namespace wvoec diff --git a/oemcrypto/util/test/oemcrypto_wvcrc32_unittest.cpp b/oemcrypto/util/test/oemcrypto_wvcrc32_unittest.cpp new file mode 100644 index 0000000..3d62b17 --- /dev/null +++ b/oemcrypto/util/test/oemcrypto_wvcrc32_unittest.cpp @@ -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. +// +// Reference implementation utilities of OEMCrypto APIs +// +#include + +#include "wvcrc32.h" + +namespace wvoec { +namespace util { +uint32_t ComputeCrc32(const std::string& s) { + return wvcrc32(reinterpret_cast(s.data()), s.size()); +} + +uint32_t ComputeCrc32Cont(const std::string& s, uint32_t prev_crc) { + return wvcrc32Cont(reinterpret_cast(s.data()), s.size(), + prev_crc); +} + +TEST(OEMCryptoWvCrc32Test, BasicTest) { + EXPECT_EQ(0xF88AC628u, ComputeCrc32("abcdefg")); + EXPECT_EQ(0xDF520F72u, ComputeCrc32("Widevine")); + EXPECT_EQ(0x0376E6E7u, ComputeCrc32("123456789")); + EXPECT_EQ(0xBA62119Eu, + ComputeCrc32("The quick brown fox jumps over the lazy dog")); +} + +TEST(OEMCryptoWvCrc32Test, StreamTest) { + const std::vector parts = {"The ", "quick", " brown ", + "fox", " jumps ", "over", + " the ", "lazy", " dog"}; + uint32_t crc = wvcrc32Init(); + for (const auto& part : parts) { + crc = ComputeCrc32Cont(part, crc); + } + EXPECT_EQ(0xBA62119Eu, crc); +} + +TEST(OEMCryptoWvCrc32Test, Keybox) { + // clang-format off + const uint8_t kKeyboxData[128] = { + // deviceID = WidevineCRCTestKeyBox + 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, + 0x43, 0x52, 0x43, 0x54, 0x65, 0x73, 0x74, 0x4b, + 0x65, 0x79, 0x62, 0x6f, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // key = random + 0x8a, 0x7c, 0xda, 0x3e, 0x09, 0xd9, 0x8e, 0xd5, + 0x47, 0x47, 0x00, 0x84, 0x5a, 0x1f, 0x52, 0xd4, + // data = random + 0x98, 0xa5, 0x00, 0x19, 0x8b, 0xfe, 0x54, 0xfd, + 0xca, 0x4d, 0x26, 0xa3, 0xfa, 0xaa, 0x3b, 0x6c, + 0x35, 0xfe, 0x03, 0x7c, 0xbf, 0x35, 0xba, 0xce, + 0x31, 0xb5, 0x1e, 0x3c, 0x49, 0xd6, 0x3f, 0x9c, + 0x3a, 0xde, 0x9b, 0x58, 0xcc, 0x54, 0x8d, 0xc0, + 0x4b, 0x04, 0xcc, 0xee, 0xae, 0x4d, 0x9f, 0x90, + 0xd3, 0xf3, 0xfe, 0x23, 0x26, 0x13, 0x56, 0x80, + 0xe4, 0x3b, 0x79, 0x22, 0x69, 0x5d, 0xd6, 0xb7, + 0xa0, 0x0e, 0x7e, 0x07, 0xcd, 0x1a, 0x15, 0xca, + // magic + 'k', 'b', 'o', 'x', + // crc + 0x09, 0x7b, 0x7e, 0xcc + }; + // clang-format on + const uint32_t crc_computed = wvcrc32n(kKeyboxData, 124); + uint32_t crc_current; + memcpy(&crc_current, &kKeyboxData[124], 4); + EXPECT_EQ(crc_computed, crc_current); +} +} // namespace util +} // namespace wvoec diff --git a/util/include/log.h b/util/include/log.h index bff7205..f4d5e72 100644 --- a/util/include/log.h +++ b/util/include/log.h @@ -22,13 +22,13 @@ namespace wvutil { typedef enum { // This log level should only be used for |g_cutoff|, in order to silence all // logging. It should never be passed to |Log()| as a log level. - LOG_SILENT = -1, + CDM_LOG_SILENT = -1, - LOG_ERROR = 0, - LOG_WARN = 1, - LOG_INFO = 2, - LOG_DEBUG = 3, - LOG_VERBOSE = 4, + CDM_LOG_ERROR = 0, + CDM_LOG_WARN = 1, + CDM_LOG_INFO = 2, + CDM_LOG_DEBUG = 3, + CDM_LOG_VERBOSE = 4, } LogPriority; extern LogPriority g_cutoff; @@ -53,7 +53,7 @@ class LogBuffer { extern LogBuffer g_logbuf; -static const uint32_t UNKNOWN_UID = ~0; +static const uint32_t UNKNOWN_UID = std::numeric_limits::max(); #ifdef __ANDROID__ void SetLoggingUid(const uint32_t); @@ -93,15 +93,15 @@ CORE_UTIL_EXPORT void Log(const char* file, const char* function, int line, // Log APIs #ifndef LOGE # define LOGE(...) \ - Log(__FILE__, __func__, __LINE__, wvutil::LOG_ERROR, __VA_ARGS__) + Log(__FILE__, __func__, __LINE__, wvutil::CDM_LOG_ERROR, __VA_ARGS__) # define LOGW(...) \ - Log(__FILE__, __func__, __LINE__, wvutil::LOG_WARN, __VA_ARGS__) + Log(__FILE__, __func__, __LINE__, wvutil::CDM_LOG_WARN, __VA_ARGS__) # define LOGI(...) \ - Log(__FILE__, __func__, __LINE__, wvutil::LOG_INFO, __VA_ARGS__) + Log(__FILE__, __func__, __LINE__, wvutil::CDM_LOG_INFO, __VA_ARGS__) # define LOGD(...) \ - Log(__FILE__, __func__, __LINE__, wvutil::LOG_DEBUG, __VA_ARGS__) + Log(__FILE__, __func__, __LINE__, wvutil::CDM_LOG_DEBUG, __VA_ARGS__) # define LOGV(...) \ - Log(__FILE__, __func__, __LINE__, wvutil::LOG_VERBOSE, __VA_ARGS__) + Log(__FILE__, __func__, __LINE__, wvutil::CDM_LOG_VERBOSE, __VA_ARGS__) #endif } // namespace wvutil diff --git a/util/include/string_conversions.h b/util/include/string_conversions.h index 471d2fe..e5944bd 100644 --- a/util/include/string_conversions.h +++ b/util/include/string_conversions.h @@ -20,10 +20,15 @@ CORE_UTIL_EXPORT std::vector a2b_hex(const std::string& label, const std::string& b); CORE_UTIL_EXPORT std::string a2bs_hex(const std::string& b); -// Binary to ASCII hex conversion. +// Binary to ASCII hex conversion. The default versions limit output to 2k to +// protect us from log spam. The unlimited version has no length limit. CORE_UTIL_EXPORT std::string b2a_hex(const std::vector& b); +CORE_UTIL_EXPORT std::string unlimited_b2a_hex(const std::vector& b); CORE_UTIL_EXPORT std::string b2a_hex(const std::string& b); +CORE_UTIL_EXPORT std::string unlimited_b2a_hex(const std::string& b); CORE_UTIL_EXPORT std::string HexEncode(const uint8_t* bytes, size_t size); +CORE_UTIL_EXPORT std::string UnlimitedHexEncode(const uint8_t* bytes, + size_t size); // Base64 encoding/decoding. // Converts binary data into the ASCII Base64 character set and vice diff --git a/util/src/cdm_random.cpp b/util/src/cdm_random.cpp index b8dce14..ef51282 100644 --- a/util/src/cdm_random.cpp +++ b/util/src/cdm_random.cpp @@ -77,10 +77,10 @@ std::string CdmRandomGenerator::RandomData(size_t length) { return std::string(); } CdmRandomLock lock(generator_lock_); - std::uniform_int_distribution dist; // Range of [0, 255]. + std::uniform_int_distribution dist(0, 255); // Range of [0, 255]. std::string random_data(length, '\0'); std::generate(random_data.begin(), random_data.end(), - [&]() { return dist(generator_); }); + [&]() { return static_cast(dist(generator_)); }); return random_data; } diff --git a/util/src/string_conversions.cpp b/util/src/string_conversions.cpp index 9fd3251..37ac2ef 100644 --- a/util/src/string_conversions.cpp +++ b/util/src/string_conversions.cpp @@ -201,20 +201,34 @@ std::string b2a_hex(const std::vector& byte) { return HexEncode(byte.data(), byte.size()); } +std::string unlimited_b2a_hex(const std::vector& byte) { + if (byte.empty()) return ""; + return UnlimitedHexEncode(byte.data(), byte.size()); +} + std::string b2a_hex(const std::string& byte) { if (byte.empty()) return ""; return HexEncode(reinterpret_cast(byte.data()), byte.length()); } +std::string unlimited_b2a_hex(const std::string& byte) { + if (byte.empty()) return ""; + return UnlimitedHexEncode(reinterpret_cast(byte.data()), + byte.length()); +} + std::string HexEncode(const uint8_t* in_buffer, size_t size) { - static const char kHexChars[] = "0123456789ABCDEF"; - if (size == 0) return ""; constexpr unsigned int kMaxSafeSize = 2048; if (size > kMaxSafeSize) size = kMaxSafeSize; + return UnlimitedHexEncode(in_buffer, size); +} + +std::string UnlimitedHexEncode(const uint8_t* in_buffer, size_t size) { + static const char kHexChars[] = "0123456789ABCDEF"; + if (size == 0) return ""; // Each input byte creates two output hex characters. std::string out_buffer(size * 2, '\0'); - for (unsigned int i = 0; i < size; ++i) { char byte = in_buffer[i]; out_buffer[(i << 1)] = kHexChars[(byte >> 4) & 0xf]; diff --git a/util/test/test_clock.cpp b/util/test/test_clock.cpp index 29b964d..18331bb 100644 --- a/util/test/test_clock.cpp +++ b/util/test/test_clock.cpp @@ -13,7 +13,7 @@ namespace wvutil { namespace { // A fake clock that only advances when TestSleep::Sleep is called. -class FakeClock : public wvcdm::TestSleep::CallBack { +class FakeClock : public TestSleep::CallBack { public: FakeClock() { auto now = std::chrono::system_clock().now(); @@ -34,7 +34,7 @@ FakeClock* g_fake_clock = nullptr; // On devices running a fake OEMCrypto, we can use a fake sleep and fake time. int64_t Clock::GetCurrentTime() { - wvcdm::TestSleep::SyncFakeClock(); + TestSleep::SyncFakeClock(); if (g_fake_clock == nullptr) g_fake_clock = new FakeClock(); return g_fake_clock->now() / 1000; } diff --git a/util/test/test_sleep.cpp b/util/test/test_sleep.cpp index 4ca4b87..281e042 100644 --- a/util/test/test_sleep.cpp +++ b/util/test/test_sleep.cpp @@ -58,6 +58,21 @@ void TestSleep::SyncFakeClock() { Sleep(0); } +void TestSleep::SetFakeClock(int64_t time_seconds) { + if (real_sleep_) { + LOGE("SetFakeClock when using a real clock. Expect other failures."); + } + // Delta could be positive or negative. If the fake clock had been initialized + // by the current time on a real clock, and then the command line + // re-initializes it to 0, then delta is negative. + int64_t delta = time_seconds - Clock().GetCurrentTime(); + if (callback_ != nullptr) { + callback_->ElapseTime(delta * 1000); + } else { + LOGE("Setting fake clock with no callback. This won't work."); + } +} + bool TestSleep::RollbackSystemTime(int seconds) { if (real_sleep_) { #ifdef _WIN32 diff --git a/util/test/test_sleep.h b/util/test/test_sleep.h index 28db0b0..155b89d 100644 --- a/util/test/test_sleep.h +++ b/util/test/test_sleep.h @@ -41,6 +41,10 @@ class TestSleep { // verify this function does not roll back the clock used by OEMCrypto. static bool RollbackSystemTime(int seconds); + // Set the system clock to the specified time. This is only expected to work + // when real_sleep is false. + static void SetFakeClock(int64_t time_seconds); + // Roll the system clock forward to undo all previous calls to // RollBackSystemTime. Returns true on success. static bool ResetRollback() {