Forráskód Böngészése

Add internal md5 and sha1 message digests

Nils Maier 12 éve
szülő
commit
38bdea4e06
9 módosított fájl, 679 hozzáadás és 1 törlés
  1. 5 1
      configure.ac
  2. 110 0
      src/InternalMessageDigestImpl.cc
  3. 4 0
      src/Makefile.am
  4. 311 0
      src/md5.c
  5. 47 0
      src/md5.h
  6. 160 0
      src/sha1.c
  7. 28 0
      src/sha1.h
  8. 6 0
      test/HttpResponseTest.cc
  9. 8 0
      test/MessageDigestTest.cc

+ 5 - 1
configure.ac

@@ -453,6 +453,9 @@ else
         if test "x$have_openssl" = "xyes"; then
           AC_DEFINE([USE_OPENSSL_MD], [1], [What message digest implementation to use])
           use_md="openssl"
+        else
+          AC_DEFINE([USE_INTERNAL_MD], [1], [What message digest implementation to use])
+          use_md="internal"
         fi
       fi
     fi
@@ -481,6 +484,7 @@ AM_CONDITIONAL([HAVE_LIBGCRYPT], [ test "x$have_libgcrypt" = "xyes" ])
 AM_CONDITIONAL([USE_LIBGCRYPT_MD], [ test "x$use_md" = "xlibgcrypt"])
 AM_CONDITIONAL([HAVE_OPENSSL], [ test "x$have_openssl" = "xyes" ])
 AM_CONDITIONAL([USE_OPENSSL_MD], [ test "x$use_md" = "xopenssl"])
