Skip to content

Commit

Permalink
HKDF implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
rweather committed Dec 30, 2021
1 parent 54263c8 commit 4429613
Show file tree
Hide file tree
Showing 6 changed files with 438 additions and 0 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ improvements, please contact the author Rhys Weatherley via
Recent significant changes to the library
-----------------------------------------

Jan 2022:

* All-in-one hmac() function in Hash.h for simplified HMAC computations.
* New API for the HKDF hash-based key derivation function.

Apr 2018:

* Acorn128 and Ascon128 authenticated ciphers (finalists in the CAESAR AEAD
Expand Down
1 change: 1 addition & 0 deletions doc/crypto.dox
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ in the repository:
\li Block cipher modes: CTR, EAX, GCM, XTS
\li Stream ciphers: ChaCha
\li Hash algorithms: SHA224, SHA256, SHA384, SHA512, SHA3_256, SHA3_512, BLAKE2s, BLAKE2b (regular and HMAC modes)
\li Hash algorithm modes: HKDF
\li Extendable output functions (XOF's): SHAKE128, SHAKE256
\li Message authenticators: Poly1305, GHASH, OMAC
\li Public key algorithms: Curve25519, Ed25519, P521
Expand Down
2 changes: 2 additions & 0 deletions host/Crypto/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ SOURCES = \
GF128.cpp \
GHASH.cpp \
Hash.cpp \
HKDF.cpp \
KeccakCore.cpp \
NewHope.cpp \
NoiseSource.cpp \
Expand Down Expand Up @@ -96,6 +97,7 @@ SKETCHES = \
TestCurve25519Math/TestCurve25519Math.ino \
TestEAX/TestEAX.ino \
TestEd25519/TestEd25519.ino \
TestHKDF/TestHKDF.ino \
TestGCM/TestGCM.ino \
TestGHASH/TestGHASH.ino \
TestNewHope/TestNewHope.ino \
Expand Down
209 changes: 209 additions & 0 deletions libraries/Crypto/HKDF.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
/*
* Copyright (C) 2022 Southern Storm Software, Pty Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/

#include "HKDF.h"
#include <string.h>

/**
* \class HKDFCommon HKDF.h <HKDF.h>
* \brief Concrete base class to assist with implementing HKDF mode for
* hash algorithms.
*
* Reference: https://datatracker.ietf.org/doc/html/rfc5869
*
* \sa HKDF
*/

/**
* \brief Constructs a new HKDF instance.
*
* This constructor must be followed by a call to setHashAlgorithm().
*/
HKDFCommon::HKDFCommon()
: hash(0)
, buf(0)
, counter(1)
, posn(255)
{
}

/**
* \brief Destroys this HKDF instance.
*/
HKDFCommon::~HKDFCommon()
{
}

/**
* \brief Sets the key and salt for a HKDF session.
*
* \param key Points to the key.
* \param keyLen Length of the \a key in bytes.
* \param salt Points to the salt.
* \param saltLen Length of the \a salt in bytes.
*/
void HKDFCommon::setKey(const void *key, size_t keyLen, const void *salt, size_t saltLen)
{
// Initialise the HKDF context with the key and salt to generate the PRK.
size_t hashSize = hash->hashSize();
if (salt && saltLen) {
hash->resetHMAC(salt, saltLen);
hash->update(key, keyLen);
hash->finalizeHMAC(salt, saltLen, buf + hashSize, hashSize);
} else {
// If no salt is provided, RFC 5869 says that a string of
// hashSize zeroes should be used instead.
memset(buf, 0, hashSize);
hash->resetHMAC(buf, hashSize);
hash->update(key, keyLen);
hash->finalizeHMAC(buf, hashSize, buf + hashSize, hashSize);
}
counter = 1;
posn = hashSize;
}

/**
* \brief Extracts data from a HKDF session.
*
* \param out Points to the buffer to fill with extracted data.
* \param outLen Number of bytes to extract into the \a out buffer.
* \param info Points to the application-specific information string.
* \param infoLen Length of the \a info string in bytes.
*
* \note RFC 5869 specifies that a maximum of 255 * HashLen bytes
* should be extracted from a HKDF session. This maximum is not
* enforced by this function.
*/
void HKDFCommon::extract(void *out, size_t outLen, const void *info, size_t infoLen)
{
size_t hashSize = hash->hashSize();
uint8_t *outPtr = (uint8_t *)out;
while (outLen > 0) {
// Generate a new output block if necessary.
if (posn >= hashSize) {
hash->resetHMAC(buf + hashSize, hashSize);
if (counter != 1)
hash->update(buf, hashSize);
if (info && infoLen)
hash->update(info, infoLen);
hash->update(&counter, 1);
hash->finalizeHMAC(buf + hashSize, hashSize, buf, hashSize);
++counter;
posn = 0;
}

// Copy as much output data as we can for this block.
size_t len = hashSize - posn;
if (len > outLen)
len = outLen;
memcpy(outPtr, buf + posn, len);
posn += len;
outPtr += len;
outLen -= len;
}
}

/**
* \brief Clears sensitive information from this HKDF instance.
*/
void HKDFCommon::clear()
{
size_t hashSize = hash->hashSize();
hash->clear();
clean(buf, hashSize * 2);
counter = 1;
posn = hashSize;
}

/**
* \fn void HKDFCommon::setHashAlgorithm(Hash *hashAlg, uint8_t *buffer)
* \brief Sets the hash algorithm to use for HKDF operations.
*
* \param hashAlg Points to the hash algorithm instance to use.
* \param buffer Points to a buffer that must be at least twice the
* size of the hash output from \a hashAlg.
*/

/**
* \class HKDF HKDF.h <HKDF.h>
* \brief Implementation of the HKDF mode for hash algorithms.
*
* HKDF expands a key to a larger amount of material that can be used
* for cryptographic operations. It is based around a hash algorithm.
*
* The template parameter T must be a concrete subclass of Hash
* indicating the specific hash algorithm to use.
*
* The following example expands a 32-byte / 256-bit key into
* 128 bytes / 1024 bits of key material for use in a cryptographic session:
*
* \code
* uint8_t key[32] = {...};
* uint8_t output[128];
* HKDF<SHA256> hkdf;
* hkdf.setKey(key, sizeof(key));
* hkdf.extract(output, sizeof(output));
* \endcode
*
* Usually the key will be salted, which can be passed to the setKey()
* function:
*
* \code
* hkdf.setKey(key, sizeof(key), salt, sizeof(salt));
* \endcode
*
* It is also possible to acheive the same effect with a single function call
* using the hkdf() templated function:
*
* \code
* hkdf<SHA256>(output, sizeof(output), key, sizeof(key),
* salt, sizeof(salt), info, sizeof(info));
* \endcode
*
* Reference: https://datatracker.ietf.org/doc/html/rfc5869
*/

/**
* \fn HKDF::HKDF()
* \brief Constructs a new HKDF object for the hash algorithm T.
*/

/**
* \fn HKDF::~HKDF()
* \brief Destroys a HKDF instance and all sensitive data within it.
*/

/**
* \fn void hkdf<T>(void *out, size_t outLen, const void *key, size_t keyLen, const void *salt, size_t saltLen, const void *info, size_t infoLen)
* \brief All-in-one implementation of HKDF using a hash algorithm.
*
* \param out Points to the buffer to fill with extracted data.
* \param outLen Number of bytes to extract into the \a out buffer.
* \param key Points to the key.
* \param keyLen Length of the \a key in bytes.
* \param salt Points to the salt.
* \param saltLen Length of the \a salt in bytes.
* \param info Points to the application-specific information string.
* \param infoLen Length of the \a info string in bytes.
*
* The template parameter T must be a subclass of Hash.
*/
76 changes: 76 additions & 0 deletions libraries/Crypto/HKDF.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright (C) 2022 Southern Storm Software, Pty Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/

#ifndef CRYPTO_HKDF_h
#define CRYPTO_HKDF_h

#include "Hash.h"
#include "Crypto.h"

class HKDFCommon
{
public:
virtual ~HKDFCommon();

void setKey(const void *key, size_t keyLen, const void *salt = 0, size_t saltLen = 0);

void extract(void *out, size_t outLen, const void *info = 0, size_t infoLen = 0);

void clear();

protected:
HKDFCommon();
void setHashAlgorithm(Hash *hashAlg, uint8_t *buffer)
{
hash = hashAlg;
buf = buffer;
}

private:
Hash *hash;
uint8_t *buf;
uint8_t counter;
uint8_t posn;
};

template <typename T>
class HKDF : public HKDFCommon
{
public:
HKDF() { setHashAlgorithm(&hashAlg, buffer); }
~HKDF() { ::clean(buffer, sizeof(buffer)); }

private:
T hashAlg;
uint8_t buffer[T::HASH_SIZE * 2];
};

template <typename T> void hkdf
(void *out, size_t outLen, const void *key, size_t keyLen,
const void *salt, size_t saltLen, const void *info, size_t infoLen)
{
HKDF<T> context;
context.setKey(key, keyLen, salt, saltLen);
context.extract(out, outLen, info, infoLen);
}

#endif
Loading

0 comments on commit 4429613

Please sign in to comment.