// ------------------------------------------------------------------ // aes.c // Copyright (C) 2019-2024 Genozip Limited. Patent Pending. // Please see terms and conditions in the file LICENSE.txt // // WARNING: Genozip is proprietary, not open source software. Modifying the source code is strictly prohibited // and subject to penalties specified in the license. // Copyright claimed on additions and modifications vs public domain. // // This is an implementation of MD5 based on: https://github.com/WaterJuice/WjCryptLib/blob/master/lib/WjCryptLib_Md5.c // which has been modified extensively. The unmodified terms of the license are as follows: // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // This is free and unencumbered software released into the public domain. // // Anyone is free to copy, modify, publish, use, compile, sell, or // distribute this software, either in source code form or as a compiled // binary, for any purpose, commercial or non-commercial, and by any // means. // // In jurisdictions that recognize copyright laws, the author or authors // of this software dedicate any and all copyright interest in the // software to the public domain. We make this dedication for the benefit // of the public at large and to the detriment of our heirs and // successors. We intend this dedication to be an overt act of // relinquishment in perpetuity of all present and future rights to this // software under copyright law. // // 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 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. // // For more information, please refer to // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< #include #include "genozip.h" #include "md5.h" #include "endianness.h" #include "vblock.h" #define F( x, y, z ) ( (z) ^ ((x) & ((y) ^ (z))) ) #define G( x, y, z ) ( (y) ^ ((z) & ((x) ^ (y))) ) #define H( x, y, z ) ( (x) ^ (y) ^ (z) ) #define I( x, y, z ) ( (y) ^ ((x) | ~(z)) ) #define STEP( f, a, b, c, d, x, t, s ) \ (a) += f((b), (c), (d)) + (x) + (t); \ (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ (a) += (b); void md5_display_ctx (const Md5Context *x) // for debugging { static unsigned iteration=1; fprintf (stderr, "\n%2u: %08x %08x %08x %08x %08x %08x ", iteration, x->hi, x->lo, x->a, x->b, x->c, x->d); for (unsigned i=0; i<64; i++) fprintf (stderr, "%2.2x", x->buffer.bytes[i]); fprintf (stderr, "\n"); iteration++; } static const void *md5_transform (Md5Context *ctx, const void *data, uintmax_t size) { const uint32_t *ptr = (uint32_t *)data; uint32_t a = ctx->a; uint32_t b = ctx->b; uint32_t c = ctx->c; uint32_t d = ctx->d; do { uint32_t saved_a = a; uint32_t saved_b = b; uint32_t saved_c = c; uint32_t saved_d = d; #ifdef __LITTLE_ENDIAN__ // in little endian machines, we can access the data directly - we don't need to copy memory #define block ptr #else // in big endian machines - we need to flip the data to little endian - so we do it in a copy uint32_t block[16]; for (unsigned i=0; i < 16; i++) block[i] = LTEN32(ptr[i]); #endif // Round 1 STEP( F, a, b, c, d, block[0], 0xd76aa478, 7 ) STEP( F, d, a, b, c, block[1], 0xe8c7b756, 12 ) STEP( F, c, d, a, b, block[2], 0x242070db, 17 ) STEP( F, b, c, d, a, block[3], 0xc1bdceee, 22 ) STEP( F, a, b, c, d, block[4], 0xf57c0faf, 7 ) STEP( F, d, a, b, c, block[5], 0x4787c62a, 12 ) STEP( F, c, d, a, b, block[6], 0xa8304613, 17 ) STEP( F, b, c, d, a, block[7], 0xfd469501, 22 ) STEP( F, a, b, c, d, block[8], 0x698098d8, 7 ) STEP( F, d, a, b, c, block[9], 0x8b44f7af, 12 ) STEP( F, c, d, a, b, block[10], 0xffff5bb1, 17 ) STEP( F, b, c, d, a, block[11], 0x895cd7be, 22 ) STEP( F, a, b, c, d, block[12], 0x6b901122, 7 ) STEP( F, d, a, b, c, block[13], 0xfd987193, 12 ) STEP( F, c, d, a, b, block[14], 0xa679438e, 17 ) STEP( F, b, c, d, a, block[15], 0x49b40821, 22 ) // Round 2 STEP( G, a, b, c, d, block[1], 0xf61e2562, 5 ) STEP( G, d, a, b, c, block[6], 0xc040b340, 9 ) STEP( G, c, d, a, b, block[11], 0x265e5a51, 14 ) STEP( G, b, c, d, a, block[0], 0xe9b6c7aa, 20 ) STEP( G, a, b, c, d, block[5], 0xd62f105d, 5 ) STEP( G, d, a, b, c, block[10], 0x02441453, 9 ) STEP( G, c, d, a, b, block[15], 0xd8a1e681, 14 ) STEP( G, b, c, d, a, block[4], 0xe7d3fbc8, 20 ) STEP( G, a, b, c, d, block[9], 0x21e1cde6, 5 ) STEP( G, d, a, b, c, block[14], 0xc33707d6, 9 ) STEP( G, c, d, a, b, block[3], 0xf4d50d87, 14 ) STEP( G, b, c, d, a, block[8], 0x455a14ed, 20 ) STEP( G, a, b, c, d, block[13], 0xa9e3e905, 5 ) STEP( G, d, a, b, c, block[2], 0xfcefa3f8, 9 ) STEP( G, c, d, a, b, block[7], 0x676f02d9, 14 ) STEP( G, b, c, d, a, block[12], 0x8d2a4c8a, 20 ) // Round 3 STEP( H, a, b, c, d, block[5], 0xfffa3942, 4 ) STEP( H, d, a, b, c, block[8], 0x8771f681, 11 ) STEP( H, c, d, a, b, block[11], 0x6d9d6122, 16 ) STEP( H, b, c, d, a, block[14], 0xfde5380c, 23 ) STEP( H, a, b, c, d, block[1], 0xa4beea44, 4 ) STEP( H, d, a, b, c, block[4], 0x4bdecfa9, 11 ) STEP( H, c, d, a, b, block[7], 0xf6bb4b60, 16 ) STEP( H, b, c, d, a, block[10], 0xbebfbc70, 23 ) STEP( H, a, b, c, d, block[13], 0x289b7ec6, 4 ) STEP( H, d, a, b, c, block[0], 0xeaa127fa, 11 ) STEP( H, c, d, a, b, block[3], 0xd4ef3085, 16 ) STEP( H, b, c, d, a, block[6], 0x04881d05, 23 ) STEP( H, a, b, c, d, block[9], 0xd9d4d039, 4 ) STEP( H, d, a, b, c, block[12], 0xe6db99e5, 11 ) STEP( H, c, d, a, b, block[15], 0x1fa27cf8, 16 ) STEP( H, b, c, d, a, block[2], 0xc4ac5665, 23 ) // Round 4 STEP( I, a, b, c, d, block[0], 0xf4292244, 6 ) STEP( I, d, a, b, c, block[7], 0x432aff97, 10 ) STEP( I, c, d, a, b, block[14], 0xab9423a7, 15 ) STEP( I, b, c, d, a, block[5], 0xfc93a039, 21 ) STEP( I, a, b, c, d, block[12], 0x655b59c3, 6 ) STEP( I, d, a, b, c, block[3], 0x8f0ccc92, 10 ) STEP( I, c, d, a, b, block[10], 0xffeff47d, 15 ) STEP( I, b, c, d, a, block[1], 0x85845dd1, 21 ) STEP( I, a, b, c, d, block[8], 0x6fa87e4f, 6 ) STEP( I, d, a, b, c, block[15], 0xfe2ce6e0, 10 ) STEP( I, c, d, a, b, block[6], 0xa3014314, 15 ) STEP( I, b, c, d, a, block[13], 0x4e0811a1, 21 ) STEP( I, a, b, c, d, block[4], 0xf7537e82, 6 ) STEP( I, d, a, b, c, block[11], 0xbd3af235, 10 ) STEP( I, c, d, a, b, block[2], 0x2ad7d2bb, 15 ) STEP( I, b, c, d, a, block[9], 0xeb86d391, 21 ) a += saved_a; b += saved_b; c += saved_c; d += saved_d; ptr += 16; } while (size -= 64); ctx->a = a; ctx->b = b; ctx->c = c; ctx->d = d; return ptr; } void md5_initialize (Md5Context *ctx) { // sanity for (unsigned i=0; i < sizeof(Md5Context); i++) ASSERT0 (!((char *)ctx)[i], "md5_initialize expects ctx to be zeros, but its not"); ctx->a = 0x67452301; ctx->b = 0xefcdab89; ctx->c = 0x98badcfe; ctx->d = 0x10325476; ctx->lo = 0; ctx->hi = 0; ctx->initialized = true; } // data must be aligned on 32-bit boundary void md5_update (Md5Context *ctx, const void *data, uint32_t len) { if (!len) return; // nothing to do uint32_t saved_lo; uint32_t used; uint32_t free; saved_lo = ctx->lo; if ((ctx->lo = (saved_lo + len) & 0x1fffffff) < saved_lo) ctx->hi++; ctx->hi += (uint32_t)(len >> 29); used = saved_lo & 0x3f; if (used) { free = 64 - used; if (len < free) { memcpy (&ctx->buffer.bytes[used], data, len); goto finish; } memcpy (&ctx->buffer.bytes[used], data, free); data += free; len -= free; md5_transform (ctx, ctx->buffer.bytes, 64); } if (len >= 64) { data = md5_transform (ctx, data, len & ~(unsigned long)0x3f); len &= 0x3f; } memcpy (ctx->buffer.bytes, data, len); finish: //fprintf (stderr, "%s md5_update snapshot: %s\n", primary_command == ZIP ? "ZIP" : "PIZ", digest_display (digest_snapshot (ctx))); //md5_display_ctx (ctx); return; } Digest md5_finalize (Md5Context *ctx) { uint32_t used; uint32_t free; used = ctx->lo & 0x3f; ctx->buffer.bytes[used++] = 0x80; free = 64 - used; if (free < 8) { memset (&ctx->buffer.bytes[used], 0, free); md5_transform (ctx, ctx->buffer.bytes, 64); used = 0; free = 64; } memset (&ctx->buffer.bytes[used], 0, free - 8); ctx->lo <<= 3; ctx->buffer.words[14] = LTEN32 (ctx->lo); ctx->buffer.words[15] = LTEN32 (ctx->hi); md5_transform (ctx, ctx->buffer.bytes, 64); Digest digest = { .words = { LTEN32 (ctx->a), LTEN32 (ctx->b), LTEN32 (ctx->c), LTEN32 (ctx->d) } }; memset (ctx, 0, sizeof (Md5Context)); // return to its pre-initialized state, should it be used again return digest; } // note: data must be aligned to the 32bit boundary (its accessed as uint32_t*) Digest md5_do (const void *data, uint32_t len) { Md5Context ctx; memset (&ctx, 0, sizeof(Md5Context)); md5_initialize (&ctx); md5_update (&ctx, data, len); return md5_finalize (&ctx); }