+AM_CONDITIONAL([USE_INTERNAL_MD], [ test "x$use_md" = "xinternal"])
 
 if test "x$use_md" != "x"; then
   AC_DEFINE([ENABLE_MESSAGE_DIGEST], [1],
@@ -969,7 +973,7 @@ echo "Epoll:          $have_epoll"
 echo "Bittorrent:     $enable_bittorrent"
 echo "Metalink:       $enable_metalink"
 echo "XML-RPC:        $enable_xml_rpc"
-echo "Message Digest: $enable_message_digest"
+echo "Message Digest: $use_md"
 echo "WebSocket:      $enable_websocket"
 echo "Libaria2:       $enable_libaria2"
 if test "x$enable_libaria2" = "xyes"; then

+ 110 - 0
src/InternalMessageDigestImpl.cc

@@ -0,0 +1,110 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2013 Nils Maier
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+
+#include "MessageDigestImpl.h"
+
+#include "md5.h"
+#include "sha1.h"
+
+namespace aria2 {
+namespace {
+
+template<size_t dlen,
+         typename ctx_t,
+         int (*init_fn)(ctx_t**),
+         void (*update_fn)(ctx_t*, const void*, size_t),
+         void (*final_fn)(ctx_t*, uint8_t*),
+         void (*free_fn)(ctx_t**)>
+class MessageDigestBase : public MessageDigestImpl {
+public:
+  MessageDigestBase() { reset(); }
+  virtual ~MessageDigestBase() {
+    free_fn(&ctx_);
+  }
+
+  static  size_t length() {
+    return dlen;
+  }
+  virtual size_t getDigestLength() const CXX11_OVERRIDE {
+    return dlen;
+  }
+  virtual void reset() CXX11_OVERRIDE {
+    printf("hash-%d\n", dlen);
+    init_fn(&ctx_);
+  }
+  virtual void update(const void* data, size_t length) CXX11_OVERRIDE {
+    auto bytes = reinterpret_cast<const char*>(data);
+    while (length) {
+      size_t l = std::min(length, (size_t)std::numeric_limits<uint32_t>::max());
+      update_fn(ctx_, bytes, l);
+      length -= l;
+      bytes += l;
+    }
+  }
+  virtual void digest(unsigned char* md) CXX11_OVERRIDE {
+    final_fn(ctx_, md);
+  }
+private:
+  ctx_t* ctx_;
+};
+
+typedef MessageDigestBase<MD5_LENGTH,
+                          struct MD5_CTX,
+                          MD5_Init,
+                          MD5_Update,
+                          MD5_Final,
+                          MD5_Free>
+MessageDigestMD5;
+typedef MessageDigestBase<SHA1_LENGTH,
+                          SHA1_CTX,
+                          SHA1_Init,
+                          SHA1_Update,
+                          SHA1_Final,
+                          SHA1_Free>
+MessageDigestSHA1;
+
+} // namespace
+
+std::unique_ptr<MessageDigestImpl> MessageDigestImpl::sha1()
+{
+  return std::unique_ptr<MessageDigestImpl>(new MessageDigestSHA1());
+}
+
+MessageDigestImpl::hashes_t MessageDigestImpl::hashes = {
+  { "sha-1", make_hi<MessageDigestSHA1>() },
+  { "md5", make_hi<MessageDigestMD5>() },
+};
+
+} // namespace aria2

+ 4 - 0
src/Makefile.am

@@ -333,6 +333,10 @@ if USE_WINDOWS_MD
 SRCS += WinMessageDigestImpl.cc
 endif # USE_WINDOWS_MD
 
+if USE_INTERNAL_MD
+SRCS += InternalMessageDigestImpl.cc sha1.c md5.c
+endif # USE_WINDOWS_MD
+
 if HAVE_LIBGNUTLS
 SRCS += LibgnutlsTLSContext.cc LibgnutlsTLSContext.h \
         LibgnutlsTLSSession.cc LibgnutlsTLSSession.h

+ 311 - 0
src/md5.c

@@ -0,0 +1,311 @@
+/*
+ * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
+ * MD5 Message-Digest Algorithm (RFC 1321).
+ *
+ * Homepage:
+ * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
+ *
+ * Author:
+ * Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
+ *
+ * This software was written by Alexander Peslyak in 2001.  No copyright is
+ * claimed, and the software is hereby placed in the public domain.
+ * In case this attempt to disclaim copyright and place the software in the
+ * public domain is deemed null and void, then the software is
+ * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * (This is a heavily cut-down "BSD license".)
+ *
+ * This differs from Colin Plumb's older public domain implementation in that
+ * no exactly 32-bit integer data type is required (any 32-bit or wider
+ * unsigned integer data type will do), there's no compile-time endianness
+ * configuration, and the function prototypes match OpenSSL's.  No code from
+ * Colin Plumb's implementation has been reused; this comment merely compares
+ * the properties of the two independent implementations.
+ *
+ * The primary goals of this implementation are portability and ease of use.
+ * It is meant to be fast, but not as fast as possible.  Some known
+ * optimizations are not included to reduce source code size and avoid
+ * compile-time configuration.
+ */
+
+#include "md5.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+struct MD5_CTX {
+  size_t lo, hi;
+  size_t a, b, c, d;
+  uint8_t buffer[64];
+  size_t block[16];
+};
+
+
+/*
+ * The basic MD5 functions.
+ *
+ * F and G are optimized compared to their RFC 1321 definitions for
+ * architectures that lack an AND-NOT instruction, just like in Colin Plumb's
+ * implementation.
+ */
+#define F(x, y, z)      ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z)      ((y) ^ ((z) & ((x) ^ (y))))
+#define H(x, y, z)      ((x) ^ (y) ^ (z))
+#define I(x, y, z)      ((y) ^ ((x) | ~(z)))
+
+/*
+ * The MD5 transformation for all four rounds.
+ */
+#define STEP(f, a, b, c, d, x, t, s) \
+  (a) += f((b), (c), (d)) + (x) + (t); \
+  (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
+  (a) += (b);
+
+/*
+ * SET reads 4 input bytes in little-endian byte order and stores them
+ * in a properly aligned word in host byte order.
+ *
+ * The check for little-endian architectures that tolerate unaligned
+ * memory accesses is just an optimization.  Nothing will break if it
+ * doesn't work.
+ */
+#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
+#define SET(n) \
+  (*(size_t *)&ptr[(n) * 4])
+#define GET(n) \
+  SET(n)
+#else
+#define SET(n) \
+  (ctx->block[(n)] = \
+  (size_t)ptr[(n) * 4] | \
+  ((size_t)ptr[(n) * 4 + 1] << 8) | \
+  ((size_t)ptr[(n) * 4 + 2] << 16) | \
+  ((size_t)ptr[(n) * 4 + 3] << 24))
+#define GET(n) \
+  (ctx->block[(n)])
+#endif
+
+/*
+ * This processes one or more 64-byte data blocks, but does NOT update
+ * the bit counters.  There are no alignment requirements.
+ */
+static const void *body(struct MD5_CTX *ctx, const void *data, size_t size)
+{
+  const uint8_t *ptr;
+  size_t a, b, c, d;
+  size_t saved_a, saved_b, saved_c, saved_d;
+
+  ptr = data;
+
+  a = ctx->a;
+  b = ctx->b;
+  c = ctx->c;
+  d = ctx->d;
+
+  do {
+    saved_a = a;
+    saved_b = b;
+    saved_c = c;
+    saved_d = d;
+
+/* Round 1 */
+    STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
+    STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
+    STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
+    STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
+    STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
+    STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
+    STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
+    STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
+    STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
+    STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
+    STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
+    STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
+    STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
+    STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
+    STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
+    STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
+
+/* Round 2 */
+    STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
+    STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
+    STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
+    STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
+    STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
+    STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
+    STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
+    STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
+    STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
+    STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
+    STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
+    STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
+    STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
+    STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
+    STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
+    STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
+
+/* Round 3 */
+    STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
+    STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)
+    STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
+    STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)
+    STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
+    STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)
+    STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
+    STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)
+    STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
+    STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)
+    STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
+    STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)
+    STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
+    STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)
+    STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
+    STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)
+
+/* Round 4 */
+    STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
+    STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
+    STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
+    STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
+    STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
+    STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
+    STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
+    STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
+    STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
+    STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
+    STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
+    STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
+    STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
+    STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
+    STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
+    STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
+
+    a += saved_a;
+    b += saved_b;
+    c += saved_c;
+    d += saved_d;
+
+    ptr += 64;
+  } while (size -= 64);
+
+  ctx->a = a;
+  ctx->b = b;
+  ctx->c = c;
+  ctx->d = d;
+
+  return ptr;
+}
+
+int MD5_Init(struct MD5_CTX **ctx)
+{
+  *ctx = malloc(sizeof(struct MD5_CTX));
+  if (!*ctx) {
+    return 0;
+  }
+  (*ctx)->a = 0x67452301;
+  (*ctx)->b = 0xefcdab89;
+  (*ctx)->c = 0x98badcfe;
+  (*ctx)->d = 0x10325476;
+
+  (*ctx)->lo = 0;
+  (*ctx)->hi = 0;
+  return 1;
+}
+
+void MD5_Update(struct MD5_CTX *ctx, const void *data, size_t size)
+{
+  size_t saved_lo;
+  size_t used, free;
+
+  saved_lo = ctx->lo;
+  if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
+    ctx->hi++;
+  ctx->hi += size >> 29;
+
+  used = saved_lo & 0x3f;
+
+  if (used) {
+    free = 64 - used;
+
+    if (size < free) {
+      memcpy(&ctx->buffer[used], data, size);
+      return;
+    }
+
+    memcpy(&ctx->buffer[used], data, free);
+    data = (unsigned char *)data + free;
+    size -= free;
+    body(ctx, ctx->buffer, 64);
+  }
+
+  if (size >= 64) {
+    data = body(ctx, data, size & ~(size_t)0x3f);
+    size &= 0x3f;
+  }
+
+  memcpy(ctx->buffer, data, size);
+}
+
+void MD5_Final(struct MD5_CTX *ctx, unsigned char* result)
+{
+  size_t used, free;
+
+  used = ctx->lo & 0x3f;
+
+  ctx->buffer[used++] = 0x80;
+
+  free = 64 - used;
+
+  if (free < 8) {
+    memset(&ctx->buffer[used], 0, free);
+    body(ctx, ctx->buffer, 64);
+    used = 0;
+    free = 64;
+  }
+
+  memset(&ctx->buffer[used], 0, free - 8);
+
+  ctx->lo <<= 3;
+  ctx->buffer[56] = ctx->lo;
+  ctx->buffer[57] = ctx->lo >> 8;
+  ctx->buffer[58] = ctx->lo >> 16;
+  ctx->buffer[59] = ctx->lo >> 24;
+  ctx->buffer[60] = ctx->hi;
+  ctx->buffer[61] = ctx->hi >> 8;
+  ctx->buffer[62] = ctx->hi >> 16;
+  ctx->buffer[63] = ctx->hi >> 24;
+
+  body(ctx, ctx->buffer, 64);
+
+  result[0] = ctx->a;
+  result[1] = ctx->a >> 8;
+  result[2] = ctx->a >> 16;
+  result[3] = ctx->a >> 24;
+  result[4] = ctx->b;
+  result[5] = ctx->b >> 8;
+  result[6] = ctx->b >> 16;
+  result[7] = ctx->b >> 24;
+  result[8] = ctx->c;
+  result[9] = ctx->c >> 8;
+  result[10] = ctx->c >> 16;
+  result[11] = ctx->c >> 24;
+  result[12] = ctx->d;
+  result[13] = ctx->d >> 8;
+  result[14] = ctx->d >> 16;
+  result[15] = ctx->d >> 24;
+}
+
+void MD5_Free(struct MD5_CTX** ctx) {
+  if (!ctx || !*ctx) {
+    return;
+  }
+  free(*ctx);
+  *ctx = NULL;
+}

