mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-02-28 11:06:09 +08:00
765 lines
21 KiB
C++
765 lines
21 KiB
C++
//
|
|
// Copyright (c) 1993-2022 Robert McNeel & Associates. All rights reserved.
|
|
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
|
|
// McNeel & Associates.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
|
|
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
|
|
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
|
|
//
|
|
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
#include "opennurbs.h"
|
|
|
|
#if !defined(ON_COMPILING_OPENNURBS)
|
|
// This check is included in all opennurbs source .c and .cpp files to insure
|
|
// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled.
|
|
// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined
|
|
// and the opennurbs .h files alter what is declared and how it is declared.
|
|
#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs
|
|
#endif
|
|
|
|
/*
|
|
Description:
|
|
ON_MD5 is a small class for calculating the MD5 hash of a sequence of bytes.
|
|
It may be use incrementally (the bytes do not have to be in a contiguous
|
|
array in memory at one time).
|
|
|
|
Remarks:
|
|
Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved.
|
|
|
|
License to copy and use this software is granted provided that it
|
|
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
|
|
Algorithm" in all material mentioning or referencing this software
|
|
or this function.
|
|
|
|
License is also granted to make and use derivative works provided
|
|
that such works are identified as "derived from the RSA Data
|
|
Security, Inc. MD5 Message-Digest Algorithm" in all material
|
|
mentioning or referencing the derived work.
|
|
|
|
RSA Data Security, Inc. makes no representations concerning either
|
|
the merchantability of this software or the suitability of this
|
|
software for any particular purpose. It is provided "as is"
|
|
without express or implied warranty of any kind.
|
|
|
|
These notices must be retained in any copies of any part of this
|
|
documentation and/or software.
|
|
*/
|
|
|
|
ON_MD5_Hash::ON_MD5_Hash()
|
|
{
|
|
ON__UINT32* p = (ON__UINT32*)m_digest;
|
|
p[0] = 0U;
|
|
p[1] = 0U;
|
|
p[2] = 0U;
|
|
p[3] = 0U;
|
|
}
|
|
|
|
bool operator==(const ON_MD5_Hash& a, const ON_MD5_Hash& b)
|
|
{
|
|
const ON__UINT32* ai = (const ON__UINT32*)&a;
|
|
const ON__UINT32* bi = (const ON__UINT32*)&b;
|
|
return (ai[0] == bi[0] && ai[1] == bi[1] && ai[2] == bi[2] && ai[3] == bi[3]);
|
|
}
|
|
|
|
bool operator!=(const ON_MD5_Hash& a, const ON_MD5_Hash& b)
|
|
{
|
|
const ON__UINT32* ai = (const ON__UINT32*)&a;
|
|
const ON__UINT32* bi = (const ON__UINT32*)&b;
|
|
return (ai[0] != bi[0] || ai[1] != bi[1] || ai[2] != bi[2] || ai[3] != bi[3]);
|
|
}
|
|
|
|
int ON_MD5_Hash::Compare(
|
|
const ON_MD5_Hash& a,
|
|
const ON_MD5_Hash& b
|
|
)
|
|
{
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
if (a.m_digest[i] < b.m_digest[i])
|
|
return -1;
|
|
if (a.m_digest[i] > b.m_digest[i])
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const ON_String ON_MD5_Hash::ToUTF8String(
|
|
bool bUpperCaseHexadecimalDigits
|
|
) const
|
|
{
|
|
return ON_String::HexadecimalFromBytes(m_digest, sizeof(m_digest),bUpperCaseHexadecimalDigits,false);
|
|
}
|
|
|
|
const ON_wString ON_MD5_Hash::ToString(
|
|
bool bUpperCaseHexadecimalDigits
|
|
) const
|
|
{
|
|
return ON_wString::HexadecimalFromBytes(m_digest, sizeof(m_digest),bUpperCaseHexadecimalDigits,false);
|
|
}
|
|
|
|
bool ON_MD5_Hash::Read(
|
|
class ON_BinaryArchive& archive
|
|
)
|
|
{
|
|
*this = ON_MD5_Hash::ZeroDigest;
|
|
bool rc = false;
|
|
int major_version = 0;
|
|
int minor_version = 0;
|
|
if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version))
|
|
return rc;
|
|
for (;;)
|
|
{
|
|
if ( 1 != major_version )
|
|
break;
|
|
if (!archive.ReadByte(16,m_digest))
|
|
break;
|
|
rc = true;
|
|
break;
|
|
}
|
|
if (!archive.EndWrite3dmChunk())
|
|
rc = false;
|
|
return rc;
|
|
}
|
|
|
|
|
|
bool ON_MD5_Hash::Write(
|
|
class ON_BinaryArchive& archive
|
|
) const
|
|
{
|
|
if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0))
|
|
return false;
|
|
bool rc = archive.WriteByte(16,m_digest);
|
|
if (!archive.EndWrite3dmChunk())
|
|
rc = false;
|
|
return rc;
|
|
}
|
|
|
|
void ON_MD5_Hash::Dump(
|
|
class ON_TextLog& text_log
|
|
) const
|
|
{
|
|
const ON_wString md5_hash(ToString(true));
|
|
text_log.Print(L"MD5 hash = %ls\n",static_cast<const wchar_t*>(md5_hash));
|
|
}
|
|
|
|
|
|
ON_MD5_Hash ON_MD5_Hash::BufferHash(
|
|
const void* buffer,
|
|
size_t sizeof_buffer
|
|
)
|
|
{
|
|
if ( nullptr == buffer || sizeof_buffer <= 0 )
|
|
return ON_MD5_Hash::EmptyContentHash;
|
|
ON_MD5 md5;
|
|
md5.AccumulateBytes(buffer,sizeof_buffer);
|
|
return md5.Hash();
|
|
}
|
|
|
|
ON_MD5_Hash ON_MD5_Hash::FileHash(
|
|
const wchar_t* filename,
|
|
ON__UINT64& sizeof_file
|
|
)
|
|
{
|
|
FILE* fp =
|
|
( nullptr == filename || 0 == filename[0] )
|
|
? nullptr
|
|
: ON_FileStream::Open(filename, L"rb");
|
|
ON_MD5_Hash md5_hash = ON_MD5_Hash::FileHash(fp,sizeof_file);
|
|
ON_FileStream::Close(fp);
|
|
return md5_hash;
|
|
}
|
|
|
|
ON_MD5_Hash ON_MD5_Hash::FileHash(
|
|
const char* filename,
|
|
ON__UINT64& sizeof_file
|
|
)
|
|
{
|
|
FILE* fp =
|
|
( nullptr == filename || 0 == filename[0] )
|
|
? nullptr
|
|
: ON_FileStream::Open(filename, "rb");
|
|
ON_MD5_Hash md5_hash = ON_MD5_Hash::FileHash(fp,sizeof_file);
|
|
ON_FileStream::Close(fp);
|
|
return md5_hash;
|
|
}
|
|
|
|
ON_MD5_Hash ON_MD5_Hash::FileHash(
|
|
FILE* file,
|
|
ON__UINT64& sizeof_file
|
|
)
|
|
{
|
|
sizeof_file = 0;
|
|
if ( nullptr == file )
|
|
return ON_MD5_Hash::EmptyContentHash;
|
|
size_t sizeof_buffer = 1024;
|
|
void* buffer = onmalloc(sizeof_buffer);
|
|
ON_MD5 md5;
|
|
for (ON__UINT64 byte_count = ON_FileStream::Read(file, sizeof_buffer, buffer);
|
|
byte_count > 0;
|
|
byte_count = ON_FileStream::Read(file, sizeof_buffer, buffer)
|
|
)
|
|
{
|
|
md5.AccumulateBytes(buffer,byte_count);
|
|
}
|
|
onfree(buffer);
|
|
sizeof_file = md5.ByteCount();
|
|
return md5.Hash();
|
|
}
|
|
|
|
ON_MD5_Hash ON_MD5_Hash::StringHash(
|
|
const ON_wString& str,
|
|
ON__UINT64& byte_count
|
|
)
|
|
{
|
|
return ON_MD5_Hash::StringHash(
|
|
static_cast<const wchar_t*>(str),
|
|
(size_t)str.Length(),
|
|
byte_count
|
|
);
|
|
}
|
|
|
|
ON_MD5_Hash ON_MD5_Hash::StringHash(
|
|
const wchar_t* str,
|
|
size_t str_length,
|
|
ON__UINT64& byte_count
|
|
)
|
|
{
|
|
byte_count = 0;
|
|
if ( nullptr == str || str_length <= 0 )
|
|
return ON_MD5_Hash::EmptyContentHash;
|
|
|
|
ON_MD5 md5;
|
|
const int UTF8buffer_capacity = 1024;
|
|
char* UTF8buffer = (char*)onmalloc(UTF8buffer_capacity);
|
|
|
|
const int bTestByteOrder = false;
|
|
const ON__UINT32 error_code_point = 0xFFFD;
|
|
const unsigned int error_mask = 0xFFFFFFFFU;
|
|
while (str_length > 0)
|
|
{
|
|
const wchar_t* sNextWideChar = nullptr;
|
|
unsigned int error_status = 0;
|
|
int UTF8_count = ON_ConvertWideCharToUTF8(
|
|
bTestByteOrder,
|
|
str,
|
|
(int)str_length,
|
|
UTF8buffer,
|
|
UTF8buffer_capacity,
|
|
&error_status,
|
|
error_mask,
|
|
error_code_point,
|
|
&sNextWideChar
|
|
);
|
|
if ( UTF8_count > UTF8buffer_capacity)
|
|
break;
|
|
|
|
if ( UTF8_count > 0 && UTF8_count <= UTF8buffer_capacity)
|
|
md5.AccumulateBytes(UTF8buffer,UTF8_count);
|
|
|
|
if ( nullptr == sNextWideChar )
|
|
break;
|
|
if ( sNextWideChar <= str )
|
|
break;
|
|
size_t parsed_count = (str - sNextWideChar);
|
|
if ( parsed_count <= 0 || parsed_count >= str_length)
|
|
break;
|
|
str_length -= parsed_count;
|
|
if ( nullptr == sNextWideChar )
|
|
break;
|
|
}
|
|
|
|
onfree(UTF8buffer);
|
|
byte_count = md5.ByteCount();
|
|
return md5.Hash();
|
|
}
|
|
|
|
ON_MD5_Hash ON_MD5_Hash::StringHash(
|
|
const ON_String& str,
|
|
ON__UINT64& byte_count
|
|
)
|
|
{
|
|
return ON_MD5_Hash::StringHash(
|
|
static_cast<const char*>(str),
|
|
(size_t)str.Length(),
|
|
byte_count
|
|
);
|
|
}
|
|
|
|
ON_MD5_Hash ON_MD5_Hash::StringHash(
|
|
const char* str,
|
|
size_t str_length,
|
|
ON__UINT64& byte_count
|
|
)
|
|
{
|
|
byte_count = (nullptr != str && str_length > 0) ? ((ON__UINT64)str_length) : 0;
|
|
return ON_MD5_Hash::BufferHash(str,str_length);
|
|
}
|
|
|
|
// Constants for MD5Transform routine.
|
|
#define S11 7
|
|
#define S12 12
|
|
#define S13 17
|
|
#define S14 22
|
|
#define S21 5
|
|
#define S22 9
|
|
#define S23 14
|
|
#define S24 20
|
|
#define S31 4
|
|
#define S32 11
|
|
#define S33 16
|
|
#define S34 23
|
|
#define S41 6
|
|
#define S42 10
|
|
#define S43 15
|
|
#define S44 21
|
|
|
|
///////////////////////////////////////////////
|
|
|
|
// F, G, H and I are basic MD5 functions.
|
|
static ON__UINT32 MD5_F(ON__UINT32 x, ON__UINT32 y, ON__UINT32 z)
|
|
{
|
|
return (x&y) | (~x&z);
|
|
}
|
|
|
|
static ON__UINT32 MD5_G(ON__UINT32 x, ON__UINT32 y, ON__UINT32 z)
|
|
{
|
|
return (x&z) | (y&~z);
|
|
}
|
|
|
|
static ON__UINT32 MD5_H(ON__UINT32 x, ON__UINT32 y, ON__UINT32 z)
|
|
{
|
|
return x^y^z;
|
|
}
|
|
|
|
static ON__UINT32 MD5_I(ON__UINT32 x, ON__UINT32 y, ON__UINT32 z)
|
|
{
|
|
return y ^ (x | ~z);
|
|
}
|
|
|
|
// rotate_left rotates x left n bits.
|
|
static ON__UINT32 MD5_rotate_left(ON__UINT32 x, int n)
|
|
{
|
|
return (x << n) | (x >> (32-n));
|
|
}
|
|
|
|
// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
|
|
// Rotation is separate from addition to prevent recomputation.
|
|
static void MD5_FF(ON__UINT32 &a, ON__UINT32 b, ON__UINT32 c, ON__UINT32 d, ON__UINT32 x, ON__UINT32 s, ON__UINT32 ac)
|
|
{
|
|
a = MD5_rotate_left(a+ MD5_F(b,c,d) + x + ac, s) + b;
|
|
}
|
|
|
|
static void MD5_GG(ON__UINT32 &a, ON__UINT32 b, ON__UINT32 c, ON__UINT32 d, ON__UINT32 x, ON__UINT32 s, ON__UINT32 ac)
|
|
{
|
|
a = MD5_rotate_left(a + MD5_G(b,c,d) + x + ac, s) + b;
|
|
}
|
|
|
|
static void MD5_HH(ON__UINT32 &a, ON__UINT32 b, ON__UINT32 c, ON__UINT32 d, ON__UINT32 x, ON__UINT32 s, ON__UINT32 ac)
|
|
{
|
|
a = MD5_rotate_left(a + MD5_H(b,c,d) + x + ac, s) + b;
|
|
}
|
|
|
|
static void MD5_II(ON__UINT32 &a, ON__UINT32 b, ON__UINT32 c, ON__UINT32 d, ON__UINT32 x, ON__UINT32 s, ON__UINT32 ac)
|
|
{
|
|
a = MD5_rotate_left(a + MD5_I(b,c,d) + x + ac, s) + b;
|
|
}
|
|
|
|
//////////////////////////////////////////////
|
|
|
|
|
|
//////////////////////////////////////////////
|
|
|
|
//// nifty shortcut ctor, compute MD5 for string and finalize it right away
|
|
//MD5::MD5(const std::string &text)
|
|
//{
|
|
// init();
|
|
// update(text.c_str(), text.length());
|
|
// finalize();
|
|
//}
|
|
|
|
//////////////////////////////
|
|
|
|
static bool MD5_ValidateHelper(
|
|
const char* str,
|
|
const ON__UINT8 standard[16]
|
|
)
|
|
{
|
|
const int str_length = ON_String::Length(str);
|
|
ON_MD5 md5;
|
|
md5.AccumulateBytes(str, str_length);
|
|
const ON_MD5_Hash md5_digest(md5.Hash());
|
|
if ( 0 != memcmp(standard, &md5_digest, 16) )
|
|
return false;
|
|
|
|
bool rc = true;
|
|
for (int i = 0; i <= str_length && rc; i++)
|
|
{
|
|
for (int j = 0; i+j <= str_length && rc; j++)
|
|
{
|
|
md5.Reset();
|
|
md5.AccumulateBytes(str, i);
|
|
md5.Hash();
|
|
md5.AccumulateBytes(str+i, j);
|
|
md5.Hash();
|
|
md5.AccumulateBytes(str+i+j, str_length-i-j);
|
|
ON_MD5_Hash md5_digest1(md5.Hash());
|
|
rc = ( md5_digest == md5_digest1 );
|
|
}
|
|
}
|
|
|
|
if (rc)
|
|
{
|
|
if ( (ON__UINT64)str_length != md5.ByteCount() )
|
|
rc = false;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_MD5::Validate()
|
|
{
|
|
// http://onlinemd5.com/
|
|
|
|
// "" (empty string)
|
|
// d41d8cd9 8f00b204 e9800998 ecf8427e
|
|
const ON__UINT8 empty_string[16] = {
|
|
0xd4, 0x1d, 0x8c, 0xd9,
|
|
0x8f, 0x00, 0xb2, 0x04,
|
|
0xe9, 0x80, 0x09, 0x98,
|
|
0xec, 0xf8, 0x42, 0x7e };
|
|
if (false == MD5_ValidateHelper("",empty_string))
|
|
return false;
|
|
|
|
|
|
if (0 != memcmp(&ON_MD5_Hash::EmptyContentHash ,empty_string, sizeof(empty_string)))
|
|
return false;
|
|
|
|
// "abc"
|
|
// 90015098 3CD24FB0 D6963F7D 28E17F72
|
|
const ON__UINT8 abc[16] = {
|
|
0x90, 0x01, 0x50, 0x98,
|
|
0x3C, 0xD2, 0x4F, 0xB0,
|
|
0xD6, 0x96, 0x3F, 0x7D,
|
|
0x28, 0xE1, 0x7F, 0x72 };
|
|
if (false == MD5_ValidateHelper("abc",abc))
|
|
return false;
|
|
|
|
// "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
|
|
// 8215EF07 96A20BCA AAE116D3 876C664A
|
|
const ON__UINT8 abc_x[16] = {
|
|
0x82, 0x15, 0xEF, 0x07,
|
|
0x96, 0xA2, 0x0B, 0xCA,
|
|
0xAA, 0xE1, 0x16, 0xD3,
|
|
0x87, 0x6C, 0x66, 0x4A };
|
|
if (false == MD5_ValidateHelper("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",abc_x))
|
|
return false;
|
|
|
|
// "The quick brown fox jumps over the lazy dog"
|
|
// 9E107D9D 372BB682 6BD81D35 42A419D6
|
|
const ON__UINT8 quickfox[16] = {
|
|
0x9E, 0x10, 0x7D, 0x9D,
|
|
0x37, 0x2B, 0xB6, 0x82,
|
|
0x6B, 0xD8, 0x1D, 0x35,
|
|
0x42, 0xA4, 0x19, 0xD6 };
|
|
if (false == MD5_ValidateHelper("The quick brown fox jumps over the lazy dog",quickfox))
|
|
return false;
|
|
|
|
// "The quick brown fox jumps over the lazy dog."
|
|
// E4D909C2 90D0FB1C A068FFAD DF22CBD0
|
|
const ON__UINT8 quickfoxperiod[16] = {
|
|
0xE4, 0xD9, 0x09, 0xC2,
|
|
0x90, 0xD0, 0xFB, 0x1C,
|
|
0xA0, 0x68, 0xFF, 0xAD,
|
|
0xDF, 0x22, 0xCB, 0xD0 };
|
|
if (false == MD5_ValidateHelper("The quick brown fox jumps over the lazy dog.",quickfoxperiod))
|
|
return false;
|
|
|
|
// 1,000,000 repetitions of the character "a".
|
|
// 7707D6AE 4E027C70 EEA2A935 C2296F21.
|
|
const ON__UINT8 millionXa[16] = {
|
|
0x77, 0x07, 0xD6, 0xAE,
|
|
0x4E, 0x02, 0x7C, 0x70,
|
|
0xEE, 0xA2, 0xA9, 0x35,
|
|
0xC2, 0x29, 0x6F, 0x21 };
|
|
|
|
const size_t one_million = 1000000;
|
|
ON__UINT8 a[200];
|
|
const size_t a_count = sizeof(a) / sizeof(a[0]);
|
|
for ( size_t i = 0; i < a_count; i++)
|
|
a[i] = 'a';
|
|
ON_MD5 md5;
|
|
size_t total_count = 0;
|
|
for (total_count = 0; total_count < one_million; total_count += a_count)
|
|
{
|
|
md5.AccumulateBytes(a, a_count);
|
|
if (md5.ByteCount() != (ON__UINT64)(total_count+a_count))
|
|
return false;
|
|
}
|
|
|
|
ON_MD5_Hash md5_digest(md5.Hash());
|
|
|
|
if (0 != memcmp(&md5_digest,millionXa,sizeof(millionXa)))
|
|
return false;
|
|
|
|
md5.Reset();
|
|
ON_RandomNumberGenerator rng;
|
|
total_count = 0;
|
|
const size_t min_count = (a_count >= 10000) ? a_count/1000 : 100;
|
|
while (total_count < one_million)
|
|
{
|
|
size_t count = min_count + (rng.RandomNumber() % (ON__UINT32)min_count);
|
|
if ( total_count + count > one_million )
|
|
count = one_million - total_count;
|
|
md5.AccumulateBytes(a, count);
|
|
total_count += count;
|
|
if (md5.ByteCount() != (ON__UINT64)total_count)
|
|
return false;
|
|
}
|
|
|
|
ON_MD5_Hash md5_digestx(md5.Hash());
|
|
if (0 != memcmp(&md5_digestx,millionXa,sizeof(millionXa)))
|
|
return false;
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
ON__UINT64 ON_MD5::ByteCount() const
|
|
{
|
|
return m_byte_count;
|
|
}
|
|
|
|
void ON_MD5::Reset()
|
|
{
|
|
m_status_bits = 0;
|
|
m_byte_count = 0;
|
|
|
|
m_bit_count[0] = 0;
|
|
m_bit_count[1] = 0;
|
|
|
|
// MD5 initialization constants
|
|
m_state[0] = 0x67452301;
|
|
m_state[1] = 0xefcdab89;
|
|
m_state[2] = 0x98badcfe;
|
|
m_state[3] = 0x10325476;
|
|
|
|
m_status_bits = 1;
|
|
}
|
|
|
|
ON_MD5_Hash ON_MD5::Hash() const
|
|
{
|
|
if (2 != (2 & m_status_bits))
|
|
{
|
|
ON_MD5 tmp(*this);
|
|
if (1 != (1 & m_status_bits))
|
|
tmp.Reset();
|
|
tmp.set_final_hash();
|
|
m_md5_hash = tmp.m_md5_hash;
|
|
m_status_bits |= 2;
|
|
}
|
|
return m_md5_hash;
|
|
}
|
|
|
|
void ON_MD5::AccumulateBytes(
|
|
const void* buffer,
|
|
ON__UINT64 sizeof_buffer
|
|
)
|
|
{
|
|
if (nullptr != buffer && sizeof_buffer > 0)
|
|
{
|
|
if (1 != (1 & m_status_bits))
|
|
Reset();
|
|
m_status_bits = 1; // invalidate any intermediate cached m_digest value.
|
|
m_byte_count += sizeof_buffer;
|
|
const ON__UINT32 max_length = 0x0FFFFFFFU;
|
|
const ON__UINT8* p = (const ON__UINT8*)buffer;
|
|
while (sizeof_buffer > max_length)
|
|
{
|
|
Internal_Accumulate(p,max_length);
|
|
sizeof_buffer -= max_length;
|
|
p += max_length;
|
|
}
|
|
Internal_Accumulate(p,(ON__UINT32)sizeof_buffer);
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////
|
|
|
|
// decodes input (unsigned char) into output (ON__UINT32). Assumes len is a multiple of 4.
|
|
static void MD5_decode(ON__UINT32 output[], const ON__UINT8 input[], ON__UINT32 len)
|
|
{
|
|
for (unsigned int i = 0, j = 0; j < len; i++, j += 4)
|
|
output[i] = ((ON__UINT32)input[j]) | (((ON__UINT32)input[j+1]) << 8) |
|
|
(((ON__UINT32)input[j+2]) << 16) | (((ON__UINT32)input[j+3]) << 24);
|
|
}
|
|
|
|
//////////////////////////////
|
|
|
|
// encodes input (ON__UINT32) into output (unsigned char). Assumes len is
|
|
// a multiple of 4.
|
|
static void MD5_encode(ON__UINT8 output[], const ON__UINT32 input[], ON__UINT32 len)
|
|
{
|
|
for (ON__UINT32 i = 0, j = 0; j < len; i++, j += 4) {
|
|
output[j] = input[i] & 0xff;
|
|
output[j+1] = (input[i] >> 8) & 0xff;
|
|
output[j+2] = (input[i] >> 16) & 0xff;
|
|
output[j+3] = (input[i] >> 24) & 0xff;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////
|
|
|
|
// apply MD5 algo on a block
|
|
static void MD5_transform(const ON__UINT8 block[64], ON__UINT32 state[4])
|
|
{
|
|
ON__UINT32 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
|
|
MD5_decode (x, block, 64);
|
|
|
|
/* Round 1 */
|
|
MD5_FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
|
|
MD5_FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
|
|
MD5_FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
|
|
MD5_FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
|
|
MD5_FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
|
|
MD5_FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
|
|
MD5_FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
|
|
MD5_FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
|
|
MD5_FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
|
|
MD5_FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
|
|
MD5_FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
|
|
MD5_FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
|
|
MD5_FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
|
|
MD5_FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
|
|
MD5_FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
|
|
MD5_FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
|
|
|
|
/* Round 2 */
|
|
MD5_GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
|
|
MD5_GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
|
|
MD5_GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
|
|
MD5_GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
|
|
MD5_GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
|
|
MD5_GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
|
|
MD5_GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
|
|
MD5_GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
|
|
MD5_GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
|
|
MD5_GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
|
|
MD5_GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
|
|
MD5_GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
|
|
MD5_GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
|
|
MD5_GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
|
|
MD5_GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
|
|
MD5_GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
|
|
|
|
/* Round 3 */
|
|
MD5_HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
|
|
MD5_HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
|
|
MD5_HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
|
|
MD5_HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
|
|
MD5_HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
|
|
MD5_HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
|
|
MD5_HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
|
|
MD5_HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
|
|
MD5_HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
|
|
MD5_HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
|
|
MD5_HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
|
|
MD5_HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
|
|
MD5_HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
|
|
MD5_HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
|
|
MD5_HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
|
|
MD5_HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
|
|
|
|
/* Round 4 */
|
|
MD5_II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
|
|
MD5_II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
|
|
MD5_II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
|
|
MD5_II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
|
|
MD5_II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
|
|
MD5_II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
|
|
MD5_II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
|
|
MD5_II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
|
|
MD5_II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
|
|
MD5_II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
|
|
MD5_II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
|
|
MD5_II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
|
|
MD5_II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
|
|
MD5_II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
|
|
MD5_II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
|
|
MD5_II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
|
|
|
|
state[0] += a;
|
|
state[1] += b;
|
|
state[2] += c;
|
|
state[3] += d;
|
|
}
|
|
|
|
void ON_MD5::Internal_Accumulate(const ON__UINT8* input, ON__UINT32 length)
|
|
{
|
|
// MD5 block update operation. Continues an MD5 message-digest
|
|
// operation, processing another message block.
|
|
|
|
// compute number of bytes mod 64
|
|
ON__UINT32 index = m_bit_count[0] / 8 % 64;
|
|
|
|
// Update number of bits
|
|
if ((m_bit_count[0] += (length << 3)) < (length << 3))
|
|
m_bit_count[1]++;
|
|
m_bit_count[1] += (length >> 29);
|
|
|
|
// number of bytes we need to fill in buffer
|
|
ON__UINT32 firstpart = 64 - index;
|
|
|
|
ON__UINT32 i;
|
|
|
|
// transform as many times as possible.
|
|
if (length >= firstpart)
|
|
{
|
|
// fill buffer first, transform
|
|
memcpy(&m_buffer[index], input, firstpart);
|
|
MD5_transform(m_buffer, m_state);
|
|
|
|
// transform chunks of blocksize (64 bytes)
|
|
for (i = firstpart; i + 64 <= length; i += 64)
|
|
MD5_transform(&input[i], m_state);
|
|
|
|
index = 0;
|
|
}
|
|
else
|
|
i = 0;
|
|
|
|
// buffer remaining input
|
|
memcpy(&m_buffer[index], &input[i], length-i);
|
|
}
|
|
|
|
void ON_MD5::set_final_hash()
|
|
{
|
|
// MD5 finalization.
|
|
// Ends an MD5 message-digest operation and writes the message digest.
|
|
static unsigned char padding[64] = {
|
|
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
|
};
|
|
|
|
// Save number of bits
|
|
unsigned char bits[8];
|
|
MD5_encode(bits, m_bit_count, 8);
|
|
|
|
// pad out to 56 mod 64.
|
|
const ON__UINT32 index = (m_bit_count[0] / 8) % 64;
|
|
const ON__UINT32 padLen = (index < 56) ? (56 - index) : (120 - index);
|
|
Internal_Accumulate(padding, padLen);
|
|
|
|
// Append length (before padding)
|
|
Internal_Accumulate(bits, 8);
|
|
|
|
// Store final state in digest
|
|
MD5_encode(m_md5_hash.m_digest, m_state, 16);
|
|
}
|
|
|