From 784e2368390baeb5aab88d6e59ad2e2071690fc4 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 21 Nov 2005 18:01:20 +0000 Subject: [PATCH] - implementation of hasher_sha1 - tested --- Source/charon/testcases/hasher_sha1_test.c | 103 ++++++++++++ Source/charon/testcases/hasher_sha1_test.h | 37 +++++ Source/charon/transforms/hashers/hasher.h | 24 ++- Source/charon/transforms/hashers/hasher_sha1.c | 222 ++++++++++++++++++++++++- 4 files changed, 377 insertions(+), 9 deletions(-) create mode 100644 Source/charon/testcases/hasher_sha1_test.c create mode 100644 Source/charon/testcases/hasher_sha1_test.h diff --git a/Source/charon/testcases/hasher_sha1_test.c b/Source/charon/testcases/hasher_sha1_test.c new file mode 100644 index 0000000..bdca816 --- /dev/null +++ b/Source/charon/testcases/hasher_sha1_test.c @@ -0,0 +1,103 @@ +/** + * @file hasher_sha1_test.h + * + * @brief Tests the sha1 hasher + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include + +#include "hasher_sha1_test.h" + +#include "../utils/allocator.h" + + +/* + * described in Header-File + */ +void test_hasher_sha1(tester_t *tester) +{ + /* + * Test Vectors (from FIPS PUB 180-1) + * "abc" + * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D + * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 + * A million repetitions of "a" + * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F + */ + hasher_t *hasher = hasher_create(SHA1); + u_int8_t hash_buffer[20]; + chunk_t abc, abcdb, aaa, hash_chunk; + u_int32_t i; + u_int8_t hash_abc[] = { + 0xA9,0x99,0x3E,0x36, + 0x47,0x06,0x81,0x6A, + 0xBA,0x3E,0x25,0x71, + 0x78,0x50,0xC2,0x6C, + 0x9C,0xD0,0xD8,0x9D + }; + u_int8_t hash_abcdb[] = { + 0x84,0x98,0x3E,0x44, + 0x1C,0x3B,0xD2,0x6E, + 0xBA,0xAE,0x4A,0xA1, + 0xF9,0x51,0x29,0xE5, + 0xE5,0x46,0x70,0xF1 + }; + u_int8_t hash_aaa[] = { + 0x34,0xAA,0x97,0x3C, + 0xD4,0xC4,0xDA,0xA4, + 0xF6,0x1E,0xEB,0x2B, + 0xDB,0xAD,0x27,0x31, + 0x65,0x34,0x01,0x6F + }; + abc.ptr = "abc"; + abc.len = 3; + abcdb.ptr = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; + abcdb.len = strlen(abcdb.ptr); + aaa.ptr = "aaaaaaaaaa"; /* 10 a's */ + aaa.len = 10; + + tester->assert_true(tester, hasher->get_block_size(hasher) == 20, "block size"); + + /* simple hashing, using "abc" */ + hasher->get_hash(hasher, abc, hash_buffer); + tester->assert_false(tester, memcmp(hash_buffer, hash_abc, 20), "hash for abc"); + + /* with allocation, using "abcdb..." */ + hasher->reset(hasher); + hasher->allocate_hash(hasher, abcdb, &hash_chunk); + tester->assert_true(tester, hash_chunk.len == 20, "chunk len"); + tester->assert_false(tester, memcmp(hash_chunk.ptr, hash_abcdb, hash_chunk.len), "hash for abcdb..."); + allocator_free(hash_chunk.ptr); + + /* updating, using "aaaaaaa..." */ + hasher->reset(hasher); + for(i=0; i<100000; i++) + { + if (i != 99999) + { + hasher->get_hash(hasher, aaa, NULL); + } + else + { + hasher->get_hash(hasher, aaa, hash_buffer); + } + } + tester->assert_false(tester, memcmp(hash_buffer, hash_aaa, 20), "hash for aaa..."); +} diff --git a/Source/charon/testcases/hasher_sha1_test.h b/Source/charon/testcases/hasher_sha1_test.h new file mode 100644 index 0000000..a326378 --- /dev/null +++ b/Source/charon/testcases/hasher_sha1_test.h @@ -0,0 +1,37 @@ +/** + * @file hasher_sha1_test.h + * + * @brief Tests the sha1 hasher + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef HASHER_SHA1_TEST_H_ +#define HASHER_SHA1_TEST_H_ + +#include "../transforms/hashers/hasher.h" +#include "../transforms/hashers/hasher_sha1.h" +#include "../utils/tester.h" + +/** + * @brief Test function used to test the sha1-hasher functionality + * + * @param tester associated tester object + */ +void test_hasher_sha1(tester_t *tester); + +#endif /*HASHER_SHA1_TEST_H_*/ diff --git a/Source/charon/transforms/hashers/hasher.h b/Source/charon/transforms/hashers/hasher.h index 2428678..ff7fcc3 100644 --- a/Source/charon/transforms/hashers/hasher.h +++ b/Source/charon/transforms/hashers/hasher.h @@ -46,19 +46,30 @@ struct hasher_s { /** * @brief hash data and write it in the buffer * + * If the parameter hash is NULL, no result is written back + * an more data can be appended to already hashed data. + * If not, the result is written back and the hasher is reset. + * + * @warning: the hash output parameter must hold at least + * #hash_t.get_block_size bytes. + * * @param this calling hasher * @param data data to hash * @param [out]buffer pointer where the hash will be written * @return * - SUCCESS in any case */ - status_t (*get_hash) (hasher_t *this, chunk_t data, u_int8_t *buffer); + status_t (*get_hash) (hasher_t *this, chunk_t data, u_int8_t *hash); /** * @brief hash data and allocate space for the hash * + * If the parameter hash is NULL, no result is written back + * an more data can be appended to already hashed data. + * If not, the result is written back and the hasher is reset. + * * @param this calling hasher - * @param seed a chunk containing the seed for the next bytes + * @param data chunk with data to hash * @param [out]hash chunk which will hold allocated hash * @return * - SUCCESS in any case @@ -75,6 +86,15 @@ struct hasher_s { size_t (*get_block_size) (hasher_t *this); /** + * @brief reset the hashers state, which allows + * computation of a completly new hash. + * + * @param this calling hasher + * @return - SUCCESS in any case + */ + status_t (*reset) (hasher_t *this); + + /** * @brief Destroys a hasher object. * * @param this hasher_t object to destroy diff --git a/Source/charon/transforms/hashers/hasher_sha1.c b/Source/charon/transforms/hashers/hasher_sha1.c index 9a9df50..1349a41 100644 --- a/Source/charon/transforms/hashers/hasher_sha1.c +++ b/Source/charon/transforms/hashers/hasher_sha1.c @@ -9,6 +9,9 @@ /* * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil + * + * Ported from Steve Reid's implementation + * "SHA1 in C" found in strongSwan. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -23,19 +26,221 @@ #include "hasher_sha1.h" +#include "../../definitions.h" #include "../../utils/allocator.h" +#define BLOCK_SIZE_SHA1 20 + + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +#if BYTE_ORDER == LITTLE_ENDIAN + #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) |(rol(block->l[i],8)&0x00FF00FF)) +#elif BYTE_ORDER == BIG_ENDIAN + #define blk0(i) block->l[i] +#else + #error "Endianness not defined!" +#endif +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + typedef struct private_hasher_sha1_s private_hasher_sha1_t; struct private_hasher_sha1_s { /** * public interface for this hasher */ - hasher_sha1_t public; + hasher_sha1_t public; + + + u_int32_t state[5]; + u_int32_t count[2]; + u_int8_t buffer[64]; }; +/* Hash a single 512-bit block. This is the core of the algorithm. */ +void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64]) +{ + u_int32_t a, b, c, d, e; + typedef union { + u_int8_t c[64]; + u_int32_t l[16]; + } CHAR64LONG16; + CHAR64LONG16 block[1]; /* use array to appear as a pointer */ + memcpy(block, buffer, 64); + + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; + memset(block, '\0', sizeof(block)); +} + +/* Run your data through this. */ +void SHA1Update(private_hasher_sha1_t* this, u_int8_t *data, u_int32_t len) +{ + u_int32_t i; + u_int32_t j; + + j = this->count[0]; + if ((this->count[0] += len << 3) < j) + { + this->count[1]++; + } + this->count[1] += (len>>29); + j = (j >> 3) & 63; + if ((j + len) > 63) + { + memcpy(&this->buffer[j], data, (i = 64-j)); + SHA1Transform(this->state, this->buffer); + for ( ; i + 63 < len; i += 64) + { + SHA1Transform(this->state, &data[i]); + } + j = 0; + } + else + { + i = 0; + } + memcpy(&this->buffer[j], &data[i], len - i); +} +/* + * Add padding and return the message digest. + */ +void SHA1Final(private_hasher_sha1_t *this, u_int8_t *digest) +{ + u_int32_t i; + u_int8_t finalcount[8]; + u_int8_t c; + + for (i = 0; i < 8; i++) + { + finalcount[i] = (u_int8_t)((this->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + c = 0200; + SHA1Update(this, &c, 1); + while ((this->count[0] & 504) != 448) + { + c = 0000; + SHA1Update(this, &c, 1); + } + SHA1Update(this, finalcount, 8); /* Should cause a SHA1Transform() */ + for (i = 0; i < 20; i++) + { + digest[i] = (u_int8_t)((this->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } +} + + +/** + * implementation of hasher_t.get_hash for sha1 + */ +static status_t get_hash(private_hasher_sha1_t *this, chunk_t chunk, u_int8_t *buffer) +{ + SHA1Update(this, chunk.ptr, chunk.len); + if (buffer != NULL) + { + SHA1Final(this, buffer); + } + return SUCCESS; +} + + +/** + * implementation of hasher_t.allocate_hash for sha1 + */ +static status_t allocate_hash(private_hasher_sha1_t *this, chunk_t chunk, chunk_t *hash) +{ + chunk_t allocated_hash; + allocated_hash.ptr = allocator_alloc(BLOCK_SIZE_SHA1); + allocated_hash.len = BLOCK_SIZE_SHA1; + if (allocated_hash.ptr == NULL) + { + return OUT_OF_RES; + } + + SHA1Update(this, chunk.ptr, chunk.len); + if (hash != NULL) + { + SHA1Final(this, allocated_hash.ptr); + } + + *hash = allocated_hash; + + return SUCCESS; +} + +/** + * implementation of hasher_t.get_block_size for sha1 + */ +static size_t get_block_size(private_hasher_sha1_t *this) +{ + return BLOCK_SIZE_SHA1; +} + +/** + * implementation of hasher_t.reset for sha1 + */ +static status_t reset(private_hasher_sha1_t *this) +{ + this->state[0] = 0x67452301; + this->state[1] = 0xEFCDAB89; + this->state[2] = 0x98BADCFE; + this->state[3] = 0x10325476; + this->state[4] = 0xC3D2E1F0; + this->count[0] = 0; + this->count[1] = 0; + return SUCCESS; +} +/** + * implementation of hasher_t.destroy for sha1 + */ +static status_t destroy(private_hasher_sha1_t *this) +{ + allocator_free(this); + return SUCCESS; +} /* @@ -44,16 +249,19 @@ struct private_hasher_sha1_s { hasher_sha1_t *hasher_sha1_create() { private_hasher_sha1_t *this = allocator_alloc_thing(private_hasher_sha1_t); - if (this == NULL) { return NULL; } + this->public.hasher_interface.get_hash = (status_t (*) (hasher_t*, chunk_t, u_int8_t*))get_hash; + this->public.hasher_interface.allocate_hash = (status_t (*) (hasher_t*, chunk_t, chunk_t*))allocate_hash; + this->public.hasher_interface.get_block_size = (size_t (*) (hasher_t*))get_block_size; + this->public.hasher_interface.reset = (size_t (*) (hasher_t*))reset; + this->public.hasher_interface.destroy = (size_t (*) (hasher_t*))destroy; + + /* initialize */ + this->public.hasher_interface.reset(&(this->public.hasher_interface)); + return &(this->public); } - - - - - -- 2.7.4