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:
commit
89ccb599f5
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
158
tests/unittests/tests-hashes/tests-hashes-sha256-chain.c
Normal file
158
tests/unittests/tests-hashes/tests-hashes-sha256-chain.c
Normal 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;
|
||||
}
|
@ -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>
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user