1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #4821 from BytesGalore/add_hash_chain

hashes/sha256: initial sha256-chain computation and verification functions
This commit is contained in:
Oleg Hahm 2016-02-25 01:33:57 +01:00
commit 89ccb599f5
6 changed files with 399 additions and 0 deletions

View File

@ -1,6 +1,7 @@
/*-
* Copyright 2005 Colin Percival
* Copyright 2013 Christian Mehlis & René Kijewski
* Copyright 2016 Martin Landsmann <martin.landsmann@haw-hamburg.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -37,11 +38,13 @@
* @author Colin Percival
* @author Christian Mehlis
* @author Rene Kijewski
* @author Martin Landsmann
*
* @}
*/
#include <string.h>
#include <assert.h>
#include "hashes/sha256.h"
#include "board.h"
@ -318,3 +321,140 @@ const unsigned char *hmac_sha256(const unsigned char *key,
return result;
}
/**
* @brief helper to compute sha256 inplace for the given buffer
*
* @param[in, out] element the buffer to compute a sha256 and store it back to it
*
*/
static inline void sha256_inplace(unsigned char element[SHA256_DIGEST_LENGTH])
{
sha256_context_t ctx;
sha256_init(&ctx);
sha256_update(&ctx, element, SHA256_DIGEST_LENGTH);
sha256_final(element, &ctx);
}
unsigned char *sha256_chain(const unsigned char *seed, size_t seed_length,
size_t elements, unsigned char *tail_element)
{
unsigned char tmp_element[SHA256_DIGEST_LENGTH];
/* assert if no sha256-chain can be created */
assert(elements >= 2);
/* 1st iteration */
sha256(seed, seed_length, tmp_element);
/* perform consecutive iterations minus the first one */
for (size_t i = 0; i < (elements - 1); ++i) {
sha256_inplace(tmp_element);
}
/* store the result */
memcpy(tail_element, tmp_element, SHA256_DIGEST_LENGTH);
return tail_element;
}
unsigned char *sha256_chain_with_waypoints(const unsigned char *seed,
size_t seed_length,
size_t elements,
unsigned char *tail_element,
sha256_chain_idx_elm_t *waypoints,
size_t *waypoints_length)
{
/* assert if no sha256-chain can be created */
assert(elements >= 2);
/* assert to prevent division by 0 */
assert(*waypoints_length > 0);
/* assert if no waypoints can be created */
assert(*waypoints_length > 1);
/* if we have enough space we store the whole chain */
if (*waypoints_length >= elements) {
/* 1st iteration */
sha256(seed, seed_length, waypoints[0].element);
waypoints[0].index = 0;
/* perform consecutive iterations starting at index 1*/
for (size_t i = 1; i < elements; ++i) {
sha256_context_t ctx;
sha256_init(&ctx);
sha256_update(&ctx, waypoints[(i - 1)].element, SHA256_DIGEST_LENGTH);
sha256_final(waypoints[i].element, &ctx);
waypoints[i].index = i;
}
/* store the result */
memcpy(tail_element, waypoints[(elements - 1)].element, SHA256_DIGEST_LENGTH);
*waypoints_length = (elements - 1);
return tail_element;
}
else {
unsigned char tmp_element[SHA256_DIGEST_LENGTH];
size_t waypoint_streak = (elements / *waypoints_length);
/* 1st waypoint iteration */
sha256(seed, seed_length, tmp_element);
for (size_t i = 1; i < waypoint_streak; ++i) {
sha256_inplace(tmp_element);
}
memcpy(waypoints[0].element, tmp_element, SHA256_DIGEST_LENGTH);
waypoints[0].index = (waypoint_streak - 1);
/* index of the current computed element in the chain */
size_t index = (waypoint_streak - 1);
/* consecutive waypoint iterations */
size_t j = 1;
for (; j < *waypoints_length; ++j) {
for (size_t i = 0; i < waypoint_streak; ++i) {
sha256_inplace(tmp_element);
index++;
}
memcpy(waypoints[j].element, tmp_element, SHA256_DIGEST_LENGTH);
waypoints[j].index = index;
}
/* store/pass the last used index in the waypoint array */
*waypoints_length = (j - 1);
/* remaining iterations down to elements */
for (size_t i = index; i < (elements - 1); ++i) {
sha256_inplace(tmp_element);
}
/* store the result */
memcpy(tail_element, tmp_element, SHA256_DIGEST_LENGTH);
return tail_element;
}
}
int sha256_chain_verify_element(unsigned char *element,
size_t element_index,
unsigned char *tail_element,
size_t chain_length)
{
unsigned char tmp_element[SHA256_DIGEST_LENGTH];
int delta_count = (chain_length - element_index);
/* assert if we have an index mismatch */
assert(delta_count >= 1);
memcpy((void*)tmp_element, (void*)element, SHA256_DIGEST_LENGTH);
/* perform all consecutive iterations down to tail_element */
for (int i = 0; i < (delta_count - 1); ++i) {
sha256_inplace(tmp_element);
}
/* return if the computed element equals the tail_element */
return (memcmp(tmp_element, tail_element, SHA256_DIGEST_LENGTH) != 0);
}