+ 47 - 0
src/md5.h

@@ -0,0 +1,47 @@
+/*
+ * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
+ * MD5 Message-Digest Algorithm (RFC 1321).
+ *
+ * Homepage:
+ * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
+ *
+ * Author:
+ * Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
+ *
+ * This software was written by Alexander Peslyak in 2001.  No copyright is
+ * claimed, and the software is hereby placed in the public domain.
+ * In case this attempt to disclaim copyright and place the software in the
+ * public domain is deemed null and void, then the software is
+ * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * See md5.c for more information.
+ */
+
+#ifndef INTERNAL_MD5_H
+#define INTERNAL_MD5_H
+
+#include <sys/types.h>
+#include <stdint.h>
+
+struct MD5_CTX;
+
+#define MD5_LENGTH 16
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+int MD5_Init(struct MD5_CTX **ctx);
+void MD5_Update(struct MD5_CTX *ctx, const void *data, size_t size);
+void MD5_Final(struct MD5_CTX *ctx, uint8_t *result);
+void MD5_Free(struct MD5_CTX **ctx);
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* INTERNAL_MD5_H */

+ 160 - 0
src/sha1.c

@@ -0,0 +1,160 @@
+/* This code is public-domain - it is based on libcrypt 
+ * placed in the public domain by Wei Dai and other contributors.
+ */
+
+#include "sha1.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#define BLOCK_LENGTH 64
+
+union _buffer {
+  uint8_t b[BLOCK_LENGTH];
+  uint32_t w[BLOCK_LENGTH/4];
+};
+
+union _state {
+  uint8_t b[SHA1_LENGTH];
+  uint32_t w[SHA1_LENGTH/4];
+};
+
+struct SHA1_CTX {
+  union _buffer buffer;
+  uint8_t bufferOffset;
+  union _state state;
+  uint32_t byteCount;
+  uint8_t keyBuffer[BLOCK_LENGTH];
+  uint8_t innerHash[SHA1_LENGTH];
+};
+
+#define SHA1_K0 0x5a827999
+#define SHA1_K20 0x6ed9eba1
+#define SHA1_K40 0x8f1bbcdc
+#define SHA1_K60 0xca62c1d6
+
+const uint8_t sha1InitState[] = {
+  0x01,0x23,0x45,0x67, // H0
+  0x89,0xab,0xcd,0xef, // H1
+  0xfe,0xdc,0xba,0x98, // H2
+  0x76,0x54,0x32,0x10, // H3
+  0xf0,0xe1,0xd2,0xc3  // H4
+};
+
+static uint32_t sha1_rol32(uint32_t number, uint8_t bits) {
+  return ((number << bits) | (number >> (32-bits)));
+}
+
+static void sha1_hashBlock(struct SHA1_CTX *s) {
+  uint8_t i;
+  uint32_t a,b,c,d,e,t;
+
+  a=s->state.w[0];
+  b=s->state.w[1];
+  c=s->state.w[2];
+  d=s->state.w[3];
+  e=s->state.w[4];
+  for (i=0; i<80; i++) {
+    if (i>=16) {
+      t = s->buffer.w[(i+13)&15] ^ s->buffer.w[(i+8)&15] ^ s->buffer.w[(i+2)&15] ^ s->buffer.w[i&15];
+      s->buffer.w[i&15] = sha1_rol32(t,1);
+    }
+    if (i<20) {
+      t = (d ^ (b & (c ^ d))) + SHA1_K0;
+    } else if (i<40) {
+      t = (b ^ c ^ d) + SHA1_K20;
+    } else if (i<60) {
+      t = ((b & c) | (d & (b | c))) + SHA1_K40;
+    } else {
+      t = (b ^ c ^ d) + SHA1_K60;
+    }
+    t+=sha1_rol32(a,5) + e + s->buffer.w[i&15];
+    e=d;
+    d=c;
+    c=sha1_rol32(b,30);
+    b=a;
+    a=t;
+  }
+  s->state.w[0] += a;
+  s->state.w[1] += b;
+  s->state.w[2] += c;
+  s->state.w[3] += d;
+  s->state.w[4] += e;
+}
+
+static void sha1_addUncounted(struct SHA1_CTX *s, uint8_t data) {
+  s->buffer.b[s->bufferOffset ^ 3] = data;
+  s->bufferOffset++;
+  if (s->bufferOffset == BLOCK_LENGTH) {
+    sha1_hashBlock(s);
+    s->bufferOffset = 0;
+  }
+}
+
+static void sha1_writebyte(struct SHA1_CTX *s, uint8_t data) {
+  ++s->byteCount;
+  sha1_addUncounted(s, data);
+}
+
+static void sha1_pad(struct SHA1_CTX *s) {
+  // Implement SHA-1 padding (fips180-2 §5.1.1)
+
+  // Pad with 0x80 followed by 0x00 until the end of the block
+  sha1_addUncounted(s, 0x80);
+  while (s->bufferOffset != 56) sha1_addUncounted(s, 0x00);
+
+  // Append length in the last 8 bytes
+  sha1_addUncounted(s, 0); // We're only using 32 bit lengths
+  sha1_addUncounted(s, 0); // But SHA-1 supports 64 bit lengths
+  sha1_addUncounted(s, 0); // So zero pad the top bits
+  sha1_addUncounted(s, s->byteCount >> 29); // Shifting to multiply by 8
+  sha1_addUncounted(s, s->byteCount >> 21); // as SHA-1 supports bitstreams as well as
+  sha1_addUncounted(s, s->byteCount >> 13); // byte.
+  sha1_addUncounted(s, s->byteCount >> 5);
+  sha1_addUncounted(s, s->byteCount << 3);
+}
+
+int SHA1_Init(struct SHA1_CTX **s) {
+  *s = malloc(sizeof(struct SHA1_CTX));
+  if (!*s) {
+    return 0;
+  }
+  memcpy((*s)->state.b, sha1InitState, SHA1_LENGTH);
+  (*s)->byteCount = 0;
+  (*s)->bufferOffset = 0;
+  return 1;
+}
+
+void SHA1_Update(struct SHA1_CTX *s, const void *data, size_t len) {
+  const uint8_t *bytes = data;
+  for (;len--;) sha1_writebyte(s, *bytes++);
+}
+
+void SHA1_Final(struct SHA1_CTX *s, unsigned char* result) {
+  int i;
+  // Pad to complete the last block
+  sha1_pad(s);
+
+  // Swap byte order back
+  for (i=0; i<5; i++) {
+    uint32_t a,b;
+    a=s->state.w[i];
+    b=a<<24;
+    b|=(a<<8) & 0x00ff0000;
+    b|=(a>>8) & 0x0000ff00;
+    b|=a>>24;
+    s->state.w[i]=b;
+  }
+
+  // Return pointer to hash (20 characters)
+  memcpy(result, s->state.b, sizeof(s->state.b));
+}
+
+void SHA1_Free(struct SHA1_CTX** s) {
+  if (!s || !*s) {
+    return;
+  }
+  free(*s);
+  *s = NULL;
+}

