#include <sys/endian.h>
#include <sys/types.h>
#include <sys/md5.h>
#ifdef _KERNEL
#include <sys/param.h>
#include <sys/stdint.h>
#include <sys/systm.h>
#define assert(expr) MPASS(expr)
#else
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include <strings.h>
#endif
#define md5block _libmd_md5block
#ifdef MD5_ASM
extern void md5block(MD5_CTX *, const void *, size_t);
#else
static void md5block(MD5_CTX *, const void *, size_t);
#endif
#ifdef STANDALONE_SMALL
#define UNROLL
#else
#define UNROLL _Pragma("unroll")
#endif
void
MD5Init(MD5_CTX *ctx)
{
ctx->state[0] = 0x67452301;
ctx->state[1] = 0xefcdab89;
ctx->state[2] = 0x98badcfe;
ctx->state[3] = 0x10325476;
ctx->count[0] = 0;
ctx->count[1] = 0;
}
void
MD5Update(MD5_CTX *ctx, const void *data, unsigned int len)
{
uint64_t nn;
const char *p = data;
unsigned num;
num = ctx->count[0] % MD5_BLOCK_LENGTH;
nn = (uint64_t)ctx->count[0] | (uint64_t)ctx->count[1] << 32;
nn += len;
ctx->count[0] = (uint32_t)nn;
ctx->count[1] = (uint32_t)(nn >> 32);
if (num > 0) {
unsigned int n = MD5_BLOCK_LENGTH - num;
if (n > len)
n = len;
memcpy((char *)ctx->buffer + num, p, n);
num += n;
if (num == MD5_BLOCK_LENGTH)
md5block(ctx, (void *)ctx->buffer, MD5_BLOCK_LENGTH);
p += n;
len -= n;
}
if (len >= MD5_BLOCK_LENGTH) {
unsigned n = len & ~(unsigned)(MD5_BLOCK_LENGTH - 1);
md5block(ctx, p, n);
p += n;
len -= n;
}
if (len > 0)
memcpy((void *)ctx->buffer, p, len);
}
static void
MD5Pad(MD5_CTX *ctx)
{
uint64_t len;
unsigned t;
unsigned char tmp[MD5_BLOCK_LENGTH + sizeof(uint64_t)] = {0x80, 0};
len = (uint64_t)ctx->count[0] | (uint64_t)ctx->count[1] << 32;
t = 64 + 56 - ctx->count[0] % 64;
if (t > 64)
t -= 64;
len <<= 3;
le64enc(tmp + t, len);
MD5Update(ctx, tmp, t + 8);
assert(ctx->count[0] % MD5_BLOCK_LENGTH == 0);
}
void
MD5Final(unsigned char md[16], MD5_CTX *ctx)
{
MD5Pad(ctx);
le32enc(md + 0, ctx->state[0]);
le32enc(md + 4, ctx->state[1]);
le32enc(md + 8, ctx->state[2]);
le32enc(md + 12, ctx->state[3]);
explicit_bzero(ctx, sizeof(ctx));
}
#ifndef MD5_ASM
static const uint32_t K[64] = {
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
};
static inline uint32_t
rol32(uint32_t a, int b)
{
return (a << b | a >> (32 - b));
}
static void
md5block(MD5_CTX *ctx, const void *data, size_t len)
{
uint32_t m[16], a0, b0, c0, d0;
const char *p = data;
a0 = ctx->state[0];
b0 = ctx->state[1];
c0 = ctx->state[2];
d0 = ctx->state[3];
while (len >= MD5_BLOCK_LENGTH) {
size_t i;
uint32_t a = a0, b = b0, c = c0, d = d0, f, tmp;
UNROLL
for (i = 0; i < 16; i++)
m[i] = le32dec(p + 4*i);
UNROLL
for (i = 0; i < 16; i += 4) {
f = d ^ (b & (c ^ d));
tmp = d;
d = c;
c = b;
b += rol32(a + f + K[i] + m[i], 7);
a = tmp;
f = d ^ (b & (c ^ d));
tmp = d;
d = c;
c = b;
b += rol32(a + f + K[i + 1] + m[i + 1], 12);
a = tmp;
f = d ^ (b & (c ^ d));
tmp = d;
d = c;
c = b;
b += rol32(a + f + K[i + 2] + m[i + 2], 17);
a = tmp;
f = d ^ (b & (c ^ d));
tmp = d;
d = c;
c = b;
b += rol32(a + f + K[i + 3] + m[i + 3], 22);
a = tmp;
}
UNROLL
for (; i < 32; i += 4) {
f = c ^ (d & (b ^ c));
tmp = d;
d = c;
c = b;
b += rol32(a + f + K[i] + m[(5*i + 1) % 16], 5);
a = tmp;
f = c ^ (d & (b ^ c));
tmp = d;
d = c;
c = b;
b += rol32(a + f + K[i + 1] + m[(5*i + 6) % 16], 9);
a = tmp;
f = c ^ (d & (b ^ c));
tmp = d;
d = c;
c = b;
b += rol32(a + f + K[i + 2] + m[(5*i + 11) % 16], 14);
a = tmp;
f = c ^ (d & (b ^ c));
tmp = d;
d = c;
c = b;
b += rol32(a + f + K[i + 3] + m[5*i % 16], 20);
a = tmp;
}
UNROLL
for (; i < 48; i += 4) {
f = b ^ c ^ d;
tmp = d;
d = c;
c = b;
b += rol32(a + f + K[i] + m[(3*i + 5) % 16], 4);
a = tmp;
f = b ^ c ^ d;
tmp = d;
d = c;
c = b;
b += rol32(a + f + K[i + 1] + m[(3*i + 8) % 16], 11);
a = tmp;
f = b ^ c ^ d;
tmp = d;
d = c;
c = b;
b += rol32(a + f + K[i + 2] + m[(3*i + 11) % 16], 16);
a = tmp;
f = b ^ c ^ d;
tmp = d;
d = c;
c = b;
b += rol32(a + f + K[i + 3] + m[(3*i + 14) % 16], 23);
a = tmp;
}
UNROLL
for (; i < 64; i += 4) {
f = c ^ (b | ~d);
tmp = d;
d = c;
c = b;
b += rol32(a + f + K[i] + m[7*i % 16], 6);
a = tmp;
f = c ^ (b | ~d);
tmp = d;
d = c;
c = b;
b += rol32(a + f + K[i + 1] + m[(7*i + 7) % 16], 10);
a = tmp;
f = c ^ (b | ~d);
tmp = d;
d = c;
c = b;
b += rol32(a + f + K[i + 2] + m[(7*i + 14) % 16], 15);
a = tmp;
f = c ^ (b | ~d);
tmp = d;
d = c;
c = b;
b += rol32(a + f + K[i + 3] + m[(7*i + 5) % 16], 21);
a = tmp;
}
a0 += a;
b0 += b;
c0 += c;
d0 += d;
p += MD5_BLOCK_LENGTH;
len -= MD5_BLOCK_LENGTH;
}
ctx->state[0] = a0;
ctx->state[1] = b0;
ctx->state[2] = c0;
ctx->state[3] = d0;
}
#endif
#ifdef WEAK_REFS
#undef MD5Init
__weak_reference(_libmd_MD5Init, MD5Init);
#undef MD5Update
__weak_reference(_libmd_MD5Update, MD5Update);
#undef MD5Final
__weak_reference(_libmd_MD5Final, MD5Final);
#endif