1//---------------------------------------------------------------------------------
2//
3//  Little Color Management System
4//  Copyright (c) 1998-2012 Marti Maria Saguer
5//
6// Permission is hereby granted, free of charge, to any person obtaining
7// a copy of this software and associated documentation files (the "Software"),
8// to deal in the Software without restriction, including without limitation
9// the rights to use, copy, modify, merge, publish, distribute, sublicense,
10// and/or sell copies of the Software, and to permit persons to whom the Software
11// is furnished to do so, subject to the following conditions:
12//
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
18// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23//
24//---------------------------------------------------------------------------------
25
26#include "lcms2_internal.h"
27
28#ifdef CMS_USE_BIG_ENDIAN
29
30static
31void byteReverse(cmsUInt8Number * buf, cmsUInt32Number longs)
32{
33    do {
34
35        cmsUInt32Number t = _cmsAdjustEndianess32(*(cmsUInt32Number *) buf);
36        *(cmsUInt32Number *) buf = t;
37        buf += sizeof(cmsUInt32Number);
38
39    } while (--longs);
40
41}
42
43#else
44#define byteReverse(buf, len)
45#endif
46
47
48typedef struct {
49
50    cmsUInt32Number buf[4];
51    cmsUInt32Number bits[2];
52    cmsUInt8Number in[64];
53    cmsContext ContextID;
54
55} _cmsMD5;
56
57#define F1(x, y, z) (z ^ (x & (y ^ z)))
58#define F2(x, y, z) F1(z, x, y)
59#define F3(x, y, z) (x ^ y ^ z)
60#define F4(x, y, z) (y ^ (x | ~z))
61
62#define STEP(f, w, x, y, z, data, s) \
63    ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
64
65
66static
67void MD5_Transform(cmsUInt32Number buf[4], cmsUInt32Number in[16])
68
69{
70    register cmsUInt32Number a, b, c, d;
71
72    a = buf[0];
73    b = buf[1];
74    c = buf[2];
75    d = buf[3];
76
77    STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
78    STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
79    STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
80    STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
81    STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
82    STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
83    STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
84    STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
85    STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
86    STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
87    STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
88    STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
89    STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
90    STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
91    STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
92    STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
93
94    STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
95    STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
96    STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
97    STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
98    STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
99    STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
100    STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
101    STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
102    STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
103    STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
104    STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
105    STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
106    STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
107    STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
108    STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
109    STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
110
111    STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
112    STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
113    STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
114    STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
115    STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
116    STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
117    STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
118    STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
119    STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
120    STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
121    STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
122    STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
123    STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
124    STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
125    STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
126    STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
127
128    STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
129    STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
130    STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
131    STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
132    STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
133    STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
134    STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
135    STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
136    STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
137    STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
138    STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
139    STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
140    STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
141    STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
142    STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
143    STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
144
145    buf[0] += a;
146    buf[1] += b;
147    buf[2] += c;
148    buf[3] += d;
149}
150
151
152// Create a MD5 object
153static
154cmsHANDLE  MD5alloc(cmsContext ContextID)
155{
156    _cmsMD5* ctx = (_cmsMD5*) _cmsMallocZero(ContextID, sizeof(_cmsMD5));
157    if (ctx == NULL) return NULL;
158
159    ctx ->ContextID = ContextID;
160
161    ctx->buf[0] = 0x67452301;
162    ctx->buf[1] = 0xefcdab89;
163    ctx->buf[2] = 0x98badcfe;
164    ctx->buf[3] = 0x10325476;
165
166    ctx->bits[0] = 0;
167    ctx->bits[1] = 0;
168
169    return (cmsHANDLE) ctx;
170}
171
172
173static
174void MD5add(cmsHANDLE Handle, cmsUInt8Number* buf, cmsUInt32Number len)
175{
176    _cmsMD5* ctx = (_cmsMD5*) Handle;
177    cmsUInt32Number t;
178
179    t = ctx->bits[0];
180    if ((ctx->bits[0] = t + (len << 3)) < t)
181        ctx->bits[1]++;
182
183    ctx->bits[1] += len >> 29;
184
185    t = (t >> 3) & 0x3f;
186
187    if (t) {
188
189        cmsUInt8Number *p = (cmsUInt8Number *) ctx->in + t;
190
191        t = 64 - t;
192        if (len < t) {
193            memmove(p, buf, len);
194            return;
195        }
196
197        memmove(p, buf, t);
198        byteReverse(ctx->in, 16);
199
200        MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in);
201        buf += t;
202        len -= t;
203    }
204
205    while (len >= 64) {
206        memmove(ctx->in, buf, 64);
207        byteReverse(ctx->in, 16);
208        MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in);
209        buf += 64;
210        len -= 64;
211    }
212
213    memmove(ctx->in, buf, len);
214}
215
216// Destroy the object and return the checksum
217static
218void MD5finish(cmsProfileID* ProfileID,  cmsHANDLE Handle)
219{
220    _cmsMD5* ctx = (_cmsMD5*) Handle;
221    cmsUInt32Number count;
222    cmsUInt8Number *p;
223
224    count = (ctx->bits[0] >> 3) & 0x3F;
225
226    p = ctx->in + count;
227    *p++ = 0x80;
228
229    count = 64 - 1 - count;
230
231    if (count < 8) {
232
233        memset(p, 0, count);
234        byteReverse(ctx->in, 16);
235        MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in);
236
237        memset(ctx->in, 0, 56);
238    } else {
239        memset(p, 0, count - 8);
240    }
241    byteReverse(ctx->in, 14);
242
243    ((cmsUInt32Number *) ctx->in)[14] = ctx->bits[0];
244    ((cmsUInt32Number *) ctx->in)[15] = ctx->bits[1];
245
246    MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in);
247
248    byteReverse((cmsUInt8Number *) ctx->buf, 4);
249    memmove(ProfileID ->ID8, ctx->buf, 16);
250
251    _cmsFree(ctx ->ContextID, ctx);
252}
253
254
255
256// Assuming io points to an ICC profile, compute and store MD5 checksum
257// In the header, rendering intentent, attributes and ID should be set to zero
258// before computing MD5 checksum (per 6.1.13 in ICC spec)
259
260cmsBool CMSEXPORT cmsMD5computeID(cmsHPROFILE hProfile)
261{
262    cmsContext   ContextID;
263    cmsUInt32Number BytesNeeded;
264    cmsUInt8Number* Mem = NULL;
265    cmsHANDLE  MD5 = NULL;
266    _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
267    _cmsICCPROFILE Keep;
268
269    _cmsAssert(hProfile != NULL);
270
271    ContextID = cmsGetProfileContextID(hProfile);
272
273    // Save a copy of the profile header
274    memmove(&Keep, Icc, sizeof(_cmsICCPROFILE));
275
276    // Set RI, attributes and ID
277    memset(&Icc ->attributes, 0, sizeof(Icc ->attributes));
278    Icc ->RenderingIntent = 0;
279    memset(&Icc ->ProfileID, 0, sizeof(Icc ->ProfileID));
280
281    // Compute needed storage
282    if (!cmsSaveProfileToMem(hProfile, NULL, &BytesNeeded)) goto Error;
283
284    // Allocate memory
285    Mem = (cmsUInt8Number*) _cmsMalloc(ContextID, BytesNeeded);
286    if (Mem == NULL) goto Error;
287
288    // Save to temporary storage
289    if (!cmsSaveProfileToMem(hProfile, Mem, &BytesNeeded)) goto Error;
290
291    // Create MD5 object
292    MD5 = MD5alloc(ContextID);
293    if (MD5 == NULL) goto Error;
294
295    // Add all bytes
296    MD5add(MD5, Mem, BytesNeeded);
297
298    // Temp storage is no longer needed
299    _cmsFree(ContextID, Mem);
300
301    // Restore header
302    memmove(Icc, &Keep, sizeof(_cmsICCPROFILE));
303
304    // And store the ID
305    MD5finish(&Icc ->ProfileID,  MD5);
306    return TRUE;
307
308Error:
309
310    // Free resources as something went wrong
311    // "MD5" cannot be other than NULL here, so no need to free it
312    if (Mem != NULL) _cmsFree(ContextID, Mem);
313    memmove(Icc, &Keep, sizeof(_cmsICCPROFILE));
314    return FALSE;
315}
316
317cmsBool CMSEXPORT cmsMD5computeIDExt(const void* buf, unsigned long size, unsigned char ProfileID[16])
318{
319	cmsHANDLE  MD5;
320	cmsUInt8Number* Mem;
321
322	if (buf == NULL)
323		return FALSE;
324    MD5 = NULL;
325	Mem = (cmsUInt8Number*)_cmsMalloc(NULL,size);
326	memmove(Mem,buf,size);
327	// Create MD5 object
328    MD5 = MD5alloc(NULL);
329    if (MD5 == NULL) goto Error;
330
331	// Add all bytes
332    MD5add(MD5, Mem, size);
333
334	// Temp storage is no longer needed
335    _cmsFree(NULL, Mem);
336
337	// And store the ID
338    MD5finish((cmsProfileID*)ProfileID,  MD5);
339	return TRUE;
340Error:
341	if (MD5 != NULL) _cmsFree(NULL, MD5);
342	return FALSE;
343}
344