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
|
|
|
|
|
|
} |