Merge pull request #4701 from OlegHahm/sha1

hashes: Import of SHA-1 algorithm
pr/gpio
Oleg Hahm 7 years ago
commit e0732a5a81

@ -0,0 +1,214 @@
/*
* Copyright (C) 2016 Oliver Hahm <oliver.hahm@inria.fr>
*
* 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.
*/
/* This code is public-domain - it is based on libcrypt
* placed in the public domain by Wei Dai and other contributors.
*/
/**
* @defgroup sys_hashes_sha1 SHA-1
* @ingroup sys_hashes
* @brief Implementation of the SHA-1 hashing function
* @{
*
* @file
* @brief SHA-1 interface definition
*
* @author Wei Dai and others
* @author Oliver Hahm <oliver.hahm@inria.fr>
*/
#include <stdint.h>
#include <string.h>
#include "hashes/sha1.h"
#define SHA1_K0 0x5a827999
#define SHA1_K20 0x6ed9eba1
#define SHA1_K40 0x8f1bbcdc
#define SHA1_K60 0xca62c1d6
void sha1_init(sha1_context *s)
{
s->state[0] = 0x67452301;
s->state[1] = 0xefcdab89;
s->state[2] = 0x98badcfe;
s->state[3] = 0x10325476;
s->state[4] = 0xc3d2e1f0;
s->byte_count = 0;
s->buffer_offset = 0;
}
static uint32_t sha1_rol32(uint32_t number, uint8_t bits)
{
return ((number << bits) | (number >> (32 - bits)));
}
static void sha1_hash_block(sha1_context *s)
{
uint8_t i;
uint32_t a, b, c, d, e, t;
a = s->state[0];
b = s->state[1];
c = s->state[2];
d = s->state[3];
e = s->state[4];
for (i = 0; i < 80; i++) {
if (i >= 16) {
t = s->buffer[(i + 13) & 15] ^ s->buffer[(i + 8) & 15] ^
s->buffer[(i + 2) & 15] ^ s->buffer[i & 15];
s->buffer[i & 15] = sha1_rol32(t, 1);
}
if (i < 20) {
t = (d ^ (b & (c ^ d))) + SHA1_K0;
}
else if (i < 40) {
t = (b ^ c ^ d) + SHA1_K20;
}
else if (i < 60) {
t = ((b & c) | (d & (b | c))) + SHA1_K40;
}
else {
t = (b ^ c ^ d) + SHA1_K60;
}
t += sha1_rol32(a, 5) + e + s->buffer[i & 15];
e = d;
d = c;
c = sha1_rol32(b, 30);
b = a;
a = t;
}
s->state[0] += a;
s->state[1] += b;
s->state[2] += c;
s->state[3] += d;
s->state[4] += e;
}
static void sha1_add_uncounted(sha1_context *s, uint8_t data)
{
uint8_t *const b = (uint8_t *) s->buffer;
#ifdef __BIG_ENDIAN__
b[s->buffer_offset] = data;
#else
b[s->buffer_offset ^ 3] = data;
#endif
s->buffer_offset++;
if (s->buffer_offset == SHA1_BLOCK_LENGTH) {
sha1_hash_block(s);
s->buffer_offset = 0;
}
}
static void sha1_update_byte(sha1_context *s, unsigned char data)
{
++s->byte_count;
sha1_add_uncounted(s, data++);
}
void sha1_update(sha1_context *s, const unsigned char *data, size_t len)
{
while (len--) {
sha1_update_byte(s, *(data++));
}
}
static void sha1_pad(sha1_context *s)
{
/* Implement SHA-1 padding (fips180-2 §5.1.1) */
/* Pad with 0x80 followed by 0x00 until the end of the block */
sha1_add_uncounted(s, 0x80);
while (s->buffer_offset != 56) {
sha1_add_uncounted(s, 0x00);
}
/* Append length in the last 8 bytes */
sha1_add_uncounted(s, 0); /* We're only using 32 bit lengths */
sha1_add_uncounted(s, 0); /* But SHA-1 supports 64 bit lengths */
sha1_add_uncounted(s, 0); /* So zero pad the top bits */
sha1_add_uncounted(s, s->byte_count >> 29); /* Shifting to multiply by 8 */
sha1_add_uncounted(s, s->byte_count >> 21); /* as SHA-1 supports bitstreams as well as */
sha1_add_uncounted(s, s->byte_count >> 13); /* byte. */
sha1_add_uncounted(s, s->byte_count >> 5);
sha1_add_uncounted(s, s->byte_count << 3);
}
uint8_t *sha1_final(sha1_context *s)
{
/* Pad to complete the last block */
sha1_pad(s);
/* Swap byte order back */
for (int i = 0; i < 5; i++) {
s->state[i] =
(((s->state[i]) << 24) & 0xff000000)
| (((s->state[i]) << 8) & 0x00ff0000)
| (((s->state[i]) >> 8) & 0x0000ff00)
| (((s->state[i]) >> 24) & 0x000000ff);
}
/* Return pointer to hash (20 characters) */
return (uint8_t *) s->state;
}
void sha1(uint8_t *dst, const uint8_t *src, size_t len)
{
sha1_context ctx;
sha1_init(&ctx);
sha1_update(&ctx, (unsigned char *) src, len);
memcpy(dst, sha1_final(&ctx), SHA1_DIGEST_LENGTH);
}
#define HMAC_IPAD 0x36
#define HMAC_OPAD 0x5c
void sha1_init_hmac(sha1_context *s, const unsigned char *key, size_t key_length)
{
uint8_t i;
memset(s->key_buffer, 0, SHA1_BLOCK_LENGTH);
if (key_length > SHA1_BLOCK_LENGTH) {
/* Hash long keys */
sha1_init(s);
while(key_length--) {
sha1_update_byte(s, (unsigned char) *key++);
}
memcpy(s->key_buffer, sha1_final(s), SHA1_DIGEST_LENGTH);
}
else {
/* Block length keys are used as is */
memcpy(s->key_buffer, key, key_length);
}
/* Start inner hash */
sha1_init(s);
for (i = 0; i < SHA1_BLOCK_LENGTH; i++) {
sha1_update_byte(s, s->key_buffer[i] ^ HMAC_IPAD);
}
}
uint8_t *sha1_final_hmac(sha1_context *s)
{
uint8_t i;
/* Complete inner hash */
memcpy(s->inner_hash, sha1_final(s), SHA1_DIGEST_LENGTH);
/* Calculate outer hash */
sha1_init(s);
for (i = 0; i < SHA1_BLOCK_LENGTH; i++) {
sha1_update_byte(s, s->key_buffer[i] ^ HMAC_OPAD);
}
for (i = 0; i < SHA1_DIGEST_LENGTH; i++) {
sha1_update_byte(s, s->inner_hash[i]);
}
return sha1_final(s);
}

