|
| 1 | +/* |
| 2 | + MD5Builder - Simple MD5 hash calculations |
| 3 | +
|
| 4 | + Updated for the Pico by Earle F. Philhower, III |
| 5 | +
|
| 6 | + Modified from the ESP8266 version which is |
| 7 | + Copyright (c) 2015 Hristo Gochkov. All rights reserved. |
| 8 | + This file is part of the esp8266 core for Arduino environment. |
| 9 | +
|
| 10 | + This library is free software; you can redistribute it and/or |
| 11 | + modify it under the terms of the GNU Lesser General Public |
| 12 | + License as published by the Free Software Foundation; either |
| 13 | + version 2.1 of the License, or (at your option) any later version. |
| 14 | +
|
| 15 | + This library is distributed in the hope that it will be useful, |
| 16 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 17 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 18 | + Lesser General Public License for more details. |
| 19 | +
|
| 20 | + You should have received a copy of the GNU Lesser General Public |
| 21 | + License along with this library; if not, write to the Free Software |
| 22 | + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| 23 | +*/ |
| 24 | + |
| 25 | +#include <Arduino.h> |
| 26 | +#include <MD5Builder.h> |
| 27 | +#include <memory> |
| 28 | + |
| 29 | +/* |
| 30 | + * Constants defined by the MD5 algorithm |
| 31 | + */ |
| 32 | +#define A 0x67452301 |
| 33 | +#define B 0xefcdab89 |
| 34 | +#define C 0x98badcfe |
| 35 | +#define D 0x10325476 |
| 36 | + |
| 37 | +static uint32_t S[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, |
| 38 | + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, |
| 39 | + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, |
| 40 | + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21}; |
| 41 | + |
| 42 | +static uint32_t K[] = {0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, |
| 43 | + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, |
| 44 | + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, |
| 45 | + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, |
| 46 | + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, |
| 47 | + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, |
| 48 | + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, |
| 49 | + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, |
| 50 | + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, |
| 51 | + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, |
| 52 | + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, |
| 53 | + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, |
| 54 | + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, |
| 55 | + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, |
| 56 | + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, |
| 57 | + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391}; |
| 58 | + |
| 59 | +/* |
| 60 | + * Padding used to make the size (in bits) of the input congruent to 448 mod 512 |
| 61 | + */ |
| 62 | +static uint8_t PADDING[] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 63 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 64 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 65 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 66 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 67 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 68 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 69 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; |
| 70 | + |
| 71 | +/* |
| 72 | + * Bit-manipulation functions defined by the MD5 algorithm |
| 73 | + */ |
| 74 | +#ifdef F |
| 75 | +#undef F |
| 76 | +#endif |
| 77 | +#define F(X, Y, Z) ((X & Y) | (~X & Z)) |
| 78 | +#define G(X, Y, Z) ((X & Z) | (Y & ~Z)) |
| 79 | +#define H(X, Y, Z) (X ^ Y ^ Z) |
| 80 | +#define I(X, Y, Z) (Y ^ (X | ~Z)) |
| 81 | + |
| 82 | +/* |
| 83 | + * Rotates a 32-bit word left by n bits |
| 84 | + */ |
| 85 | +static inline uint32_t rotateLeft(uint32_t x, uint32_t n){ |
| 86 | + return (x << n) | (x >> (32 - n)); |
| 87 | +} |
| 88 | + |
| 89 | +/* |
| 90 | + * Step on 512 bits of input with the main MD5 algorithm. |
| 91 | + */ |
| 92 | +static void md5Step(uint32_t *buffer, uint32_t *input){ |
| 93 | + uint32_t AA = buffer[0]; |
| 94 | + uint32_t BB = buffer[1]; |
| 95 | + uint32_t CC = buffer[2]; |
| 96 | + uint32_t DD = buffer[3]; |
| 97 | + |
| 98 | + uint32_t E; |
| 99 | + |
| 100 | + unsigned int j; |
| 101 | + |
| 102 | + for(unsigned int i = 0; i < 64; ++i){ |
| 103 | + switch(i / 16){ |
| 104 | + case 0: |
| 105 | + E = F(BB, CC, DD); |
| 106 | + j = i; |
| 107 | + break; |
| 108 | + case 1: |
| 109 | + E = G(BB, CC, DD); |
| 110 | + j = ((i * 5) + 1) % 16; |
| 111 | + break; |
| 112 | + case 2: |
| 113 | + E = H(BB, CC, DD); |
| 114 | + j = ((i * 3) + 5) % 16; |
| 115 | + break; |
| 116 | + default: |
| 117 | + E = I(BB, CC, DD); |
| 118 | + j = (i * 7) % 16; |
| 119 | + break; |
| 120 | + } |
| 121 | + |
| 122 | + uint32_t temp = DD; |
| 123 | + DD = CC; |
| 124 | + CC = BB; |
| 125 | + BB = BB + rotateLeft(AA + E + K[i] + input[j], S[i]); |
| 126 | + AA = temp; |
| 127 | + } |
| 128 | + |
| 129 | + buffer[0] += AA; |
| 130 | + buffer[1] += BB; |
| 131 | + buffer[2] += CC; |
| 132 | + buffer[3] += DD; |
| 133 | +} |
| 134 | + |
| 135 | +/* |
| 136 | + * Add some amount of input to the context |
| 137 | + * |
| 138 | + * If the input fills out a block of 512 bits, apply the algorithm (md5Step) |
| 139 | + * and save the result in the buffer. Also updates the overall size. |
| 140 | + */ |
| 141 | +void MD5Builder::add(const uint8_t* input_buffer, const size_t input_len) { |
| 142 | + uint32_t input[16]; |
| 143 | + size_t offset = _size % 64; |
| 144 | + _size += input_len; |
| 145 | + |
| 146 | + // Copy each byte in input_buffer into the next space in our context input |
| 147 | + for(size_t i = 0; i < input_len; ++i){ |
| 148 | + _input[offset++] = (uint8_t)*(input_buffer + i); |
| 149 | + |
| 150 | + // If we've filled our context input, copy it into our local array input |
| 151 | + // then reset the offset to 0 and fill in a new buffer. |
| 152 | + // Every time we fill out a chunk, we run it through the algorithm |
| 153 | + // to enable some back and forth between cpu and i/o |
| 154 | + if(offset % 64 == 0){ |
| 155 | + for(unsigned int j = 0; j < 16; ++j){ |
| 156 | + // Convert to little-endian |
| 157 | + // The local variable `input` our 512-bit chunk separated into 32-bit words |
| 158 | + // we can use in calculations |
| 159 | + input[j] = (uint32_t)(_input[(j * 4) + 3]) << 24 | |
| 160 | + (uint32_t)(_input[(j * 4) + 2]) << 16 | |
| 161 | + (uint32_t)(_input[(j * 4) + 1]) << 8 | |
| 162 | + (uint32_t)(_input[(j * 4)]); |
| 163 | + } |
| 164 | + md5Step(_buffer, input); |
| 165 | + offset = 0; |
| 166 | + } |
| 167 | + } |
| 168 | +} |
| 169 | + |
| 170 | +static bool hex_char_to_nibble(uint8_t c, uint8_t& nibble) { |
| 171 | + if (c >= 'a' && c <= 'f') { |
| 172 | + nibble = c - ((uint8_t)'a' - 0xA); |
| 173 | + return true; |
| 174 | + } |
| 175 | + if (c >= 'A' && c <= 'F') { |
| 176 | + nibble = c - ((uint8_t)'A' - 0xA); |
| 177 | + return true; |
| 178 | + } |
| 179 | + if (c >= '0' && c <= '9') { |
| 180 | + nibble = c - (uint8_t)'0'; |
| 181 | + return true; |
| 182 | + } |
| 183 | + return false; |
| 184 | +} |
| 185 | + |
| 186 | +void MD5Builder::begin(void) { |
| 187 | + _size = 0; |
| 188 | + |
| 189 | + _buffer[0] = (uint32_t)A; |
| 190 | + _buffer[1] = (uint32_t)B; |
| 191 | + _buffer[2] = (uint32_t)C; |
| 192 | + _buffer[3] = (uint32_t)D; |
| 193 | +} |
| 194 | + |
| 195 | +void MD5Builder::addHexString(const char * data) { |
| 196 | + size_t len = strlen(data); |
| 197 | + |
| 198 | + // Require an even number of hex characters; odd lengths cannot form full bytes. |
| 199 | + if ((len == 0) || (len % 2 != 0)) { |
| 200 | + return; |
| 201 | + } |
| 202 | + |
| 203 | + constexpr size_t chunk_size = 64; |
| 204 | + uint8_t tmp[chunk_size]; |
| 205 | + size_t byte_count = len / 2; |
| 206 | + |
| 207 | + for (size_t processed = 0; processed < byte_count;) { |
| 208 | + size_t remaining = byte_count - processed; |
| 209 | + size_t this_chunk = (remaining > chunk_size) ? chunk_size : remaining; |
| 210 | + |
| 211 | + for (size_t i = 0; i < this_chunk; ++i) { |
| 212 | + size_t hex_index = (processed + i) * 2; |
| 213 | + uint8_t high; |
| 214 | + uint8_t low; |
| 215 | + |
| 216 | + if (!hex_char_to_nibble(static_cast<uint8_t>(data[hex_index]), high) || |
| 217 | + !hex_char_to_nibble(static_cast<uint8_t>(data[hex_index + 1]), low)) { |
| 218 | + return; |
| 219 | + } |
| 220 | + |
| 221 | + tmp[i] = static_cast<uint8_t>((high << 4) | low); |
| 222 | + } |
| 223 | + |
| 224 | + add(tmp, this_chunk); |
| 225 | + processed += this_chunk; |
| 226 | + } |
| 227 | +} |
| 228 | + |
| 229 | +bool MD5Builder::addStream(Stream &stream, const size_t maxLen) { |
| 230 | + const int buf_size = 512; |
| 231 | + size_t maxLengthLeft = maxLen; |
| 232 | + |
| 233 | + auto buf = std::unique_ptr<uint8_t[]> {new (std::nothrow) uint8_t[buf_size]}; |
| 234 | + |
| 235 | + if (!buf) { |
| 236 | + return false; |
| 237 | + } |
| 238 | + |
| 239 | + size_t bytesAvailable = stream.available(); |
| 240 | + while ((bytesAvailable > 0) && (maxLengthLeft > 0)) { |
| 241 | + |
| 242 | + // determine number of bytes to read |
| 243 | + size_t readBytes = bytesAvailable; |
| 244 | + if (readBytes > maxLengthLeft) { |
| 245 | + readBytes = maxLengthLeft; // read only until max_len |
| 246 | + } |
| 247 | + if (readBytes > buf_size) { |
| 248 | + readBytes = buf_size; // not read more the buffer can handle |
| 249 | + } |
| 250 | + |
| 251 | + // read data and check if we got something |
| 252 | + size_t numBytesRead = stream.readBytes(buf.get(), readBytes); |
| 253 | + if (numBytesRead < 1) { |
| 254 | + return false; |
| 255 | + } |
| 256 | + |
| 257 | + // Update MD5 with buffer payload |
| 258 | + add(buf.get(), numBytesRead); |
| 259 | + |
| 260 | + // update available number of bytes |
| 261 | + maxLengthLeft -= numBytesRead; |
| 262 | + bytesAvailable = stream.available(); |
| 263 | + } |
| 264 | + |
| 265 | + return true; |
| 266 | +} |
| 267 | + |
| 268 | +/* |
| 269 | + * Pad the current input so its length is congruent to 448 bits (56 bytes) modulo 512 bits, |
| 270 | + * then append the size in bits to the very end, and save the result of the final iteration |
| 271 | + * into digest. |
| 272 | + */ |
| 273 | +void MD5Builder::calculate(void) { |
| 274 | + uint32_t input[16]; |
| 275 | + size_t offset = _size % 64; |
| 276 | + size_t padding_length = offset < 56 ? 56 - offset : (56 + 64) - offset; |
| 277 | + |
| 278 | + // Fill in the padding and undo the changes to size that resulted from the update |
| 279 | + add(PADDING, padding_length); |
| 280 | + _size -= padding_length; |
| 281 | + |
| 282 | + // Do a final update (internal to this function) |
| 283 | + // Last two 32-bit words are the two halves of the size (converted from bytes to bits) |
| 284 | + for(unsigned int j = 0; j < 14; ++j){ |
| 285 | + input[j] = (uint32_t)(_input[(j * 4) + 3]) << 24 | |
| 286 | + (uint32_t)(_input[(j * 4) + 2]) << 16 | |
| 287 | + (uint32_t)(_input[(j * 4) + 1]) << 8 | |
| 288 | + (uint32_t)(_input[(j * 4)]); |
| 289 | + } |
| 290 | + uint64_t bit_length = _size * 8ULL; |
| 291 | + input[14] = (uint32_t)(bit_length); |
| 292 | + input[15] = (uint32_t)(bit_length >> 32); |
| 293 | + |
| 294 | + md5Step(_buffer, input); |
| 295 | + |
| 296 | + // Move the result into digest (convert from little-endian) |
| 297 | + for(unsigned int i = 0; i < 4; ++i){ |
| 298 | + _digest[(i * 4) + 0] = (uint8_t)((_buffer[i] & 0x000000FF)); |
| 299 | + _digest[(i * 4) + 1] = (uint8_t)((_buffer[i] & 0x0000FF00) >> 8); |
| 300 | + _digest[(i * 4) + 2] = (uint8_t)((_buffer[i] & 0x00FF0000) >> 16); |
| 301 | + _digest[(i * 4) + 3] = (uint8_t)((_buffer[i] & 0xFF000000) >> 24); |
| 302 | + } |
| 303 | +} |
| 304 | + |
| 305 | +void MD5Builder::getBytes(uint8_t * output) const { |
| 306 | + memcpy(output, _digest, 16); |
| 307 | +} |
| 308 | + |
| 309 | +void MD5Builder::getChars(char * output) const { |
| 310 | + for (uint8_t i = 0; i < 16; i++) { |
| 311 | + sprintf(output + (i * 2), "%02x", _digest[i]); |
| 312 | + } |
| 313 | +} |
| 314 | + |
| 315 | +String MD5Builder::toString(void) const { |
| 316 | + char out[33]; |
| 317 | + getChars(out); |
| 318 | + return String(out); |
| 319 | +} |
0 commit comments