2

The common implementation for MD5 is given by RFC1321. Where the MD5Update function receive a pointer to chars.

My architecture, a SHARC ADSP-21371, is not byte adressable which means:

sizeof(int32_t) == 1

Thus I cannot really use this algorithm as is. I need to wrap some complexity to unpack each int32_t data.

Is there an alternative solution that I can use out of the box and if possible compatible C99 or C11?

nowox
  • 25,978
  • 39
  • 143
  • 293
  • I've never implemented md5. However, according to Wikipedia, md5 uses 32-bit words, with the only complication being that they're *little-endian*, you could adapt the [pseudo-code algorithm](https://en.wikipedia.org/wiki/MD5#Pseudocode) from there. – Antti Haapala -- Слава Україні Jun 26 '17 at 07:55
  • Other than that, this falls under the "resource request", which with this would probably basically become "would you google this for me". – Antti Haapala -- Слава Україні Jun 26 '17 at 07:56
  • I googled this, but I did not find anything yet. – nowox Jun 26 '17 at 07:57
  • Did you get any real problems with any md5 implementation? I assume that the compiler should be able to work without any problems with a ANSI-C compliant solution – jeb Jun 26 '17 at 09:14
  • Not really. If `uint8_t` doesn't exist you're doomed with the standard implementation. – nowox Jun 26 '17 at 09:20
  • Then I say you need to write this yourself. The code doesn't seem that difficult to implement from scratch. At least the pseudo doesn't need byte addressing anywhere, except just for the message padding, which again would be a no-brainer - and you might need BE->LE if you want the message to be hashed in LE order. – Antti Haapala -- Слава Україні Jun 26 '17 at 16:00
  • And btw, your arch *is* *byte* / *char* addressable, with CHAR_BIT == 32. It is non-octet addressable. – Antti Haapala -- Слава Україні Jun 26 '17 at 16:02
  • @AnttiHaapala well, you're right. The proper term is `octet` not `byte`. – nowox Jun 26 '17 at 16:11
  • @Groo go and check ADSP-21371 then – nowox Jun 26 '17 at 18:54
  • @nowox Your compiler should not compile, `sizeof(int32_t) = 1`, mean that one char on your system is 32 bits... I never work on embedded but this suck. – Stargateur Jun 26 '17 at 20:16
  • @Stargateur exactly this is f***ing annoying when you want to unpack data received from the network. On this arch, int8_t and int16_t do not exist :( – nowox Jun 26 '17 at 21:43
  • @Stargateur is there any known pattern do this? I feel completely doomed with EtherCAT where I receive x86 C structures data that I cannot unpack because the alignment information is missing. The only workaround is to write a manual wrapper around each structure to fill it properly. – nowox Jun 28 '17 at 11:33

1 Answers1

2

I finally implemented MD5 for a octet-addressable architecture, here is for a DSP SHARC (ADSP-21371).

md5.h

#ifndef MD5_H
#define MD5_H

#include <stdlib.h>
#include <stdint.h>

typedef struct {
  uint32_t state[4];   //!< State (ABCD)
  uint64_t count;      //!< Number of bits
  uint32_t buffer[16]; //!< Input buffer
} md5_context_t;

void md5_init(md5_context_t*);
void md5_update(md5_context_t*, uint32_t*, size_t);
void md5_final(md5_context_t*);

#endif /* MD5_H */         

md5.c

/**
 * This is a custom implementation for MD5 hash algorithm derived from RFC1321
 * https://www.ietf.org/rfc/rfc1321.txt
 *
 * The original implementation requires a octet-addressable processor which is not
 * the case of the SHARC. Thus the implementation must me modified to support this
 * processor.
 *
 * It comes with a limitation when the input buffer is not a multple of 32-bits.
 * At the end of the the message, a padding is inserted starting with 0x80. If the
 * message is 0x12345678, the beginning of the 64-bytes buffer will be:
 *
 * 0x012345678
 * 0x000000080
 * 0x000000000
 * ...
 *
 * If the input message is 0x78 0x56 0x34 0x12 0xAA, the input buffer should be:
 *
 * 0x012345678
 * 0x0000080AA
 * 0x000000000
 * ...
 *
 * Which will not be the case in this implementation because the given size to md5_update
 * is expressed in sizeof(long) which is 32-bits wide on a SHARC architecture.
 */
#include "md5.h"
#include <string.h>

#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

static uint32_t padding[16] = {
    [0] = 0x80, [1 ... sizeof(padding) / sizeof(uint32_t) - 2] = 0};

/**
 * F, G, H and I are basic MD5 functions.
 */
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))