View File

@ -1,6 +1,7 @@
/*-
* Copyright 2005 Colin Percival
* Copyright 2013 Christian Mehlis & René Kijewski
* Copyright 2016 Martin Landsmann <martin.landsmann@haw-hamburg.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -68,6 +69,16 @@ typedef struct {
unsigned char buf[64];
} sha256_context_t;
/**
* @brief sha256-chain indexed element
*/
typedef struct {
/** the position of this element in its chain */
size_t index;
/** the element */
unsigned char element[SHA256_DIGEST_LENGTH];
} sha256_chain_idx_elm_t;
/**
* @brief SHA-256 initialization. Begins a SHA-256 operation.
*
@ -124,6 +135,74 @@ const unsigned char *hmac_sha256(const unsigned char *key,
size_t message_length,
unsigned char *result);
/**
* @brief function to produce a hash chain statring with a given seed element.
* The chain is computed by taking the sha256 from the seed,
* hash the resulting sha256 and continuing taking sha256
* from each result consecutively.
*
* @param[in] seed the seed of the sha256-chain, i.e. the first element
* @param[in] seed_length the size of seed in bytes
* @param[in] elements the number of chained elements,
* i.e. the index of the last element is (elements-1)
* @param[out] tail_element the final element of the sha256-chain
*
* @returns pointer to tail_element
*/
unsigned char *sha256_chain(const unsigned char *seed, size_t seed_length,
size_t elements, unsigned char *tail_element);
/**
* @brief function to produce a hash chain statring with a given seed element.
* The chain is computed the same way as done with sha256_chain().
* Additionally intermediate elements are saved while computing the chain.
* This slows down computation, but provides the caller with indexed
* "waypoint"-elements.
* They are supposed to shortcut computing verification elements,
* this comes in handy when using long chains,
* e.g. a chain with 2<sup>32</sup> elements.
*
* @param[in] seed the seed of the sha256-chain, i.e. the first element
* @param[in] seed_length the size of seed in bytes
* @param[in] elements the number of chained elements,
* i.e. the index of the last element is (elements-1)
* @param[out] tail_element the final element of the sha256-chain
* @param[out] waypoints intermediate elements are stored there.
* @param[in, out] waypoints_length the size of the waypoints array.
* If the given size is equal or greater elements, the complete
* chain will be stored.
* Otherwise every n-th element is stored where:
* n = floor(elements / waypoints_length);
* floor is implicitly used since we perform unsigned integer division.
* The last used waypoint index is stored in the variable after call.
* That is (elements - 1) if the complete chain is stored,
* and (*waypoints_length - 1) if we only store some waypoints.
*
* @returns pointer to tail_element
*/
unsigned char *sha256_chain_with_waypoints(const unsigned char *seed,
size_t seed_length,
size_t elements,
unsigned char *tail_element,
sha256_chain_idx_elm_t *waypoints,
size_t *waypoints_length);
/**
* @brief function to verify if a given chain element is part of the chain.
*
* @param[in] element the chain element to be verified
* @param[in] element_index the position in the chain
* @param[in] tail_element the last element of the sha256-chain
* @param[in] chain_length the number of elements in the chain
*
* @returns 0 if element is verified to be part of the chain at element_index
* 1 if the element cannot be verified as part of the chain
*/
int sha256_chain_verify_element(unsigned char *element,
size_t element_index,
unsigned char *tail_element,
size_t chain_length);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,158 @@
/*
* Copyright (C) 2016 Martin Landsmann <martin.landsmann@haw-hamburg.de>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup unittests
* @{
*
* @file
* @brief testcases for the sha256-chain implementation
*
* @author Martin Landsmann <martin.landsmann@haw-hamburg.de>
*
* @}
*/
#include <limits.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "embUnit/embUnit.h"
#include "hashes/sha256.h"
#include "tests-hashes.h"
static void test_sha256_hash_chain(void)
{
const char strSeed[] = "My cool secret seed, you'll never guess it ;) 12345";
static unsigned char tail_hash_chain_element[SHA256_DIGEST_LENGTH];
/* we produce a sha256-chain of 257 elements */
size_t elements = 257;
memset(tail_hash_chain_element, 0, SHA256_DIGEST_LENGTH);
TEST_ASSERT(sha256_chain((unsigned char*)strSeed, strlen(strSeed),
elements, tail_hash_chain_element) != NULL);
/* we check if the first element is part of the chain */
unsigned char element_one[SHA256_DIGEST_LENGTH];
sha256((unsigned char*)strSeed, strlen(strSeed), element_one);
TEST_ASSERT(sha256_chain_verify_element(element_one, 0,
tail_hash_chain_element, elements) == 0);
/* now we check if the test fails if the index is wrong */
TEST_ASSERT(sha256_chain_verify_element(element_one, 2,
tail_hash_chain_element, elements) == 1);
/* now we check if other elements are also part of the chain */
unsigned char tmp_element[SHA256_DIGEST_LENGTH];
memcpy((void*)tmp_element, (void*)element_one, SHA256_DIGEST_LENGTH);
/* since we know the seed we build every element and test if its in the chain */
for (size_t i = 1; i < elements; ++i) {
sha256_context_t ctx;
sha256_init(&ctx);
sha256_update(&ctx, tmp_element, SHA256_DIGEST_LENGTH);
sha256_final(tmp_element, &ctx);
TEST_ASSERT(sha256_chain_verify_element(tmp_element, i,
tail_hash_chain_element, elements) == 0);
}
}
static void test_sha256_hash_chain_with_waypoints(void)
{
const char strSeed[] = "My cool secret seed, you'll never guess it ;P 123456!";
static unsigned char tail_hash_chain_element[SHA256_DIGEST_LENGTH];
/* we produce a sha256-chain of 257 elements */
size_t elements = 257;
/* the first element of the hash chain */
unsigned char element_one[SHA256_DIGEST_LENGTH];
sha256((unsigned char*)strSeed, strlen(strSeed), element_one);
/* now we check storing some waypoints, lets say 10 */
size_t waypoints_length = 10;
sha256_chain_idx_elm_t waypoints[waypoints_length];
memset(tail_hash_chain_element, 0, SHA256_DIGEST_LENGTH);
sha256_chain_with_waypoints((unsigned char*)strSeed,
strlen(strSeed),
elements,
tail_hash_chain_element,
waypoints,
&waypoints_length);
/* we test if the chain has been computed properly */
TEST_ASSERT(sha256_chain_verify_element(element_one, 0,
tail_hash_chain_element, elements) == 0);
/* and we check if our waypoints are properly stored */
for (size_t i = 0; i < (waypoints_length + 1); ++i) {
TEST_ASSERT(sha256_chain_verify_element(waypoints[i].element,
waypoints[i].index,
tail_hash_chain_element, elements) == 0);
}
}
static void test_sha256_hash_chain_store_whole(void)
{
const char strSeed[] = "My cool secret seed, you'll never guess it ;P 123456!";
static unsigned char tail_hash_chain_element[SHA256_DIGEST_LENGTH];
/* now we check storing the whole chain
* not a too large one though to remain inside the stack bounds
*/
size_t elements = 17;
/* the first element of the hash chain */
unsigned char element_one[SHA256_DIGEST_LENGTH];
sha256((unsigned char*)strSeed, strlen(strSeed), element_one);
size_t whole_chain_length = elements;
sha256_chain_idx_elm_t waypoints_whole_chain[whole_chain_length];
memset(tail_hash_chain_element, 0, SHA256_DIGEST_LENGTH);
sha256_chain_with_waypoints((unsigned char*)strSeed,
strlen(strSeed),
whole_chain_length,
tail_hash_chain_element,
waypoints_whole_chain,
&whole_chain_length);
/* we test again if the chain has been computed properly */
TEST_ASSERT(sha256_chain_verify_element(element_one, 0,
tail_hash_chain_element, elements) == 0);
/* and we check if our complete chain has been properly stored */
TEST_ASSERT( (whole_chain_length + 1) == elements );
for (size_t i = 0; i < (whole_chain_length + 1); ++i) {
TEST_ASSERT(sha256_chain_verify_element(waypoints_whole_chain[i].element,
waypoints_whole_chain[i].index,
tail_hash_chain_element, elements) == 0);
}
}
Test *tests_hashes_sha256_chain_tests(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
new_TestFixture(test_sha256_hash_chain),
new_TestFixture(test_sha256_hash_chain_with_waypoints),
new_TestFixture(test_sha256_hash_chain_store_whole),
};
EMB_UNIT_TESTCALLER(hashes_sha256_tests, NULL, NULL,
fixtures);
return (Test *)&hashes_sha256_tests;
}

View File

@ -8,6 +8,20 @@
* directory for more details.
*/
/**
* @ingroup unittests
* @{
*
* @file
* @brief testcases for the sha256 implementation
*
* @author Christian Mehlis <mehlis@inf.fu-berlin.de>
* @author Philipp Rosenkranz <philipp.rosenkranz@fu-berlin.de>
* @author Martin Landsmann <martin.landsmann@haw-hamburg.de>
*
* @}
*/
#include <limits.h>
#include <string.h>
#include <stdio.h>

View File

@ -26,4 +26,5 @@ void tests_hashes(void)
TESTS_RUN(tests_hashes_sha1_tests());
TESTS_RUN(tests_hashes_sha256_tests());
TESTS_RUN(tests_hashes_sha256_hmac_tests());
TESTS_RUN(tests_hashes_sha256_chain_tests());
}

View File

@ -58,6 +58,13 @@ Test *tests_hashes_sha256_tests(void);
*/
Test *tests_hashes_sha256_hmac_tests(void);
/**
* @brief Generates tests for hashes/sha256.h - sha256-chain
*
* @return embUnit tests if successful, NULL if not.
*/
Test *tests_hashes_sha256_chain_tests(void);
#ifdef __cplusplus
}
#endif