+ 28 - 0
src/sha1.h

@@ -0,0 +1,28 @@
+/* This code is public-domain - it is based on libcrypt 
+ * placed in the public domain by Wei Dai and other contributors.
+ */
+
+#ifndef INTERNAL_SHA1_H
+#define INTERNAL_SHA1_H
+
+#include <sys/types.h>
+#include <stdint.h>
+
+#define SHA1_LENGTH 20
+
+struct SHA1_CTX;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int SHA1_Init(struct SHA1_CTX **s);
+void SHA1_Update(struct SHA1_CTX *s, const void *data, size_t len);
+void SHA1_Final(struct SHA1_CTX *s, uint8_t *result);
+void SHA1_Free(struct SHA1_CTX **s);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* INTERNAL_SHA1_H */

+ 6 - 0
test/HttpResponseTest.cc

@@ -654,14 +654,20 @@ void HttpResponseTest::testGetDigest()
      "MD5=LJDK2+9ClF8Nz/K5WZd/+A==");
   std::vector<Checksum> result;
   httpResponse.getDigest(result);
+#ifdef USE_INTERNAL_MD
+  CPPUNIT_ASSERT_EQUAL((size_t)2, result.size());
+#else
   CPPUNIT_ASSERT_EQUAL((size_t)3, result.size());
