md5.cpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. #include "md5.h"
  2. #include <library/cpp/string_utils/base64/base64.h>
  3. #include <util/stream/input.h>
  4. #include <util/stream/file.h>
  5. #include <util/string/hex.h>
  6. #include <contrib/libs/nayuki_md5/md5.h>
  7. namespace {
  8. constexpr size_t MD5_BLOCK_LENGTH = 64;
  9. constexpr size_t MD5_PADDING_SHIFT = 56;
  10. constexpr size_t MD5_HEX_DIGEST_LENGTH = 32;
  11. struct TMd5Stream: public IOutputStream {
  12. inline TMd5Stream(MD5* md5)
  13. : M_(md5)
  14. {
  15. }
  16. void DoWrite(const void* buf, size_t len) override {
  17. M_->Update(buf, len);
  18. }
  19. MD5* M_;
  20. };
  21. inline TArrayRef<const ui8> MakeUnsignedArrayRef(const void* data, const size_t size) {
  22. return MakeArrayRef(static_cast<const ui8*>(data), size);
  23. }
  24. inline TArrayRef<const ui8> MakeUnsignedArrayRef(const TArrayRef<const char>& data) {
  25. return MakeUnsignedArrayRef(data.data(), data.size());
  26. }
  27. }
  28. char* MD5::File(const char* filename, char* buf) {
  29. try {
  30. TUnbufferedFileInput fi(filename);
  31. return Stream(&fi, buf);
  32. } catch (...) {
  33. }
  34. return nullptr;
  35. }
  36. TString MD5::File(const TString& filename) {
  37. TString buf;
  38. buf.ReserveAndResize(MD5_HEX_DIGEST_LENGTH);
  39. auto result = MD5::File(filename.data(), buf.begin());
  40. if (result == nullptr) {
  41. buf.clear();
  42. }
  43. return buf;
  44. }
  45. char* MD5::Data(const TArrayRef<const ui8>& data, char* buf) {
  46. return MD5().Update(data).End(buf);
  47. }
  48. char* MD5::Data(const void* data, size_t len, char* buf) {
  49. return Data(MakeUnsignedArrayRef(data, len), buf);
  50. }
  51. TString MD5::Data(const TArrayRef<const ui8>& data) {
  52. TString buf;
  53. buf.ReserveAndResize(MD5_HEX_DIGEST_LENGTH);
  54. Data(data, buf.begin());
  55. return buf;
  56. }
  57. TString MD5::Data(TStringBuf data) {
  58. return Data(MakeUnsignedArrayRef(data));
  59. }
  60. char* MD5::Stream(IInputStream* in, char* buf) {
  61. return MD5().Update(in).End(buf);
  62. }
  63. MD5& MD5::Update(IInputStream* in) {
  64. TMd5Stream md5(this);
  65. TransferData(in, &md5);
  66. return *this;
  67. }
  68. static const ui8 PADDING[MD5_BLOCK_LENGTH] = {
  69. 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  70. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  71. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  72. /* MD5 initialization. Begins an MD5 operation, writing a new context. */
  73. void MD5::Init() {
  74. BufferSize = 0;
  75. StreamSize = 0;
  76. /* Load magic initialization constants. */
  77. State[0] = 0x67452301;
  78. State[1] = 0xefcdab89;
  79. State[2] = 0x98badcfe;
  80. State[3] = 0x10325476;
  81. }
  82. /*
  83. * MD5 block update operation. Continues an MD5 message-digest
  84. * operation, processing another message block, and updating the
  85. * context.
  86. */
  87. void MD5::UpdatePart(TArrayRef<const ui8> data) {
  88. /* Count input bytes */
  89. StreamSize += data.size();
  90. if (BufferSize > 0) {
  91. /* Filling remaining buffer */
  92. const ui8 freeBufferSize = MD5_BLOCK_LENGTH - BufferSize;
  93. const ui8 partLen = data.size() >= freeBufferSize ? freeBufferSize : data.size();
  94. memcpy(&Buffer[BufferSize], data.data(), partLen);
  95. BufferSize += partLen;
  96. data = data.Slice(partLen);
  97. if (BufferSize == MD5_BLOCK_LENGTH) {
  98. /* Buffer is full and ready for hashing */
  99. md5_compress(State, Buffer);
  100. BufferSize = 0;
  101. }
  102. }
  103. /* Processing input by chanks */
  104. while (data.size() >= MD5_BLOCK_LENGTH) {
  105. md5_compress(State, data.data());
  106. data = data.Slice(MD5_BLOCK_LENGTH);
  107. }
  108. /* Save remaining input in buffer */
  109. memcpy(Buffer, data.data(), data.size());
  110. BufferSize += data.size();
  111. }
  112. /*
  113. * MD5 padding. Adds padding followed by original length.
  114. */
  115. void MD5::Pad() {
  116. size_t streamSize = StreamSize;
  117. const size_t paddingSize = (MD5_PADDING_SHIFT > BufferSize) ? (MD5_PADDING_SHIFT - BufferSize) : (MD5_PADDING_SHIFT + MD5_BLOCK_LENGTH - BufferSize);
  118. Update(PADDING, paddingSize);
  119. // Size of stream in bits
  120. // If size greater than 2^64 - 1 only lower 64 bits used
  121. streamSize <<= 3;
  122. for (int i = 0; i < 8; ++i, streamSize >>= 8) {
  123. // Storing in reverse order
  124. Buffer[MD5_PADDING_SHIFT + i] = static_cast<ui8>(streamSize & 0xFFU);
  125. }
  126. md5_compress(State, Buffer);
  127. }
  128. /*
  129. * MD5 finalization. Ends an MD5 message-digest operation, writing the
  130. * the message digest and zeroizing the context.
  131. */
  132. ui8* MD5::Final(ui8 digest[16]) {
  133. /* Do padding. */
  134. Pad();
  135. /* Store state in digest */
  136. memcpy(digest, State, 16);
  137. /* Zeroize sensitive information. */
  138. Init();
  139. return digest;
  140. }
  141. char* MD5::End(char* buf) {
  142. static const char hex[] = "0123456789abcdef";
  143. ui8 digest[16];
  144. if (!buf)
  145. buf = (char*)malloc(33);
  146. if (!buf)
  147. return nullptr;
  148. Final(digest);
  149. for (ui8 i = 0; i < MD5_HEX_DIGEST_LENGTH / 2; i++) {
  150. buf[i * 2] = hex[digest[i] >> 4];
  151. buf[i * 2 + 1] = hex[digest[i] & 0x0f];
  152. }
  153. buf[32] = '\0';
  154. return buf;
  155. }
  156. char* MD5::End_b64(char* buf) {
  157. ui8 digest[16];
  158. if (!buf)
  159. buf = (char*)malloc(25);
  160. if (!buf)
  161. return nullptr;
  162. Final(digest);
  163. Base64Encode(buf, digest, 16);
  164. buf[24] = '\0';
  165. return buf;
  166. }
  167. ui64 MD5::EndHalfMix() {
  168. ui8 digest[16];
  169. Final(digest);
  170. ui64 res = 0;
  171. for (int i = 3; i >= 0; i--) {
  172. res |= (ui64)(digest[0 + i] ^ digest[8 + i]) << ((3 - i) << 3);
  173. res |= (ui64)(digest[4 + i] ^ digest[12 + i]) << ((7 - i) << 3);
  174. }
  175. return res;
  176. }
  177. TString MD5::Calc(TStringBuf data) {
  178. return Calc(MakeUnsignedArrayRef(data));
  179. }
  180. TString MD5::Calc(const TArrayRef<const ui8>& data) {
  181. return Data(data);
  182. }
  183. TString MD5::CalcRaw(TStringBuf data) {
  184. return CalcRaw(MakeUnsignedArrayRef(data));
  185. }
  186. TString MD5::CalcRaw(const TArrayRef<const ui8>& data) {
  187. TString result;
  188. result.ReserveAndResize(16);
  189. MD5().Update(data).Final(reinterpret_cast<ui8*>(result.begin()));
  190. return result;
  191. }
  192. ui64 MD5::CalcHalfMix(const char* data, size_t len) {
  193. return CalcHalfMix(MakeUnsignedArrayRef(data, len));
  194. }
  195. ui64 MD5::CalcHalfMix(TStringBuf data) {
  196. return CalcHalfMix(MakeUnsignedArrayRef(data));
  197. }
  198. ui64 MD5::CalcHalfMix(const TArrayRef<const ui8>& data) {
  199. return MD5().Update(data).EndHalfMix();
  200. }
  201. bool MD5::IsMD5(TStringBuf data) {
  202. return IsMD5(MakeUnsignedArrayRef(data));
  203. }
  204. bool MD5::IsMD5(const TArrayRef<const ui8>& data) {
  205. if (data.size() != 32) {
  206. return false;
  207. }
  208. for (const ui8 *p = data.data(), *e = data.data() + data.size(); p != e; ++p) {
  209. if (Char2DigitTable[*p] == '\xff') {
  210. return false;
  211. }
  212. }
  213. return true;
  214. }