@ -0,0 +1,123 @@
/*
* Copyright (C) 2016 Oliver Hahm <oliver.hahm@inria.fr>
*
* 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.
*/
/**
* @defgroup sys_hashes_sha1 SHA-1
* @ingroup sys_hashes
* @brief Implementation of the SHA-1 hashing function
* @{
*
* @file
* @brief SHA-1 interface definition
*
* @author Wei Dai and others
* @author Oliver Hahm <oliver.hahm@inria.fr>
*/
/* This code is public-domain - it is based on libcrypt
* placed in the public domain by Wei Dai and other contributors. */
#ifndef _SHA1_H
#define _SHA1_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Length of SHA-1 digests in byte
*/
#define SHA1_DIGEST_LENGTH (20)
/**
* @brief Length of SHA-1 block in byte
*/
#define SHA1_BLOCK_LENGTH (64)
/**
* @brief SHA-1 algorithm context
* @internal
**/
typedef struct {
/** internal buffer */
uint32_t buffer[SHA1_BLOCK_LENGTH / sizeof(uint32_t)];
/** buffering current state of hashing */
uint32_t state[SHA1_DIGEST_LENGTH / sizeof(uint32_t)];
/** already processed bytes */
uint32_t byte_count;
/** internal state variable to keep track if the buffer is filled before
* proceeding to hash this block */
uint8_t buffer_offset;
/** internal state of the key buffer */
uint8_t key_buffer[SHA1_BLOCK_LENGTH];
/** temporary buffer for the inner hashing */
uint8_t inner_hash[SHA1_DIGEST_LENGTH];
} sha1_context;
/**
* @brief Initialize SHA-1 message digest context
*
* @param[in] s Pointer to the SHA-1 context to initialize
*/
void sha1_init(sha1_context *s);
/**
* @brief Update the SHA-1 context with a portion of the message being hashed
*
* @param[in] s Pointer to the SHA-1 context to update
* @param[in] data Pointer to the buffer to be hashed
* @param[in] len Length of the buffer
*/
void sha1_update(sha1_context *s, const unsigned char *data, size_t len);
/**
* @brief Finalizes the SHA-1 message digest
*
* @param[in] s Pointer to the SHA-1 context
*
* @return Calculated digest
*/
uint8_t *sha1_final(sha1_context *s);
/**
* @brief Calculate a SHA1 hash from the given data
*
* @param[out] dst Result location, must be 20 byte
* @param[in] src Input data
* @param[in] len Length of @p buf
*/
void sha1(uint8_t *dst, const uint8_t *src, size_t len);
/**
* @brief Initialize SHA-1 message digest context with MAC
*
* @param[in] s Pointer to the SHA-1 context to initialize
* @param[in] key Key used in the HMAC-SHA1 computation
* @param[in] key_length The size in bytes of @p key
*/
void sha1_init_hmac(sha1_context *s, const unsigned char *key, size_t key_length);
/**
* @brief Finalizes the SHA-1 message digest with MAC
*
* @param[in] s Pointer to the SHA-1 context
*
* @return Calculated digest
*/
uint8_t* sha1_final_hmac(sha1_context *s);
#ifdef __cplusplus
}
#endif
#endif /* _SHA1_H */
/** @} */

