From 792b6373efffdd3ebfb8dd9a02fcdeff9c742024 Mon Sep 17 00:00:00 2001
From: Andreas Lange <andreas@lange.cx>
Date: Mon, 30 Apr 2001 02:33:42 +0200
Subject: [PATCH] Added crypt_md5()

Rev: src/modules/_Crypto/crypto.c:1.45
Rev: src/modules/_Crypto/crypto.h:1.7
Rev: src/modules/_Crypto/lib/Makefile.in:1.29
Rev: src/modules/_Crypto/lib/crypt_md5.c:1.1
Rev: src/modules/_Crypto/testsuite.in:1.17
---
 .gitattributes                      |   1 +
 src/modules/_Crypto/crypto.c        |  54 +++++++++-
 src/modules/_Crypto/crypto.h        |   4 +-
 src/modules/_Crypto/lib/Makefile.in |   4 +-
 src/modules/_Crypto/lib/crypt_md5.c | 152 ++++++++++++++++++++++++++++
 src/modules/_Crypto/testsuite.in    |   6 ++
 6 files changed, 216 insertions(+), 5 deletions(-)
 create mode 100644 src/modules/_Crypto/lib/crypt_md5.c

diff --git a/.gitattributes b/.gitattributes
index e2ffa3bff3..9224117406 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -461,6 +461,7 @@ testfont binary
 /src/modules/_Crypto/lib/cast.c foreign_ident
 /src/modules/_Crypto/lib/cast_sboxes.h foreign_ident
 /src/modules/_Crypto/lib/configure.in foreign_ident
+/src/modules/_Crypto/lib/crypt_md5.c foreign_ident
 /src/modules/_Crypto/lib/desCode.h foreign_ident
 /src/modules/_Crypto/lib/desKerb.c foreign_ident
 /src/modules/_Crypto/lib/desQuick.c foreign_ident
diff --git a/src/modules/_Crypto/crypto.c b/src/modules/_Crypto/crypto.c
index afcc059885..eea6712b53 100644
--- a/src/modules/_Crypto/crypto.c
+++ b/src/modules/_Crypto/crypto.c
@@ -1,5 +1,5 @@
 /*
- * $Id: crypto.c,v 1.44 2001/03/28 15:07:41 grubba Exp $
+ * $Id: crypto.c,v 1.45 2001/04/30 00:33:41 lange Exp $
  *
  * A pike module for getting access to some common cryptos.
  *
@@ -246,6 +246,55 @@ static void f_des_parity(INT32 args)
   push_string(end_shared_string(s));
 }
 
+/*! @decl string crypt_md5(string password)
+ *! @decl string crypt_md5(string password, string salt)
+ *!
+ *! This function crypts a password with an algorithm using MD5 hashing.
+ *! 
+ *! If @[salt] is left out, an 8 character long salt (max length) will 
+ *! be randomized.
+ *!
+ *! Verification can be done by supplying the crypted password as @[salt]:
+ *! @code{crypt_md5(typed_pw, crypted_pw) == crypted_pw}
+ *! 
+ *! @seealso
+ *!   @[crypt()]
+ */
+static void f_crypt_md5(INT32 args)
+{
+  char salt[8];
+  char *ret, *saltp ="";
+  char *choice =
+    "cbhisjKlm4k65p7qrJfLMNQOPxwzyAaBDFgnoWXYCZ0123tvdHueEGISRTUV89./";
+ 
+  if (args < 1)
+    SIMPLE_TOO_FEW_ARGS_ERROR("crypt_md5", 1);
+
+  if (Pike_sp[-args].type != T_STRING)
+    SIMPLE_BAD_ARG_ERROR("crypt_md5", 1, "string");
+
+  if (args > 1)
+  {
+    if (Pike_sp[1-args].type != T_STRING)
+      SIMPLE_BAD_ARG_ERROR("crypt_md5", 2, "string");
+
+    saltp = Pike_sp[1-args].u.string->str;
+  } else {
+    unsigned int i, r;
+    for (i = 0; i < sizeof(salt); i++) 
+    {
+      r = my_rand();
+      salt[i] = choice[r % (size_t) strlen(choice)];
+    }
+    saltp = salt;
+  }
+
+  ret = (char *)crypt_md5(Pike_sp[-args].u.string->str, saltp);
+
+  pop_n_elems(args);
+  push_string(make_shared_string(ret));
+}
+
 /*
  * Crypto
  */
@@ -585,6 +634,9 @@ void pike_module_init(void)
   ADD_FUNCTION("hex_to_string", f_hex_to_string, tFunc(tStr, tStr), 0);
   /* function(string:string) */
   ADD_FUNCTION("des_parity", f_des_parity, tFunc(tStr, tStr), 0);
