File Coverage

_ed2k.c
Criterion Covered Total %
statement 15 28 53.5
branch 4 16 25.0
condition n/a
subroutine n/a
pod n/a
total 19 44 43.1


line stmt bran cond sub pod time code
1             /* ed2k.c - an implementation of EDonkey 2000 Hash Algorithm.
2             *
3             * Copyright (c) 2006, Aleksey Kravchenko
4             *
5             * Permission to use, copy, modify, and/or distribute this software for any
6             * purpose with or without fee is hereby granted.
7             *
8             * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9             * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10             * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11             * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12             * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13             * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14             * PERFORMANCE OF THIS SOFTWARE.
15             *
16             * This file implements eMule-compatible version of algorithm.
17             * Note that eDonkey and eMule ed2k hashes are different only for
18             * files containing exactly multiple of 9728000 bytes.
19             *
20             * The file data is divided into full chunks of 9500 KiB (9728000 bytes) plus
21             * a remainder chunk, and a separate 128-bit MD4 hash is computed for each.
22             * If the file length is an exact multiple of 9500 KiB, the remainder zero
23             * size chunk is still used at the end of the hash list. The ed2k hash is
24             * computed by concatenating the chunks' MD4 hashes in order and hashing the
25             * result using MD4. Although, if the file is composed of a single non-full
26             * chunk, its MD4 hash is returned with no further modifications.
27             *
28             * See http://en.wikipedia.org/wiki/EDonkey_network for algorithm description.
29             */
30              
31             #include
32             #include "ed2k.h"
33              
34             /* each hashed file is divided into 9500 KiB sized chunks */
35             #define ED2K_CHUNK_SIZE 9728000
36              
37             /**
38             * Initialize context before calculaing hash.
39             *
40             * @param ctx context to initialize
41             */
42 2           void rhash_ed2k_init(ed2k_ctx* ctx)
43             {
44 2           rhash_md4_init(&ctx->md4_context);
45 2           rhash_md4_init(&ctx->md4_context_inner);
46 2           ctx->not_emule = 0;
47 2           }
48              
49             /**
50             * Calculate message hash.
51             * Can be called repeatedly with chunks of the message to be hashed.
52             *
53             * @param ctx the algorithm context containing current hashing state
54             * @param msg message chunk
55             * @param size length of the message chunk
56             */
57 2           void rhash_ed2k_update(ed2k_ctx* ctx, const unsigned char* msg, size_t size)
58             {
59             unsigned char chunk_md4_hash[16];
60 2           unsigned blockleft = ED2K_CHUNK_SIZE - (unsigned)ctx->md4_context_inner.length;
61              
62             /* note: eMule-compatible algorithm hashes by md4_inner
63             * the messages which sizes are multiple of 9728000
64             * and then processes obtained hash by external md4 */
65              
66 2 50         while ( size >= blockleft )
67             {
68 0 0         if (size == blockleft && ctx->not_emule) break;
    0          
69              
70             /* if internal ed2k chunk is full, then finalize it */
71 0           rhash_md4_update(&ctx->md4_context_inner, msg, blockleft);
72 0           msg += blockleft;
73 0           size -= blockleft;
74 0           blockleft = ED2K_CHUNK_SIZE;
75              
76             /* just finished an ed2k chunk, updating md4_external context */
77 0           rhash_md4_final(&ctx->md4_context_inner, chunk_md4_hash);
78 0           rhash_md4_update(&ctx->md4_context, chunk_md4_hash, 16);
79 0           rhash_md4_init(&ctx->md4_context_inner);
80             }
81              
82 2 50         if (size) {
83             /* hash leftovers */
84 2           rhash_md4_update(&ctx->md4_context_inner, msg, size);
85             }
86 2           }
87              
88             /**
89             * Store calculated hash into the given array.
90             *
91             * @param ctx the algorithm context containing current hashing state
92             * @param result calculated hash in binary form
93             */
94 2           void rhash_ed2k_final(ed2k_ctx* ctx, unsigned char result[16])
95             {
96             /* check if hashed message size is greater or equal to ED2K_CHUNK_SIZE */
97 2 50         if ( ctx->md4_context.length ) {
98              
99             /* note: weird eMule algorithm always processes the inner
100             * md4 context, no matter if it contains data or is empty */
101              
102             /* if any data are left in the md4_context_inner */
103 0 0         if ( (size_t)ctx->md4_context_inner.length > 0 || !ctx->not_emule)
    0          
104             {
105             /* emule algorithm processes aditional block, even if it's empty */
106             unsigned char md4_digest_inner[16];
107 0           rhash_md4_final(&ctx->md4_context_inner, md4_digest_inner);
108 0           rhash_md4_update(&ctx->md4_context, md4_digest_inner, 16);
109             }
110             /* first call final to flush md4 buffer and finalize the hash value */
111 0           rhash_md4_final(&ctx->md4_context, result);
112             /* store the calculated ed2k hash in the md4_context_inner.hash */
113 0           memcpy(&ctx->md4_context_inner.hash, &ctx->md4_context.hash, md4_hash_size);
114             } else {
115             /* return just the message MD4 hash */
116 2 50         if (result) rhash_md4_final(&ctx->md4_context_inner, result);
117             }
118 2           }