diff --git a/libwvdrmengine/oemcrypto/util/include/hmac.h b/libwvdrmengine/oemcrypto/util/include/hmac.h new file mode 100644 index 00000000..279cbbf7 --- /dev/null +++ b/libwvdrmengine/oemcrypto/util/include/hmac.h @@ -0,0 +1,139 @@ +// Copyright 2022 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_HMAC_H_ +#define WVOEC_UTIL_HMAC_H_ + +#include +#include + +#include +#include + +#include "OEMCryptoCENCCommon.h" + +namespace wvoec { +namespace util { +// Size of an HMAC-SHA-1 signature. Same size as a SHA-1 digest. +static constexpr size_t kHmacSha1SignatureSize = 20; +// Size of an HMAC-SHA-256 signature. Same size as a SHA-256 digest. +static constexpr size_t kHmacSha256SignatureSize = 32; + +// == Signature Generate == + +// Generates a HMAC-SHA-1 signature using the provided |key| and +// |message|. Both |key| and |message| must be non-zero length. +// The input/output |signature_length| should initially contain the +// size of the |signature| buffer, and the function will assign +// the final length of the signature. +// +// Return values: +// OEMCrypto_SUCCESS if signature is generated successfully; +// |signature_length| may be updated with the actual +// signature size +// OEMCrypto_ERROR_SHORT_BUFFER if the provided |signature| buffer +// is too small to fit an HMAC-SHA-1 signature; +// |signature_length| is updated with the require size +// OEMCrypto_ERROR_INVALID_CONTEXT if any the parameters are +// incorrect +// OEMCrypto_ERROR_UNKNOWN_FAILURE otherwise +OEMCryptoResult HmacSha1(const uint8_t* key, size_t key_length, + const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length); +OEMCryptoResult HmacSha1(const std::vector& key, + const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length); + +std::vector HmacSha1(const std::vector& key, + const std::vector& message); + +// Generates a HMAC-SHA-256 signature using the provided |key| and +// |message|. Both |key| and |message| must be non-zero length. +// The input/output |signature_length| should initially contain the +// size of the |signature| buffer, and the function will assign +// the final length of the signature. +// +// Return values: +// OEMCrypto_SUCCESS if signature is generated successfully; +// |signature_length| may be updated with the actual +// signature size +// OEMCrypto_ERROR_SHORT_BUFFER if the provided |signature| buffer +// is too small to fit an HMAC-SHA-256 signature; +// |signature_length| is updated with the require size +// OEMCrypto_ERROR_INVALID_CONTEXT if any the parameters are +// incorrect +// OEMCrypto_ERROR_UNKNOWN_FAILURE otherwise +OEMCryptoResult HmacSha256(const uint8_t* key, size_t key_length, + const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length); +OEMCryptoResult HmacSha256(const std::vector& key, + const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length); + +bool HmacSha256(const std::vector& key, + const std::vector& message, + std::vector* signature); + +bool HmacSha256(const std::vector& key, const std::string& message, + std::vector* signature); + +std::vector HmacSha256(const std::vector& key, + const uint8_t* message, size_t message_length); +std::vector HmacSha256(const std::vector& key, + const std::vector& message); +std::vector HmacSha256(const std::vector& key, + const std::string& message); + +// == Signature Verification == + +// Verifies an HMAC-SHA-1 signature using the provided |key| and +// |message| against the provided |signature|. +// +// Return values: +// OEMCrypto_SUCCESS if signature is valid +// OEMCrypto_ERROR_SIGNATURE_FAILURE if signature is invalid +// OEMCrypto_ERROR_INVALID_CONTEXT if any the parameters are +// incorrect +// OEMCrypto_ERROR_UNKNOWN_FAILURE otherwise +OEMCryptoResult HmacSha1Verify(const uint8_t* key, size_t key_length, + const uint8_t* message, size_t message_length, + const uint8_t* signature, + size_t signature_length); +OEMCryptoResult HmacSha1Verify(const std::vector& key, + const uint8_t* message, size_t message_length, + const uint8_t* signature, + size_t signature_length); + +OEMCryptoResult HmacSha1Verify(const std::vector& key, + const std::vector& message, + const std::vector& signature); + +// Verifies an HMAC-SHA-256 signature using the provided |key| and +// |message| against the provided |signature|. +// +// Return values: +// OEMCrypto_SUCCESS if signature is valid +// OEMCrypto_ERROR_SIGNATURE_FAILURE if signature is invalid +// OEMCrypto_ERROR_INVALID_CONTEXT if any the parameters are +// incorrect +// OEMCrypto_ERROR_UNKNOWN_FAILURE otherwise +OEMCryptoResult HmacSha256Verify(const uint8_t* key, size_t key_length, + const uint8_t* message, size_t message_length, + const uint8_t* signature, + size_t signature_length); +OEMCryptoResult HmacSha256Verify(const std::vector& key, + const uint8_t* message, size_t message_length, + const uint8_t* signature, + size_t signature_length); +OEMCryptoResult HmacSha256Verify(const std::vector& key, + const std::vector& message, + const std::vector& signature); +OEMCryptoResult HmacSha256Verify(const std::vector& key, + const std::string& message, + const std::vector& signature); +} // namespace util +} // namespace wvoec +#endif // WVOEC_UTIL_HMAC_H_ diff --git a/libwvdrmengine/oemcrypto/util/src/hmac.cpp b/libwvdrmengine/oemcrypto/util/src/hmac.cpp new file mode 100644 index 00000000..8d665e59 --- /dev/null +++ b/libwvdrmengine/oemcrypto/util/src/hmac.cpp @@ -0,0 +1,269 @@ +// Copyright 2022 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 "hmac.h" + +#include +#include +#include + +#include "log.h" +#include "string_conversions.h" + +namespace wvoec { +namespace util { +namespace { +constexpr bool kHmacSha256 = true; +constexpr bool kHmacSha1 = false; +const std::vector kEmptyVector; + +// Assumes all parameters are valid. +inline bool HmacShaInternal(const uint8_t* key, size_t key_length, + const uint8_t* message, size_t message_length, + uint8_t* signature, bool sha256) { + return HMAC(sha256 ? EVP_sha256() : EVP_sha1(), key, + static_cast(key_length), message, message_length, signature, + nullptr) != nullptr; +} + +OEMCryptoResult GenerateSignatureInternal(const uint8_t* key, size_t key_length, + const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length, + bool sha256) { + if (key == nullptr || key_length == 0) { + LOGE("Input |key| is %s", key_length == 0 ? "empty" : "null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (message == nullptr || message_length == 0) { + LOGE("Input |message| is %s", message_length == 0 ? "empty" : "null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (signature_length == nullptr) { + LOGE("Input/output |signature_length| is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (signature == nullptr && *signature_length > 0) { + LOGE("Output |signature| is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + const size_t required_signature_size = + sha256 ? kHmacSha256SignatureSize : kHmacSha1SignatureSize; + if (*signature_length < required_signature_size) { + *signature_length = required_signature_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + if (!HmacShaInternal(key, key_length, message, message_length, signature, + sha256)) { + LOGE("Failed to generate signature"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + *signature_length = required_signature_size; + return OEMCrypto_SUCCESS; +} + +bool GenerateSignatureInternal(const uint8_t* key, size_t key_length, + const uint8_t* message, size_t message_length, + std::vector* signature, bool sha256) { + if (signature == nullptr) { + LOGE("Output |signature| is null"); + return false; + } + size_t signature_length = + sha256 ? kHmacSha256SignatureSize : kHmacSha1SignatureSize; + signature->resize(signature_length); + const OEMCryptoResult res = + GenerateSignatureInternal(key, key_length, message, message_length, + signature->data(), &signature_length, sha256); + if (res != OEMCrypto_SUCCESS) { + signature->clear(); + return false; + } + return true; +} + +// Assumes signature pointers are valid. +inline bool CompareSignatures(const uint8_t* signature_a, + const uint8_t* signature_b, bool sha256) { + const size_t signature_size = + sha256 ? kHmacSha256SignatureSize : kHmacSha1SignatureSize; + return CRYPTO_memcmp(signature_a, signature_b, signature_size) == 0; +} + +OEMCryptoResult VerifySignatureInternal(const uint8_t* key, size_t key_length, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length, bool sha256) { + if (signature == nullptr && signature_length > 0) { + LOGE("Input |signature| is null"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + size_t expected_signature_length = + sha256 ? kHmacSha256SignatureSize : kHmacSha1SignatureSize; + if (signature_length != expected_signature_length) { + LOGE("Invalid signature length: expected = %zu, actual = %zu", + expected_signature_length, signature_length); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + // Allocate for the larger of the two. + uint8_t expected_signature[kHmacSha256SignatureSize]; + const OEMCryptoResult res = GenerateSignatureInternal( + key, key_length, message, message_length, expected_signature, + &expected_signature_length, sha256); + if (res != OEMCrypto_SUCCESS) return res; + if (!CompareSignatures(signature, expected_signature, sha256)) { + LOGD("Signatures do not match: type = HMAC-SHA-%s", sha256 ? "256" : "1"); + LOGD("provided = %s", + wvutil::HexEncode(signature, signature_length).c_str()); + LOGD("expected = %s", + wvutil::HexEncode(expected_signature, expected_signature_length) + .c_str()); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + return OEMCrypto_SUCCESS; +} +} // namespace + +OEMCryptoResult HmacSha1(const uint8_t* key, size_t key_length, + const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length) { + return GenerateSignatureInternal(key, key_length, message, message_length, + signature, signature_length, kHmacSha1); +} + +OEMCryptoResult HmacSha1(const std::vector& key, + const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length) { + return GenerateSignatureInternal(key.data(), key.size(), message, + message_length, signature, signature_length, + kHmacSha1); +} + +std::vector HmacSha1(const std::vector& key, + const std::vector& message) { + std::vector signature; + const bool res = + GenerateSignatureInternal(key.data(), key.size(), message.data(), + message.size(), &signature, kHmacSha1); + return res ? signature : kEmptyVector; +} + +OEMCryptoResult HmacSha256(const uint8_t* key, size_t key_length, + const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length) { + return GenerateSignatureInternal(key, key_length, message, message_length, + signature, signature_length, kHmacSha256); +} + +OEMCryptoResult HmacSha256(const std::vector& key, + const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length) { + return GenerateSignatureInternal(key.data(), key.size(), message, + message_length, signature, signature_length, + kHmacSha256); +} + +bool HmacSha256(const std::vector& key, + const std::vector& message, + std::vector* signature) { + return GenerateSignatureInternal(key.data(), key.size(), message.data(), + message.size(), signature, kHmacSha256); +} + +bool HmacSha256(const std::vector& key, const std::string& message, + std::vector* signature) { + return GenerateSignatureInternal( + key.data(), key.size(), reinterpret_cast(message.data()), + message.size(), signature, kHmacSha256); +} + +std::vector HmacSha256(const std::vector& key, + const uint8_t* message, size_t message_length) { + std::vector signature; + const bool res = GenerateSignatureInternal( + key.data(), key.size(), message, message_length, &signature, kHmacSha256); + return res ? signature : kEmptyVector; +} + +std::vector HmacSha256(const std::vector& key, + const std::vector& message) { + std::vector signature; + const bool res = + GenerateSignatureInternal(key.data(), key.size(), message.data(), + message.size(), &signature, kHmacSha256); + return res ? signature : kEmptyVector; +} + +std::vector HmacSha256(const std::vector& key, + const std::string& message) { + std::vector signature; + const bool res = GenerateSignatureInternal( + key.data(), key.size(), reinterpret_cast(message.data()), + message.size(), &signature, kHmacSha256); + return res ? signature : kEmptyVector; +} + +OEMCryptoResult HmacSha1Verify(const uint8_t* key, size_t key_length, + const uint8_t* message, size_t message_length, + const uint8_t* signature, + size_t signature_length) { + return VerifySignatureInternal(key, key_length, message, message_length, + signature, signature_length, kHmacSha1); +} + +OEMCryptoResult HmacSha1Verify(const std::vector& key, + const uint8_t* message, size_t message_length, + const uint8_t* signature, + size_t signature_length) { + return VerifySignatureInternal(key.data(), key.size(), message, + message_length, signature, signature_length, + kHmacSha1); +} + +OEMCryptoResult HmacSha1Verify(const std::vector& key, + const std::vector& message, + const std::vector& signature) { + return VerifySignatureInternal(key.data(), key.size(), message.data(), + message.size(), signature.data(), + signature.size(), kHmacSha1); +} + +OEMCryptoResult HmacSha256Verify(const uint8_t* key, size_t key_length, + const uint8_t* message, size_t message_length, + const uint8_t* signature, + size_t signature_length) { + return VerifySignatureInternal(key, key_length, message, message_length, + signature, signature_length, kHmacSha256); +} + +OEMCryptoResult HmacSha256Verify(const std::vector& key, + const uint8_t* message, size_t message_length, + const uint8_t* signature, + size_t signature_length) { + return VerifySignatureInternal(key.data(), key.size(), message, + message_length, signature, signature_length, + kHmacSha256); +} + +OEMCryptoResult HmacSha256Verify(const std::vector& key, + const std::vector& message, + const std::vector& signature) { + return VerifySignatureInternal(key.data(), key.size(), message.data(), + message.size(), signature.data(), + signature.size(), kHmacSha256); +} + +OEMCryptoResult HmacSha256Verify(const std::vector& key, + const std::string& message, + const std::vector& signature) { + return VerifySignatureInternal( + key.data(), key.size(), reinterpret_cast(message.data()), + message.size(), signature.data(), signature.size(), kHmacSha256); +} +} // namespace util +} // namespace wvoec