+  /* function(string:string)|function(string,string:string) */
+  ADD_FUNCTION("crypt_md5", f_crypt_md5,
+	       tOr(tFunc(tStr,tStr), tFunc(tStr tStr,tStr)), 0);
 
   pike_md2_init();
   pike_md5_init();
diff --git a/src/modules/_Crypto/crypto.h b/src/modules/_Crypto/crypto.h
index 497a669471..2fa621b802 100644
--- a/src/modules/_Crypto/crypto.h
+++ b/src/modules/_Crypto/crypto.h
@@ -1,5 +1,5 @@
 /*
- * $Id: crypto.h,v 1.6 2000/10/02 19:35:02 grubba Exp $
+ * $Id: crypto.h,v 1.7 2001/04/30 00:33:41 lange Exp $
  *
  * Prototypes for some functions.
  *
@@ -34,4 +34,4 @@ extern void pike_rsa_init(void);
 extern void pike_rsa_exit(void);
 extern void pike_pipe_init(void);
 extern void pike_pipe_exit(void);
-
+extern char *crypt_md5(const char *pw, const char *salt);
diff --git a/src/modules/_Crypto/lib/Makefile.in b/src/modules/_Crypto/lib/Makefile.in
index 47702d61f6..8f8b7c503b 100644
--- a/src/modules/_Crypto/lib/Makefile.in
+++ b/src/modules/_Crypto/lib/Makefile.in
@@ -1,4 +1,4 @@
-# $Id: Makefile.in,v 1.28 2001/01/19 20:24:12 grubba Exp $
+# $Id: Makefile.in,v 1.29 2001/04/30 00:33:41 lange Exp $
 #
 # Makefile for low-level crypto library
 
@@ -160,7 +160,7 @@ desQuickCore.c:
 MASS_DESTRUCTION_OBJS = idea.o arcfour.o cast.o rijndael.o $(O)
 # END NATIONAL SECURITY
 
-OBJS = $(MASS_DESTRUCTION_OBJS) sha.o md2.o md5.o
+OBJS = $(MASS_DESTRUCTION_OBJS) sha.o md2.o md5.o crypt_md5.o
 
 rijndael.o: $(SRCDIR)/../include/rijndael.h rijndael-boxes.dat
 
diff --git a/src/modules/_Crypto/lib/crypt_md5.c b/src/modules/_Crypto/lib/crypt_md5.c
new file mode 100644
index 0000000000..d9ae7cd052
--- /dev/null
+++ b/src/modules/_Crypto/lib/crypt_md5.c
@@ -0,0 +1,152 @@
+/*
+ * $Id: crypt_md5.c,v 1.1 2001/04/30 00:33:42 lange Exp $
+ *
+ *  crypt-md5.c :  Implementation of the MD5 password hash function
+ *
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * Adapted to Pike by Andreas Lange
+ *
+ */
+
+#include <string.h>
+#include "crypto_types.h"
+#include "md5.h"
+
+
+static unsigned char itoa64[] =		/* 0 ... 63 => ascii - 64 */
+	"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+static void to64(char *s, unsigned long v, int n)
+{
+	while (--n >= 0) {
+		*s++ = itoa64[v & 0x3f];
+		v >>= 6;
+	}
+}
+
+
+char *crypt_md5(const char *pw, const char *salt)
+{
+	static char *magic = "$1$"; /*
+				     * This string is magic for
+				     * this algorithm.  Having
+				     * it this way, we can get
+				     * better later on
+				     */
+	static char passwd[120], *p;
+	static const char *sp, *ep;
+	unsigned char final[MD5_DIGESTSIZE];
+	int sl, pl, i;
+	struct md5_ctx ctx, ctx1;
+	unsigned long l;
+
+	/* Refine the Salt first */
+	sp = salt;
+
+	/* If it starts with the magic string, then skip that */
+	if (!strncmp(sp, magic, strlen(magic)))
+		sp += strlen(magic);
+
+	/* It stops at the first '$', max 8 chars */
+	for (ep = sp; *ep && *ep != '$' && ep < (sp + 8); ep++)
+		continue;
+
+	/* get the length of the true salt */
+	sl = ep - sp;
+
+	md5_init(&ctx);
+
+	/* The password first, since that is what is most unknown */
+	md5_update(&ctx, (unsigned INT8 *) pw, strlen(pw));
+
+	/* Then our magic string */
+	md5_update(&ctx, (unsigned INT8 *) magic, strlen(magic));
+
+	/* Then the raw salt */
+	md5_update(&ctx, (unsigned INT8 *) sp, sl);
+
+	/* Then just as many characters of the MD5(pw,salt,pw) */
+	md5_init(&ctx1);
+	md5_update(&ctx1, (unsigned INT8 *) pw, strlen(pw));
+	md5_update(&ctx1, (unsigned INT8 *) sp, sl);
+	md5_update(&ctx1, (unsigned INT8 *) pw, strlen(pw));
+	md5_final(&ctx1);
+	md5_digest(&ctx1, final);
+	for(pl = strlen(pw); pl > 0; pl -= MD5_DIGESTSIZE)
+	  md5_update(&ctx, (unsigned INT8 *) final, 
+		     pl>MD5_DIGESTSIZE ? MD5_DIGESTSIZE : pl);
+
+	/* Don't leave anything around in vm they could use. */
+	memset(final, 0, sizeof(final) );
+
+	/* Then something really weird... */
+	for (i = strlen(pw); i ; i >>= 1)
+		if(i&1)
+		    md5_update(&ctx, (unsigned INT8 *) final, 1);
+		else
+		    md5_update(&ctx, (unsigned INT8 *) pw, 1);
+
+	/* Now make the output string */
+	strcpy(passwd, magic);
+	strncat(passwd, sp, sl);
+	strcat(passwd, "$");
+
+	md5_final(&ctx);
+	md5_digest(&ctx, final);
+
+        /* And now, just to make sure things don't run too fast... */
+	for(i=0;i<1000;i++) {
+		md5_init(&ctx1);
+		if(i & 1)
+		  md5_update(&ctx1, (unsigned INT8 *) pw, strlen(pw));
+		else
+		  md5_update(&ctx1, (unsigned INT8 *) final, MD5_DIGESTSIZE);
+
+		if(i % 3)
+		  md5_update(&ctx1, (unsigned INT8 *) sp, sl);
+
+		if(i % 7)
+		  md5_update(&ctx1, (unsigned INT8 *) pw, strlen(pw));
+
+		if(i & 1)
+		  md5_update(&ctx1, (unsigned INT8 *) final, MD5_DIGESTSIZE);
+		else
+		  md5_update(&ctx1, (unsigned INT8 *) pw, strlen(pw));
+
+		md5_final(&ctx1);
+		md5_digest(&ctx1, final);
+	}
+
+	p = passwd + strlen(passwd);
+
+	l = (final[0] << 16) | (final[6] << 8) | final[12];
+	to64(p, l, 4);
+	p += 4;
+	l = (final[1] << 16) | (final[7] << 8) | final[13];
+	to64(p, l, 4);
+	p += 4;
+	l = (final[2] << 16) | (final[8] << 8) | final[14];
+	to64(p, l, 4);
+	p += 4;
+	l = (final[3] << 16) | (final[9] << 8) | final[15];
+	to64(p, l, 4);
+	p += 4;
+	l = (final[4] << 16) | (final[10] << 8) | final[5];
+	to64(p, l, 4);
+	p += 4;
+	l = final[11];
+	to64(p, l, 2);
+	p += 2;
+	*p = '\0';
+
+	/* Don't leave anything around in vm they could use. */
+	memset(final, 0, sizeof(final) );
+
+	return passwd;
+}
diff --git a/src/modules/_Crypto/testsuite.in b/src/modules/_Crypto/testsuite.in
index 2d57660128..e4efeb9836 100644
--- a/src/modules/_Crypto/testsuite.in
+++ b/src/modules/_Crypto/testsuite.in
@@ -251,3 +251,9 @@ test_eq([[Crypto.hmac(xCrypto.sha)("Jefe")("what do ya want for nothing?")]],
 	[[xCrypto.hex_to_string("effcdf6ae5eb2fa2d27416d5f184df9c259a7c79")]])
 test_eq([[Crypto.hmac(xCrypto.sha)("\252" * 16)("\335" * 50)]],
 	[[xCrypto.hex_to_string("d730594d167e35d5956fd8003d0db3d3f46dc7bb")]])
+
+// CRYPT_MD5
+test_eq([[xCrypto.crypt_md5("Hello","sl5hO7j4")]],
+	[["$1$sl5hO7j4$glLmY.ttmi1hWK8ucIrig."]])
+test_eq([[xCrypto.crypt_md5("Hello","$1$sl5hO7j4$glLmY.ttmi1hWK8ucIrig.")]],
+	[["$1$sl5hO7j4$glLmY.ttmi1hWK8ucIrig."]])
-- 
GitLab