+#endif
 
   Checksum c = result[0];
+#ifndef USE_INTERNAL_MD
   CPPUNIT_ASSERT_EQUAL(std::string("sha-256"), c.getHashType());
   CPPUNIT_ASSERT_EQUAL(std::string("f83f271ae773dc6fe4a6454a41e0eb237c43e7bbf451e426cc60993a4d379ec5"),
                        util::toHex(c.getDigest()));
 
   c = result[1];
+#endif
   CPPUNIT_ASSERT_EQUAL(std::string("sha-1"), c.getHashType());
   CPPUNIT_ASSERT_EQUAL(std::string("f36003f22b462ffa184390533c500d8989e9f681"),
                        util::toHex(c.getDigest()));

+ 8 - 0
test/MessageDigestTest.cc

@@ -18,9 +18,11 @@ class MessageDigestTest:public CppUnit::TestFixture {
   CPPUNIT_TEST_SUITE_END();
 
   std::unique_ptr<MessageDigest> sha1_;
+  std::unique_ptr<MessageDigest> md5_;
 public:
   void setUp()
   {
+    md5_ = MessageDigest::create("md5");
     sha1_ = MessageDigest::sha1();
   }
 
@@ -37,6 +39,10 @@ CPPUNIT_TEST_SUITE_REGISTRATION( MessageDigestTest );
 
 void MessageDigestTest::testDigest()
 {
+  md5_->update("aria2", 5);
+  CPPUNIT_ASSERT_EQUAL(std::string("2c90cadbef42945f0dcff2b959977ff8"),
+                       util::toHex(md5_->digest()));
+
   sha1_->update("aria2", 5);
   CPPUNIT_ASSERT_EQUAL(std::string("f36003f22b462ffa184390533c500d8989e9f681"),
                        util::toHex(sha1_->digest()));
@@ -44,6 +50,7 @@ void MessageDigestTest::testDigest()
 
 void MessageDigestTest::testSupports()
 {
+  CPPUNIT_ASSERT(MessageDigest::supports("md5"));
   CPPUNIT_ASSERT(MessageDigest::supports("sha-1"));
   // Fails because sha1 is not valid name.
   CPPUNIT_ASSERT(!MessageDigest::supports("sha1"));
@@ -51,6 +58,7 @@ void MessageDigestTest::testSupports()
 
 void MessageDigestTest::testGetDigestLength()
 {
+  CPPUNIT_ASSERT_EQUAL((size_t)16, MessageDigest::getDigestLength("md5"));
   CPPUNIT_ASSERT_EQUAL((size_t)20, MessageDigest::getDigestLength("sha-1"));
   CPPUNIT_ASSERT_EQUAL((size_t)20, sha1_->getDigestLength());
 }