Skip to content

Commit

Permalink
Added HKDF support for Hash subclasses.
Browse files Browse the repository at this point in the history
  • Loading branch information
mlaz committed Nov 13, 2016
1 parent 6d71947 commit 40e2dd2
Show file tree
Hide file tree
Showing 2 changed files with 349 additions and 0 deletions.
140 changes: 140 additions & 0 deletions libraries/Crypto/HKDF.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
* Copyright (C) 2016 Miguel Azevedo, <lobaoazevedo@ua.pt>.
*
* 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"

/**
* \class HKDF HKDF.h <HKDF.h>
* \brief HKDF support for the hashing algorithms.
*
* Reference: RFC5869 - https://tools.ietf.org/html/rfc5869
*
* \sa Hash, SHA256
*/

template < typename T >
class HKDF
{
public:
~HKDF() { delete & hash; }

/**
* \brief First phase of HKDF.
* \brief Extracts a pseudorandom key from a given key and salt.
* \param salt Optional (might be NULL) salt value (a non-secret random value);
* \param saltLen The salt length in bytes. If salt is NUll this value is ignored.
* \param inputKey Input keying material.
* \param keyLen inputKey length in bytes.
* \param outputKey a previous allocated buffer of the same length of the
* hash function being used.
*
* Reference: RFC5869 - https://tools.ietf.org/html/rfc5869
*
* \sa expand()
*/
void extract(void *salt, size_t saltLen, void *inputKey, size_t keyLen, void *outputKey)
{
uint8_t s[hash.hashSize()];
if (salt == NULL) {
memset(s, 0, hash.hashSize());
salt = s;
saltLen = hash.hashSize();
}

hash.resetHMAC(salt, saltLen);
hash.update(inputKey, keyLen);
hash.finalizeHMAC(salt, saltLen, outputKey, hash.hashSize());
}

/**
* \brief Second phase of HKDF.
* \brief Expands a given pseudorandom key using a given info.
* \param inputKey The pseudorandom key extracted with extract().
* \param info The optional (can be NULL) context and application specific information.
* \param infoLen The length of info. If info is NULL this value is ignored.
* \param outputKey Output keying material. A buffer of L lenght bytes.
* \param L The length of outputKey in bytes.
*
* \sa extract()
*/
void expand(void *inputKey, void *info, size_t infoLen,void *outputKey, size_t L)
{
int N = L / hash.hashSize();
uint8_t i;

infoLen = (info == NULL) ? 0 : infoLen;
size_t saltLen = hash.hashSize() + infoLen + 1;
uint8_t salt[saltLen];
salt[saltLen - 1] = 1;

if (info != NULL) {
memcpy(salt + hash.hashSize(), info, infoLen);
}
// Calculate T(1)
hash.resetHMAC(inputKey, hash.hashSize());
hash.update(salt + hash.hashSize(), infoLen + 1);
hash.finalizeHMAC(inputKey, hash.hashSize(), outputKey, hash.hashSize());

salt[saltLen - 1] += 1;
memcpy(salt, outputKey, hash.hashSize());

// Calculate T(2) ... T(N)
for (i = 1; i < N; i++) {
hash.resetHMAC(inputKey, hash.hashSize());
hash.update(salt, saltLen);
hash.finalizeHMAC(inputKey,
hash.hashSize(),
((uint8_t *) outputKey) + (i * hash.hashSize()),
hash.hashSize());

salt[saltLen - 1] += 1;
memcpy(salt,
((uint8_t *) outputKey) + (i * hash.hashSize()),
hash.hashSize());
}

// Process remaining octets if there are any.
if (L % hash.hashSize()) {
uint8_t rslt[hash.hashSize()];
int remain = L - N * hash.hashSize();
hash.resetHMAC(inputKey, hash.hashSize());
hash.update(salt, saltLen);
hash.finalizeHMAC(inputKey,
hash.hashSize(),
rslt,
hash.hashSize());

memcpy(((uint8_t *) outputKey) + (N * hash.hashSize()),
rslt,
remain);
}
}


private:
T hash;
};

#endif
209 changes: 209 additions & 0 deletions libraries/Crypto/examples/TestHKDF/TestHKDF.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
/**
* This example runs tests on the HKDF implementation to verify correct
* behaviour using SHA256 hashing function.
* These test vectors are based on the ones supplied by RFC5869.
* Author: Miguel Azevedo <lobaoazevedo@ua.pt>
*/

#include <Crypto.h>
#include <SHA256.h>
#include <HKDF.h>
#include <string.h>

#define HASH_SIZE 32

