blob: 38e4b99c32ebb2a7dc5ccc6d89db9617b3fe23d2 [file] [log] [blame]
/*
*
* Copyright (c) 2019 Google LLC.
* Copyright (c) 2013-2017 Nest Labs, Inc.
* All rights reserved.
*
* 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.
*/
/**
* @file
* This file implements a template object for doing keyed-hash
* message authentication code (HMAC) and specialized objects
* for HMAC with SHA-1 and SHA-256.
*
*/
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
#include <stdint.h>
#include <string.h>
#include "WeaveCrypto.h"
#include "HMAC.h"
#include <Weave/Support/CodeUtils.h>
namespace nl {
namespace Weave {
namespace Crypto {
using namespace nl::Weave::TLV;
template <class H>
HMAC<H>::HMAC()
{
Reset();
}
template <class H>
HMAC<H>::~HMAC()
{
Reset();
}
template <class H>
void HMAC<H>::Begin(const uint8_t *key, uint16_t keyLen)
{
uint8_t pad[kBlockLength];
Reset();
// Copy the key. If the key is larger than a block, hash it an use the result as the key.
if (keyLen > kBlockLength)
{
mHash.Begin();
mHash.AddData(key, keyLen);
mHash.Finish(mKey);
mKeyLen = keyLen = kDigestLength;
}
else
{
memcpy(mKey, key, keyLen);
mKeyLen = keyLen;
}
// Form the pad for the inner hash.
memcpy(pad, mKey, mKeyLen);
if (keyLen < kBlockLength)
memset(pad + mKeyLen, 0, kBlockLength - mKeyLen);
for (size_t i = 0; i < kBlockLength; i++)
pad[i] = pad[i] ^ 0x36;
// Begin generating the inner hash starting with the pad.
mHash.Begin();
mHash.AddData(pad, kBlockLength);
ClearSecretData(pad, sizeof(kBlockLength));
}
template <class H>
void HMAC<H>::AddData(const uint8_t *msgData, uint16_t dataLen)
{
// Add a chunk of data to the inner hash.
mHash.AddData(msgData, dataLen);
}
#if WEAVE_WITH_OPENSSL
template <class H>
void HMAC<H>::AddData(const BIGNUM& num)
{
// Add a chunk of data to the inner hash.
mHash.AddData(num);
}
#endif
template <class H>
void HMAC<H>::Finish(uint8_t *hashBuf)
{
uint8_t pad[kBlockLength];
uint8_t innerHash[kDigestLength];
// Finalize the inner hash.
mHash.Finish(innerHash);
// Form the pad for the outer hash.
memcpy(pad, mKey, mKeyLen);
if (mKeyLen < kBlockLength)
memset(pad + mKeyLen, 0, kBlockLength - mKeyLen);
for (size_t i = 0; i < kBlockLength; i++)
pad[i] = pad[i] ^ 0x5c;
// Generate the outer hash from the pad and the inner hash.
mHash.Begin();
mHash.AddData(pad, kBlockLength);
mHash.AddData(innerHash, kDigestLength);
mHash.Finish(hashBuf);
// Clear state.
Reset();
ClearSecretData(pad, sizeof(kBlockLength));
ClearSecretData(innerHash, sizeof(kDigestLength));
}
template <class H>
void HMAC<H>::Reset()
{
mHash.Reset();
ClearSecretData(mKey, sizeof(mKey));
mKeyLen = 0;
}
template class HMAC<Platform::Security::SHA1>;
template class HMAC<Platform::Security::SHA256>;
/**
* Compares with another HMAC signature.
*
* @param[in] other The EncodedHMACSignature object with which signature should be compared.
*
* @retval true The signatures are equal.
* @retval false The signatures are not equal.
*/
bool EncodedHMACSignature::IsEqual(const EncodedHMACSignature& other) const
{
return (Sig != NULL &&
other.Sig != NULL &&
Len == other.Len &&
memcmp(Sig, other.Sig, Len) == 0);
}
/**
* Reads the signature as a Weave HMACSignature structure from the specified TLV reader.
*
* @param[in] reader The TLVReader object from which the encoded signature should
* be read.
*
* @retval #WEAVE_NO_ERROR If the operation succeeded.
* @retval other Other Weave error codes related to signature reading.
*/
WEAVE_ERROR EncodedHMACSignature::ReadSignature(TLVReader& reader)
{
WEAVE_ERROR err;
VerifyOrExit(reader.GetType() == kTLVType_ByteString, err = WEAVE_ERROR_WRONG_TLV_TYPE);
err = reader.GetDataPtr(const_cast<const uint8_t *&>(Sig));
SuccessOrExit(err);
Len = reader.GetLength();
exit:
return err;
}
/**
* Generate and encode a Weave HMAC signature
*
* Computes an HMAC signature for a given data using secret key and writes the signature
* as a Weave HMACSignature structure to the specified TLV writer with the given tag.
*
* @param[in] sigAlgoOID Algorithm OID to be used to generate HMAC signature.
* @param[in] writer The TLVWriter object to which the encoded signature should
* be written.
* @param[in] tag TLV tag to be associated with the encoded signature structure.
* @param[in] data A buffer containing the data to be signed.
* @param[in] dataLen The length in bytes of the data.
* @param[in] key A buffer containing the secret key to be used to generate HMAC
* signature.
* @param[in] keyLen The length in bytes of the secret key.
*
* @retval #WEAVE_NO_ERROR If the operation succeeded.
* @retval other Other Weave error codes related to the signature encoding.
*/
WEAVE_ERROR GenerateAndEncodeWeaveHMACSignature(OID sigAlgoOID,
TLVWriter& writer, uint64_t tag,
const uint8_t * data, uint16_t dataLen,
const uint8_t * key, uint16_t keyLen)
{
WEAVE_ERROR err;
HMACSHA256 hmac;
uint8_t hmacSig[HMACSHA256::kDigestLength];
EncodedHMACSignature sig;
// Current implementation only supports HMACWithSHA256 signature algorithm.
VerifyOrExit(sigAlgoOID == ASN1::kOID_SigAlgo_HMACWithSHA256, err = WEAVE_ERROR_UNSUPPORTED_SIGNATURE_TYPE);
// Generate the MAC.
hmac.Begin(key, keyLen);
hmac.AddData(data, dataLen);
hmac.Finish(hmacSig);
sig.Sig = hmacSig;
sig.Len = HMACSHA256::kDigestLength;
// Encode an HMACSignature value into the supplied writer.
err = sig.WriteSignature(writer, tag);
SuccessOrExit(err);
exit:
return err;
}
/**
* Verify a Weave HMAC signature.
*
* Verifies an HMAC signature using given data and a secret key to be used to verify the signature.
*
* @param[in] sigAlgoOID Algorithm OID to be used to generate HMAC signature.
* @param[in] data A buffer containing the data to be signed.
* @param[in] dataLen The length in bytes of the data.
* @param[in] sig Encoded HMAC signature to be verified.
* @param[in] key A buffer containing the secret key to be used to generate HMAC
* signature.
* @param[in] keyLen The length in bytes of the secret key.
*
* @retval #WEAVE_NO_ERROR If HMAC signature verification succeeded.
* @retval #WEAVE_ERROR_INVALID_SIGNATURE
* If HMAC signature verification failed.
*/
WEAVE_ERROR VerifyHMACSignature(OID sigAlgoOID,
const uint8_t * data, uint16_t dataLen,
const EncodedHMACSignature& sig,
const uint8_t * key, uint16_t keyLen)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
HMACSHA256 hmac;
uint8_t hmacSig[HMACSHA256::kDigestLength];
EncodedHMACSignature lSig;
// Current implementation only supports HMACWithSHA256 signature algorithm.
VerifyOrExit(sigAlgoOID == ASN1::kOID_SigAlgo_HMACWithSHA256, err = WEAVE_ERROR_UNSUPPORTED_SIGNATURE_TYPE);
// Generate the MAC.
hmac.Begin(key, keyLen);
hmac.AddData(data, dataLen);
hmac.Finish(hmacSig);
lSig.Sig = hmacSig;
lSig.Len = HMACSHA256::kDigestLength;
VerifyOrExit(lSig.IsEqual(sig), err = WEAVE_ERROR_INVALID_SIGNATURE);
exit:
return err;
}
// ==================== Documentation for Inline Public Members ====================
/**
* @fn WEAVE_ERROR EncodedHMACSignature::WriteSignature(TLVWriter& writer, uint64_t tag) const
*
* Writes the signature as a Weave HMACSignature structure to the specified TLV writer
* with the given tag.
*
* @param[in] writer The TLVWriter object to which the encoded signature should
* be written.
* @param[in] tag TLV tag to be associated with the encoded signature structure.
* @retval #WEAVE_NO_ERROR If the operation succeeded.
* @retval other Other Weave error codes related to signature writing.
*/
} // namespace Crypto
} // namespace Weave
} // namespace nl