minetest-world-manager/src/minetest_world_manager_sha1.c

242 lines
8.9 KiB
C

/* minetest_world_manager_sha1.c
Copyright (c) 2016 YuGiOhJCJ
Copyright (c) 2005 Michael D. Leonhard
http://yugiohjcj.1s.fr/
http://tamale.net/
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.
*/
#include <stdlib.h> /* for malloc */
#include <string.h> /* for memcpy */
#include "minetest_world_manager_print.h" /* for minetest_world_manager_print_error */
#include "minetest_world_manager_sha1.h" /* for minetest_world_manager_sha1_t */
static unsigned char *minetest_world_manager_sha1_get_digest_return(unsigned char *digest);
static minetest_world_manager_sha1_uint32_t minetest_world_manager_sha1_lrot(minetest_world_manager_sha1_uint32_t x, int bits);
static int minetest_world_manager_sha1_process(minetest_world_manager_sha1_t *sha1);
static int minetest_world_manager_sha1_store_big_endian_uint32(unsigned char *byte, minetest_world_manager_sha1_uint32_t num);
static unsigned char *minetest_world_manager_sha1_get_digest_return(unsigned char *digest)
{
if(digest != NULL)
free(digest);
return NULL;
}
static minetest_world_manager_sha1_uint32_t minetest_world_manager_sha1_lrot(minetest_world_manager_sha1_uint32_t x, int bits)
{
return (x << bits) | (x >> (32 - bits));
}
static int minetest_world_manager_sha1_process(minetest_world_manager_sha1_t *sha1)
{
int t;
minetest_world_manager_sha1_uint32_t a, b, c, d, e, k, f, w[80];
minetest_world_manager_sha1_uint32_t temp;
if(sha1->unprocessed_bytes != 64)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to process SHA1 because 'sha1->unprocessed_bytes' is not equal to 64.");
return -1;
}
a = sha1->h0;
b = sha1->h1;
c = sha1->h2;
d = sha1->h3;
e = sha1->h4;
for(t = 0; t < 16; t++)
w[t] = (sha1->bytes[t * 4] << 24) + (sha1->bytes[t * 4 + 1] << 16) + (sha1->bytes[t * 4 + 2] << 8) + sha1->bytes[t * 4 + 3];
for(; t< 80; t++)
w[t] = minetest_world_manager_sha1_lrot(w[t-3] ^ w[t-8] ^ w[t-14] ^ w[t-16], 1);
for(t = 0; t < 80; t++)
{
if(t < 20)
{
k = 0x5a827999;
f = (b & c) | ((b ^ 0xFFFFFFFF) & d);
}
else if(t < 40)
{
k = 0x6ed9eba1;
f = b ^ c ^ d;
}
else if(t < 60)
{
k = 0x8f1bbcdc;
f = (b & c) | (b & d) | (c & d);
}
else
{
k = 0xca62c1d6;
f = b ^ c ^ d;
}
temp = minetest_world_manager_sha1_lrot(a, 5) + f + e + w[t] + k;
e = d;
d = c;
c = minetest_world_manager_sha1_lrot(b, 30);
b = a;
a = temp;
}
sha1->h0 += a;
sha1->h1 += b;
sha1->h2 += c;
sha1->h3 += d;
sha1->h4 += e;
sha1->unprocessed_bytes = 0;
return 0;
}
static int minetest_world_manager_sha1_store_big_endian_uint32(unsigned char *byte, minetest_world_manager_sha1_uint32_t num)
{
if(byte == NULL)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to save to memory in big-endian order for SHA1 because 'byte' is equal to NULL.");
return -1;
}
byte[0] = (unsigned char) (num >> 24);
byte[1] = (unsigned char) (num >> 16);
byte[2] = (unsigned char) (num >> 8);
byte[3] = (unsigned char) num;
return 0;
}
int minetest_world_manager_sha1_add_bytes(minetest_world_manager_sha1_t *sha1, const char *data, int num)
{
if(data == NULL)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to add bytes for SHA1 because 'data' is equal to NULL.");
return -1;
}
if(num < 0)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to add bytes for SHA1 because 'num' is less than 0.");
return -1;
}
sha1->size += num;
while(num > 0)
{
int needed;
int to_copy;
needed = 64 - sha1->unprocessed_bytes;
if(needed <= 0)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to add bytes for SHA1 because 'needed' is less than or equal to 0.");
return -1;
}
to_copy = (num < needed) ? num : needed;
memcpy(sha1->bytes + sha1->unprocessed_bytes, data, to_copy);
num -= to_copy;
data += to_copy;
sha1->unprocessed_bytes += to_copy;
if(sha1->unprocessed_bytes == 64)
{
if(minetest_world_manager_sha1_process(sha1) == -1)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to process SHA1.");
return -1;
}
}
}
return 0;
}
unsigned char *minetest_world_manager_sha1_get_digest(minetest_world_manager_sha1_t *sha1)
{
minetest_world_manager_sha1_uint32_t total_bits_l = sha1->size << 3;
minetest_world_manager_sha1_uint32_t total_bits_h = sha1->size >> 29;
unsigned char footer[64] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
int needed_zeros = 0;
unsigned char *digest = NULL;
if(minetest_world_manager_sha1_add_bytes(sha1, "\x80", 1) == -1)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to add bytes for SHA1.");
return minetest_world_manager_sha1_get_digest_return(digest);
}
if(sha1->unprocessed_bytes > 56)
if(minetest_world_manager_sha1_add_bytes(sha1, (char*) footer, 64 - sha1->unprocessed_bytes) == -1)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to add bytes for SHA1.");
return minetest_world_manager_sha1_get_digest_return(digest);
}
if(sha1->unprocessed_bytes > 56)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to get the digest for SHA1 because 'sha1->unprocessed_bytes' is more than 56.");
return minetest_world_manager_sha1_get_digest_return(digest);
}
needed_zeros = 56 - sha1->unprocessed_bytes;
if(minetest_world_manager_sha1_store_big_endian_uint32(footer + needed_zeros, total_bits_h) == -1)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to store file size (in bits) in big-endian format for SHA1.");
return minetest_world_manager_sha1_get_digest_return(digest);
}
if(minetest_world_manager_sha1_store_big_endian_uint32(footer + needed_zeros + 4, total_bits_l) == -1)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to store file size (in bits) in big-endian format for SHA1.");
return minetest_world_manager_sha1_get_digest_return(digest);
}
if(minetest_world_manager_sha1_add_bytes(sha1, (char*) footer, needed_zeros + 8) == -1)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to add bytes for SHA1.");
return minetest_world_manager_sha1_get_digest_return(digest);
}
digest = (unsigned char*) malloc(20);
if(minetest_world_manager_sha1_store_big_endian_uint32(digest, sha1->h0) == -1)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to copy the digest bytes for SHA1.");
return minetest_world_manager_sha1_get_digest_return(digest);
}
if(minetest_world_manager_sha1_store_big_endian_uint32(digest + 4, sha1->h1) == -1)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to copy the digest bytes for SHA1.");
return minetest_world_manager_sha1_get_digest_return(digest);
}
if(minetest_world_manager_sha1_store_big_endian_uint32(digest + 8, sha1->h2) == -1)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to copy the digest bytes for SHA1.");
return minetest_world_manager_sha1_get_digest_return(digest);
}
if(minetest_world_manager_sha1_store_big_endian_uint32(digest + 12, sha1->h3) == -1)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to copy the digest bytes for SHA1.");
return minetest_world_manager_sha1_get_digest_return(digest);
}
if(minetest_world_manager_sha1_store_big_endian_uint32(digest + 16, sha1->h4) == -1)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to copy the digest bytes for SHA1.");
return minetest_world_manager_sha1_get_digest_return(digest);
}
return digest;
}
int minetest_world_manager_sha1_initialize(minetest_world_manager_sha1_t *sha1)
{
if(sizeof(minetest_world_manager_sha1_uint32_t) * 5 != 20)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to initialize SHA1 because the size of minetest_world_manager_sha1_uint32_t is not correct.");
return -1;
}
sha1->h0 = 0x67452301;
sha1->h1 = 0xefcdab89;
sha1->h2 = 0x98badcfe;
sha1->h3 = 0x10325476;
sha1->h4 = 0xc3d2e1f0;
sha1->unprocessed_bytes = 0;
sha1->size = 0;
return 0;
}