struct TestHashVectorHKDF {
const char *name;
uint8_t *IKM;
size_t IKM_size;
uint8_t *salt;
size_t salt_size;
uint8_t *info;
size_t info_size;
uint8_t L;

uint8_t PRK[HASH_SIZE];
uint8_t *OKM;
};

uint8_t ikm1[22] = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
};

uint8_t salt1[13] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c
};

uint8_t info1[10] = { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9
};

uint8_t okm1[42] = { 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a,
0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a,
0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c,
0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf,
0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18,
0x58, 0x65
};

static TestHashVectorHKDF const testVectorHKDF_SHA256_1 = {
"HKDF-SHA-256 #1",
ikm1,
22,
salt1,
13,
info1,
10,
42,
{0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf,
0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63,
0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31,
0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5},
okm1
};

uint8_t ikm2[80] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f
};

uint8_t salt2[80] = { 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf
};

uint8_t info2[80] = { 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
};

uint8_t okm2[82] = { 0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1,
0xc8, 0xe7, 0xf7, 0x8c, 0x59, 0x6a, 0x49, 0x34,
0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8,
0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, 0xa9, 0x7c,
0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72,
0x71, 0xcb, 0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09,
0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8,
0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71,
0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec, 0x3e, 0x87,
0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f,
0x1d, 0x87
};

static TestHashVectorHKDF const testVectorHKDF_SHA256_2 = {
"HKDF-SHA-256 #2",
ikm2,
80,
salt2,
80,
info2,
80,
82,
{0x06, 0xa6, 0xb8, 0x8c, 0x58, 0x53, 0x36, 0x1a,
0x06, 0x10, 0x4c, 0x9c, 0xeb, 0x35, 0xb4, 0x5c,
0xef, 0x76, 0x00, 0x14, 0x90, 0x46, 0x71, 0x01,
0x4a, 0x19, 0x3f, 0x40, 0xc1, 0x5f, 0xc2, 0x44},
okm2
};

uint8_t ikm3[22] = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
};

uint8_t okm3[42] = { 0x8d, 0xa4, 0xe7, 0x75, 0xa5, 0x63, 0xc1, 0x8f,
0x71, 0x5f, 0x80, 0x2a, 0x06, 0x3c, 0x5a, 0x31,
0xb8, 0xa1, 0x1f, 0x5c, 0x5e, 0xe1, 0x87, 0x9e,
0xc3, 0x45, 0x4e, 0x5f, 0x3c, 0x73, 0x8d, 0x2d,
0x9d, 0x20, 0x13, 0x95, 0xfa, 0xa4, 0xb6, 0x1a,
0x96, 0xc8
};

static TestHashVectorHKDF const testVectorHKDF_SHA256_3 = {
"HKDF-SHA-256 #3",
ikm1,
22,
NULL,
0,
NULL,
0,
42,
{0x19, 0xef, 0x24, 0xa3, 0x2c, 0x71, 0x7b, 0x16,
0x7f, 0x33, 0xa9, 0x1d, 0x6f, 0x64, 0x8b, 0xdf,
0x96, 0x59, 0x67, 0x76, 0xaf, 0xdb, 0x63, 0x77,
0xac, 0x43, 0x4c, 0x1c, 0x29, 0x3c, 0xcb, 0x04},
okm3
};

HKDF < SHA256 > hkdfsha256;

void testHKDF(HKDF < SHA256 > *hash, const struct TestHashVectorHKDF *test)
{
uint8_t prk[HASH_SIZE];
uint8_t okm[test->L];
Serial.print(test->name);
Serial.print(" ... ");

hash->extract(test->salt,
test->salt_size,
test->IKM,
test->IKM_size,
prk);

if (!memcmp(prk, test->PRK, HASH_SIZE))
Serial.print("Extract: Passed");
else
Serial.print("Extract Failed");


Serial.print(" ... ");
hash->expand(prk, test->info, test->info_size, okm, test->L);

if (!memcmp(okm, test->OKM, test->L))
Serial.println("Expand: Passed");
else
Serial.println("Expand: Failed");

}

void setup()
{
Serial.begin(9600);

Serial.println();

Serial.print("State Size ...");
Serial.println(sizeof(SHA256));
Serial.println();

Serial.println("Test Vectors:");
testHKDF(&hkdfsha256, &testVectorHKDF_SHA256_1);
testHKDF(&hkdfsha256, &testVectorHKDF_SHA256_2);
testHKDF(&hkdfsha256, &testVectorHKDF_SHA256_3);
}

void loop()
{
}

0 comments on commit 40e2dd2

Please sign in to comment.