Files
amiga-rhash/librhash/ed2k.c
Carsten Larsen afd26249c2 Release 1.34
2017-04-12 00:33:28 +02:00

119 lines
4.3 KiB
C

/* ed2k.c - an implementation of EDonkey 2000 Hash Algorithm.
*
* Copyright: 2006-2012 Aleksey Kravchenko <rhash.admin@gmail.com>
*
* 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.
*
* 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. Use this program at your own risk!
*
* This file implements eMule-compatible version of algorithm.
* Note that eDonkey and eMule ed2k hashes are different only for
* files containing exactly multiple of 9728000 bytes.
*
* The file data is divided into full chunks of 9500 KiB (9728000 bytes) plus
* a remainder chunk, and a separate 128-bit MD4 hash is computed for each.
* If the file length is an exact multiple of 9500 KiB, the remainder zero
* size chunk is still used at the end of the hash list. The ed2k hash is
* computed by concatenating the chunks' MD4 hashes in order and hashing the
* result using MD4. Although, if the file is composed of a single non-full
* chunk, its MD4 hash is returned with no further modifications.
*
* See http://en.wikipedia.org/wiki/EDonkey_network for algorithm description.
*/
#include <string.h>
#include "ed2k.h"
/* each hashed file is divided into 9500 KiB sized chunks */
#define ED2K_CHUNK_SIZE 9728000
/**
* Initialize context before calculaing hash.
*
* @param ctx context to initialize
*/
void rhash_ed2k_init(ed2k_ctx *ctx)
{
rhash_md4_init(&ctx->md4_context);
rhash_md4_init(&ctx->md4_context_inner);
ctx->not_emule = 0;
}
/**
* Calculate message hash.
* Can be called repeatedly with chunks of the message to be hashed.
*
* @param ctx the algorithm context containing current hashing state
* @param msg message chunk
* @param size length of the message chunk
*/
void rhash_ed2k_update(ed2k_ctx *ctx, const unsigned char* msg, size_t size)
{
unsigned char chunk_md4_hash[16];
unsigned blockleft = ED2K_CHUNK_SIZE - (unsigned)ctx->md4_context_inner.length;
/* note: eMule-compatible algorithm hashes by md4_inner
* the messages which sizes are multiple of 9728000
* and then processes obtained hash by external md4 */
while ( size >= blockleft )
{
if (size == blockleft && ctx->not_emule) break;
/* if internal ed2k chunk is full, then finalize it */
rhash_md4_update(&ctx->md4_context_inner, msg, blockleft);
msg += blockleft;
size -= blockleft;
blockleft = ED2K_CHUNK_SIZE;
/* just finished an ed2k chunk, updating md4_external context */
rhash_md4_final(&ctx->md4_context_inner, chunk_md4_hash);
rhash_md4_update(&ctx->md4_context, chunk_md4_hash, 16);
rhash_md4_init(&ctx->md4_context_inner);
}
if (size) {
/* hash leftovers */
rhash_md4_update(&ctx->md4_context_inner, msg, size);
}
}
/**
* Store calculated hash into the given array.
*
* @param ctx the algorithm context containing current hashing state
* @param result calculated hash in binary form
*/
void rhash_ed2k_final(ed2k_ctx *ctx, unsigned char result[16])
{
/* check if hashed message size is greater or equal to ED2K_CHUNK_SIZE */
if ( ctx->md4_context.length ) {
/* note: weird eMule algorithm always processes the inner
* md4 context, no matter if it contains data or is empty */
/* if any data are left in the md4_context_inner */
if ( (size_t)ctx->md4_context_inner.length > 0 || !ctx->not_emule)
{
/* emule algorithm processes aditional block, even if it's empty */
unsigned char md4_digest_inner[16];
rhash_md4_final(&ctx->md4_context_inner, md4_digest_inner);
rhash_md4_update(&ctx->md4_context, md4_digest_inner, 16);
}
/* first call final to flush md4 buffer and finalize the hash value */
rhash_md4_final(&ctx->md4_context, result);
/* store the calculated ed2k hash in the md4_context_inner.hash */
memcpy(&ctx->md4_context_inner.hash, &ctx->md4_context.hash, md4_hash_size);
} else {
/* return just the message MD4 hash */
if (result) rhash_md4_final(&ctx->md4_context_inner, result);
}
}