/**
 * ROTATE_LEFT rotates x left n bits.
 */
#define ROTATE_LEFT(x, n) (((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.
  */
#define FF(a, b, c, d, x, s, ac) { \
    (a) += F ((b), (c), (d)) + (x) + (uint32_t)(ac); \
    (a) = ROTATE_LEFT ((a), (s)); \
    (a) += (b); \
    }

#define GG(a, b, c, d, x, s, ac) { \
    (a) += G ((b), (c), (d)) + (x) + (uint32_t)(ac); \
    (a) = ROTATE_LEFT ((a), (s)); \
    (a) += (b); \
    }

#define HH(a, b, c, d, x, s, ac) { \
    (a) += H ((b), (c), (d)) + (x) + (uint32_t)(ac); \
    (a) = ROTATE_LEFT ((a), (s)); \
    (a) += (b); \
    }

#define II(a, b, c, d, x, s, ac) { \
    (a) += I ((b), (c), (d)) + (x) + (uint32_t)(ac); \
    (a) = ROTATE_LEFT ((a), (s)); \
    (a) += (b); \
    }

/**
 * MD5 basic transformation. Transforms state based on block.
 */
static void md5_transform(uint32_t state[4], uint32_t x[16])
{
  uint32_t a = state[0];
  uint32_t b = state[1];
  uint32_t c = state[2];
  uint32_t d = state[3];

  // Round 1
  FF(a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
  FF(d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
  FF(c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
  FF(b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
  FF(a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
  FF(d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
  FF(c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
  FF(b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
  FF(a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
  FF(d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
  FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
  FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
  FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
  FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
  FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
  FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */

  // Round 2
  GG(a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
  GG(d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
  GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
  GG(b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
  GG(a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
  GG(d, a, b, c, x[10], S22, 0x02441453); /* 22 */
  GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
  GG(b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
  GG(a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
  GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
  GG(c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
  GG(b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
  GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
  GG(d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
  GG(c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
  GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */

  // Round 3
  HH(a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
  HH(d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
  HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
  HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
  HH(a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
  HH(d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
  HH(c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
  HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
  HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
  HH(d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
  HH(c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
  HH(b, c, d, a, x[ 6], S34, 0x04881d05); /* 44 */
  HH(a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
  HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
  HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
  HH(b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */

  // Round 4
  II(a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
  II(d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
  II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
  II(b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
  II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
  II(d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
  II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
  II(b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
  II(a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
  II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
  II(c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
  II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
  II(a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
  II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
  II(c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
  II(b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */

  state[0] += a;
  state[1] += b;
  state[2] += c;
  state[3] += d;
}

/**
 * MD5 block update operation. Continues an MD5 message-digest
 * operation, processing another message block, and updating the
 * context.
 */
void md5_update(md5_context_t *context, uint32_t* input, size_t length)
{
    size_t size = 0;
    size_t index = (context->count >> 5) & 0xF; // Modulo 16
    size_t part_len = 16 - index;

    context->count += length << 5; // Update number of bits

    // Transform as many times as possible.
    if (length >= part_len) {
        memcpy(&context->buffer[index], input, sizeof(uint32_t) * part_len);
        md5_transform(context->state, context->buffer);
        for (size_t size = part_len; size + 15 < length; size += 16)
            md5_transform(context->state, &input[size]);
        index = 0;
    }

    // Buffer remaining input
    memcpy(&context->buffer[index], &input[size], sizeof(uint32_t) * (length - size));
}

/**
 * MD5 finalization. Ends an MD5 message-digest operation, writing the
 * the message digest and zeroizing the context.
 */
void md5_final(md5_context_t *context)
{
  int64_t count = context->count; // Count before padding

  // Pad out to 56 mod 64 bytes.
  size_t index = (context->count >> 5) & 0xf;
  size_t padLen = index < 14 ? 14 - index : 30 - index;

  md5_update(context, padding, padLen);
  md5_update(context, &((uint32_t*)(&count))[1], 1);
  md5_update(context, &((uint32_t*)(&count))[0], 1);
}

/**
 * MD5 initialization. Begins an MD5 operation, writing a new context.
 */
void md5_init(md5_context_t *context)
{
  context->count = 0;

  context->state[0] = 0x67452301;
  context->state[1] = 0xefcdab89;
  context->state[2] = 0x98badcfe;
  context->state[3] = 0x10325476;
}
nowox
  • 25,978
  • 39
  • 143
  • 293