@ -0,0 +1,199 @@
/*
* Copyright (C) 2016 Oliver Hahm <oliver.hahm@inria.fr>
*
* 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 Test cases for the SHA-1 hash implementation
*
* @author Oliver Hahm <oliver.hahm@inria.fr>
*
* @}
*/
#include <stdio.h>
#include <string.h>
#include "embUnit/embUnit.h"
#include "hashes/sha1.h"
#define TEST_CASES_NUM (4)
/*
* * Define patterns for testing
* */
#define TEST1 "abc"
#define TEST2a "abcdbcdecdefdefgefghfghighijhi"
#define TEST2b "jkijkljklmklmnlmnomnopnopq"
#define TEST2 TEST2a TEST2b
#define TEST3 "a"
#define TEST4a "01234567012345670123456701234567"
#define TEST4b "01234567012345670123456701234567"
/* an exact multiple of 512 bits */
#define TEST4 TEST4a TEST4b
static char *_testarray[TEST_CASES_NUM] =
{
TEST1,
TEST2,
TEST3,
TEST4
};
static long int _repeatcount[TEST_CASES_NUM] = { 1, 1, 1000000, 10 };
static char *_resultarray[TEST_CASES_NUM + 2] =
{
"A9 99 3E 36 47 06 81 6A BA 3E 25 71 78 50 C2 6C 9C D0 D8 9D",
"84 98 3E 44 1C 3B D2 6E BA AE 4A A1 F9 51 29 E5 E5 46 70 F1",
/* for repeatcount == 1000000 */
"34 AA 97 3C D4 C4 DA A4 F6 1E EB 2B DB AD 27 31 65 34 01 6F",
/* for repeatcount == 10*/
"DE A3 56 A2 CD DD 90 C7 A7 EC ED C5 EB B5 63 93 4F 46 04 52",
/* for single runs */
"86 F7 E4 37 FA A5 A7 FC E1 5D 1D DC B9 EA EA EA 37 76 67 B8",
"E0 C0 94 E8 67 EF 46 C3 50 EF 54 A7 F5 9D D6 0B ED 92 AE 83"
};
#define TEST_CASES_HMAC_NUM (5)
#define TEST1_HMAC "Hi There"
#define TEST2_HMAC "what do ya want for nothing?"
#define TEST3_HMAC "Test With Truncation"
#define TEST4_HMAC "Test Using Larger Than Block-Size Key - Hash Key First"
#define TEST5_HMAC "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data"
static uint8_t _hmac_key1[]={
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
};
static uint8_t _hmac_key2[]= "Jefe";
static uint8_t _hmac_key3[]={
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c
};
static uint8_t _hmac_key4[]={
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
};
static uint8_t _hmac_key5[]={
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
};
static char *_resultarray_hmac[TEST_CASES_HMAC_NUM] =
{
"B6 17 31 86 55 05 72 64 E2 8B C0 B6 FB 37 8C 8E F1 46 BE 00",
"EF FC DF 6A E5 EB 2F A2 D2 74 16 D5 F1 84 DF 9C 25 9A 7C 79",
"4C 1A 03 42 4B 55 E0 7F E7 F2 7B E1 D5 8B B9 32 4A 9A 5A 04",
"AA 4A E5 E1 52 72 D0 0E 95 70 56 37 CE 8A 3B 55 ED 40 21 12",
"E8 E9 9D 0F 45 23 7D 78 6D 6B BA A7 96 5C 78 08 BB FF 1A 91 "
};
static int calc_and_compare_hash(const char *str, const char *expected,
long int repeatcount)
{
sha1_context ctx;
uint8_t *hash;
char tmp[(3 * SHA1_DIGEST_LENGTH) + 1];
/* calculate hash */
sha1_init(&ctx);
for (long int i = 0; i < repeatcount; ++i) {
sha1_update(&ctx, (unsigned char*) str, strlen(str));
}
hash = sha1_final(&ctx);
/* copy hash to string */
for (size_t i = 0; i < SHA1_DIGEST_LENGTH; i++) {
sprintf(&(tmp[i * 3]), "%02X ", (unsigned) hash[i]);
}
tmp[SHA1_DIGEST_LENGTH* 2] = '\0';
/* compare with result string */
return strncmp(tmp, expected, strlen((char*) tmp));
}
static int calc_and_compare_hash2(const char *str, const char *expected)
{
char tmp[(3 * SHA1_DIGEST_LENGTH) + 1];
uint8_t hash[SHA1_DIGEST_LENGTH];
sha1((unsigned char*) hash, (unsigned char*) str, strlen(str));
/* copy hash to string */
for (size_t i = 0; i < SHA1_DIGEST_LENGTH; i++) {
sprintf(&(tmp[i * 3]), "%02X ", (unsigned) hash[i]);
}
tmp[SHA1_DIGEST_LENGTH* 2] = '\0';
/* compare with result string */
return strncmp(tmp, expected, strlen((char*) tmp));
}
static int calc_and_compare_hash_hmac(const char *str, const char *expected,
const uint8_t *key, size_t key_len)
{
sha1_context ctx;
uint8_t *hash;
char tmp[(3 * SHA1_DIGEST_LENGTH) + 1];
/* calculate hash */
sha1_init_hmac(&ctx, key, key_len);
sha1_update(&ctx, (unsigned char*) str, strlen(str));
hash = sha1_final_hmac(&ctx);
/* copy hash to string */
for (size_t i = 0; i < SHA1_DIGEST_LENGTH; i++) {
sprintf(&(tmp[i * 3]), "%02X ", (unsigned) hash[i]);
}
tmp[SHA1_DIGEST_LENGTH* 2] = '\0';
/* compare with result string */
return strncmp(tmp, expected, strlen((char*) tmp));
}
/* test cases copied from section 7.3 of RFC 3174
* https://tools.ietf.org/html/rfc3174#section-7.3
*/
static void test_hashes_sha1(void)
{
TEST_ASSERT(calc_and_compare_hash(_testarray[0], _resultarray[0], _repeatcount[0]) == 0);
TEST_ASSERT(calc_and_compare_hash(_testarray[1], _resultarray[1], _repeatcount[1]) == 0);
TEST_ASSERT(calc_and_compare_hash(_testarray[2], _resultarray[2], _repeatcount[2]) == 0);
TEST_ASSERT(calc_and_compare_hash(_testarray[3], _resultarray[3], _repeatcount[3]) == 0);
TEST_ASSERT(calc_and_compare_hash2(_testarray[0], _resultarray[0]) == 0);
TEST_ASSERT(calc_and_compare_hash2(_testarray[1], _resultarray[1]) == 0);
TEST_ASSERT(calc_and_compare_hash2(_testarray[2], _resultarray[4]) == 0);
TEST_ASSERT(calc_and_compare_hash2(_testarray[3], _resultarray[5]) == 0);
TEST_ASSERT(calc_and_compare_hash_hmac(TEST1_HMAC, _resultarray_hmac[0], _hmac_key1, sizeof(_hmac_key1)) == 0);
TEST_ASSERT(calc_and_compare_hash_hmac(TEST2_HMAC, _resultarray_hmac[1], _hmac_key2, sizeof(_hmac_key2)) == 0);
TEST_ASSERT(calc_and_compare_hash_hmac(TEST3_HMAC, _resultarray_hmac[2], _hmac_key3, sizeof(_hmac_key3)) == 0);
TEST_ASSERT(calc_and_compare_hash_hmac(TEST4_HMAC, _resultarray_hmac[3], _hmac_key4, sizeof(_hmac_key4)) == 0);
TEST_ASSERT(calc_and_compare_hash_hmac(TEST5_HMAC, _resultarray_hmac[4], _hmac_key5, sizeof(_hmac_key5)) == 0);
}
Test *tests_hashes_sha1_tests(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
new_TestFixture(test_hashes_sha1),
};
EMB_UNIT_TESTCALLER(test_hashes_sha1, NULL, NULL, fixtures);
return (Test *)&test_hashes_sha1;
}

@ -23,5 +23,6 @@
void tests_hashes(void)
{
TESTS_RUN(tests_hashes_md5_tests());
TESTS_RUN(tests_hashes_sha1_tests());
TESTS_RUN(tests_hashes_sha256_tests());
}

@ -37,6 +37,13 @@ void tests_hashes(void);
*/
Test *tests_hashes_md5_tests(void);
/**
* @brief Generates tests for hashes/sha1.h
*
* @return embUnit tests if successful, NULL if not.
*/
Test *tests_hashes_sha1_tests(void);
/**
* @brief Generates tests for hashes/sha256.h
*

Loading…
Cancel
Save