High-level wrapper around HMAC-SHA256 algorithm.

[ Merge of http://go/wvgerrit/152950 ]

This CL introduces several functions for computing a HMAC-SHA256
signature.  The functions wrap the OpenSSL/BoringSSL implementation
of HMAC(), allowing for common C++ types to be passed in.  Several
of the functions follow several OEMCrypto conventions for generating
signatures (ex. returning OEMCrypto_ERROR_SHORT_BUFFER if signature
buffer is too small).

Also provided limited wrappers for HMAC-SHA-1, which are used for
a limited number of operations within OEMCrypto.

Bug: 154055871
Bug: 145026434
Bug: 236317198
Test: hmac_unittest
Change-Id: I4a9e56066a7c3f14c7159270503225cd794c1bb6
This commit is contained in:
Alex Dale
2022-06-16 18:00:19 -07:00
parent a3453e8bf5
commit 173b230588
2 changed files with 408 additions and 0 deletions

View File

@@ -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 <stddef.h>
#include <stdint.h>
#include <string>
#include <vector>
#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<uint8_t>& key,
const uint8_t* message, size_t message_length,
uint8_t* signature, size_t* signature_length);
std::vector<uint8_t> HmacSha1(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& 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<uint8_t>& key,
const uint8_t* message, size_t message_length,
uint8_t* signature, size_t* signature_length);
bool HmacSha256(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& message,
std::vector<uint8_t>* signature);
bool HmacSha256(const std::vector<uint8_t>& key, const std::string& message,
std::vector<uint8_t>* signature);
std::vector<uint8_t> HmacSha256(const std::vector<uint8_t>& key,
const uint8_t* message, size_t message_length);
std::vector<uint8_t> HmacSha256(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& message);
std::vector<uint8_t> HmacSha256(const std::vector<uint8_t>& 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<uint8_t>& key,
const uint8_t* message, size_t message_length,
const uint8_t* signature,
size_t signature_length);
OEMCryptoResult HmacSha1Verify(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& message,
const std::vector<uint8_t>& 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<uint8_t>& key,
const uint8_t* message, size_t message_length,
const uint8_t* signature,
size_t signature_length);
OEMCryptoResult HmacSha256Verify(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& message,
const std::vector<uint8_t>& signature);
OEMCryptoResult HmacSha256Verify(const std::vector<uint8_t>& key,
const std::string& message,
const std::vector<uint8_t>& signature);
} // namespace util
} // namespace wvoec
#endif // WVOEC_UTIL_HMAC_H_

View File

@@ -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 <openssl/crypto.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include "log.h"
#include "string_conversions.h"
namespace wvoec {
namespace util {
namespace {
constexpr bool kHmacSha256 = true;
constexpr bool kHmacSha1 = false;
const std::vector<uint8_t> 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<int>(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<uint8_t>* 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<uint8_t>& 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<uint8_t> HmacSha1(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& message) {
std::vector<uint8_t> 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<uint8_t>& 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<uint8_t>& key,
const std::vector<uint8_t>& message,
std::vector<uint8_t>* signature) {
return GenerateSignatureInternal(key.data(), key.size(), message.data(),
message.size(), signature, kHmacSha256);
}
bool HmacSha256(const std::vector<uint8_t>& key, const std::string& message,
std::vector<uint8_t>* signature) {
return GenerateSignatureInternal(
key.data(), key.size(), reinterpret_cast<const uint8_t*>(message.data()),
message.size(), signature, kHmacSha256);
}
std::vector<uint8_t> HmacSha256(const std::vector<uint8_t>& key,
const uint8_t* message, size_t message_length) {
std::vector<uint8_t> signature;
const bool res = GenerateSignatureInternal(
key.data(), key.size(), message, message_length, &signature, kHmacSha256);
return res ? signature : kEmptyVector;
}
std::vector<uint8_t> HmacSha256(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& message) {
std::vector<uint8_t> signature;
const bool res =
GenerateSignatureInternal(key.data(), key.size(), message.data(),
message.size(), &signature, kHmacSha256);
return res ? signature : kEmptyVector;
}
std::vector<uint8_t> HmacSha256(const std::vector<uint8_t>& key,
const std::string& message) {
std::vector<uint8_t> signature;
const bool res = GenerateSignatureInternal(
key.data(), key.size(), reinterpret_cast<const uint8_t*>(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<uint8_t>& 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<uint8_t>& key,
const std::vector<uint8_t>& message,
const std::vector<uint8_t>& 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<uint8_t>& 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<uint8_t>& key,
const std::vector<uint8_t>& message,
const std::vector<uint8_t>& signature) {
return VerifySignatureInternal(key.data(), key.size(), message.data(),
message.size(), signature.data(),
signature.size(), kHmacSha256);
}
OEMCryptoResult HmacSha256Verify(const std::vector<uint8_t>& key,
const std::string& message,
const std::vector<uint8_t>& signature) {
return VerifySignatureInternal(
key.data(), key.size(), reinterpret_cast<const uint8_t*>(message.data()),
message.size(), signature.data(), signature.size(), kHmacSha256);
}
} // namespace util
} // namespace wvoec