xref: /netbsd-src/lib/libcrypt/md5crypt.c (revision 4d92dbe90eae91beded70db613c2e6a1fd6dd84a)
1*4d92dbe9Sriastradh /*	$NetBSD: md5crypt.c,v 1.16 2024/07/23 22:37:11 riastradh Exp $	*/
249de2aa1Sad 
349de2aa1Sad /*
449de2aa1Sad  * ----------------------------------------------------------------------------
549de2aa1Sad  * "THE BEER-WARE LICENSE" (Revision 42):
649de2aa1Sad  * <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
749de2aa1Sad  * can do whatever you want with this stuff. If we meet some day, and you think
849de2aa1Sad  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
949de2aa1Sad  * ----------------------------------------------------------------------------
1049de2aa1Sad  *
1149de2aa1Sad  * from FreeBSD: crypt.c,v 1.5 1996/10/14 08:34:02 phk Exp
1249de2aa1Sad  * via OpenBSD: md5crypt.c,v 1.9 1997/07/23 20:58:27 kstailey Exp
1349de2aa1Sad  *
1449de2aa1Sad  */
1549de2aa1Sad 
1649de2aa1Sad #include <sys/cdefs.h>
1749de2aa1Sad #if !defined(lint)
18*4d92dbe9Sriastradh __RCSID("$NetBSD: md5crypt.c,v 1.16 2024/07/23 22:37:11 riastradh Exp $");
1949de2aa1Sad #endif /* not lint */
2049de2aa1Sad 
2149de2aa1Sad #include <unistd.h>
2249de2aa1Sad #include <stdio.h>
2349de2aa1Sad #include <string.h>
2449de2aa1Sad #include <md5.h>
2549de2aa1Sad 
263a0c68edSsjg #include "crypt.h"
273a0c68edSsjg 
2849de2aa1Sad #define MD5_MAGIC	"$1$"
2949de2aa1Sad #define MD5_MAGIC_LEN	3
3049de2aa1Sad 
31d1bb0be3Sthorpej #define	INIT(x)			MD5Init((x))
32d1bb0be3Sthorpej #define	UPDATE(x, b, l)		MD5Update((x), (b), (l))
33d1bb0be3Sthorpej #define	FINAL(v, x)		MD5Final((v), (x))
3449de2aa1Sad 
3549de2aa1Sad 
3649de2aa1Sad /*
3749de2aa1Sad  * MD5 password encryption.
3849de2aa1Sad  */
39f9151ba9Snia crypt_private char *
4049de2aa1Sad __md5crypt(const char *pw, const char *salt)
4149de2aa1Sad {
4249de2aa1Sad 	static char passwd[120], *p;
4349de2aa1Sad 	const char *sp, *ep;
4449de2aa1Sad 	unsigned char final[16];
4549de2aa1Sad 	unsigned int i, sl, pwl;
4649de2aa1Sad 	MD5_CTX	ctx, ctx1;
4749de2aa1Sad 	u_int32_t l;
4849de2aa1Sad 	int pl;
4949de2aa1Sad 
5049de2aa1Sad 	pwl = strlen(pw);
5149de2aa1Sad 
5249de2aa1Sad 	/* Refine the salt first */
5349de2aa1Sad 	sp = salt;
5449de2aa1Sad 
5549de2aa1Sad 	/* If it starts with the magic string, then skip that */
5649de2aa1Sad 	if (strncmp(sp, MD5_MAGIC, MD5_MAGIC_LEN) == 0)
5749de2aa1Sad 		sp += MD5_MAGIC_LEN;
5849de2aa1Sad 
5949de2aa1Sad 	/* It stops at the first '$', max 8 chars */
6049de2aa1Sad 	for (ep = sp; *ep != '\0' && *ep != '$' && ep < (sp + 8); ep++)
6149de2aa1Sad 		continue;
6249de2aa1Sad 
6349de2aa1Sad 	/* get the length of the true salt */
6449de2aa1Sad 	sl = ep - sp;
6549de2aa1Sad 
66d1bb0be3Sthorpej 	INIT(&ctx);
6749de2aa1Sad 
6849de2aa1Sad 	/* The password first, since that is what is most unknown */
69d1bb0be3Sthorpej 	UPDATE(&ctx, (const unsigned char *)pw, pwl);
7049de2aa1Sad 
7149de2aa1Sad 	/* Then our magic string */
72d1bb0be3Sthorpej 	UPDATE(&ctx, (const unsigned char *)MD5_MAGIC, MD5_MAGIC_LEN);
7349de2aa1Sad 
7449de2aa1Sad 	/* Then the raw salt */
75d1bb0be3Sthorpej 	UPDATE(&ctx, (const unsigned char *)sp, sl);
7649de2aa1Sad 
7749de2aa1Sad 	/* Then just as many characters of the MD5(pw,salt,pw) */
78d1bb0be3Sthorpej 	INIT(&ctx1);
79d1bb0be3Sthorpej 	UPDATE(&ctx1, (const unsigned char *)pw, pwl);
80d1bb0be3Sthorpej 	UPDATE(&ctx1, (const unsigned char *)sp, sl);
81d1bb0be3Sthorpej 	UPDATE(&ctx1, (const unsigned char *)pw, pwl);
82d1bb0be3Sthorpej 	FINAL(final, &ctx1);
8349de2aa1Sad 
8449de2aa1Sad 	for (pl = pwl; pl > 0; pl -= 16)
85d1bb0be3Sthorpej 		UPDATE(&ctx, final, (unsigned int)(pl > 16 ? 16 : pl));
8649de2aa1Sad 
8749de2aa1Sad 	/* Don't leave anything around in vm they could use. */
8849de2aa1Sad 	memset(final, 0, sizeof(final));
8949de2aa1Sad 
9049de2aa1Sad 	/* Then something really weird... */
9149de2aa1Sad 	for (i = pwl; i != 0; i >>= 1)
9249de2aa1Sad 		if ((i & 1) != 0)
93d1bb0be3Sthorpej 		    UPDATE(&ctx, final, 1);
9449de2aa1Sad 		else
95d1bb0be3Sthorpej 		    UPDATE(&ctx, (const unsigned char *)pw, 1);
9649de2aa1Sad 
9749de2aa1Sad 	/* Now make the output string */
9849de2aa1Sad 	memcpy(passwd, MD5_MAGIC, MD5_MAGIC_LEN);
99e5afda7bSad 	strlcpy(passwd + MD5_MAGIC_LEN, sp, sl + 1);
100fcfc71fcSitojun 	strlcat(passwd, "$", sizeof(passwd));
10149de2aa1Sad 
102d1bb0be3Sthorpej 	FINAL(final, &ctx);
10349de2aa1Sad 
1047babedb7Sdrochner 	/* memset(&ctx, 0, sizeof(ctx)); done by MD5Final() */
105487c0196Sdrochner 
10649de2aa1Sad 	/*
10749de2aa1Sad 	 * And now, just to make sure things don't run too fast. On a 60 MHz
10849de2aa1Sad 	 * Pentium this takes 34 msec, so you would need 30 seconds to build
10949de2aa1Sad 	 * a 1000 entry dictionary...
11049de2aa1Sad 	 */
11149de2aa1Sad 	for (i = 0; i < 1000; i++) {
112d1bb0be3Sthorpej 		INIT(&ctx1);
11349de2aa1Sad 
11449de2aa1Sad 		if ((i & 1) != 0)
115d1bb0be3Sthorpej 			UPDATE(&ctx1, (const unsigned char *)pw, pwl);
11649de2aa1Sad 		else
117d1bb0be3Sthorpej 			UPDATE(&ctx1, final, 16);
11849de2aa1Sad 
11949de2aa1Sad 		if ((i % 3) != 0)
120d1bb0be3Sthorpej 			UPDATE(&ctx1, (const unsigned char *)sp, sl);
12149de2aa1Sad 
12249de2aa1Sad 		if ((i % 7) != 0)
123d1bb0be3Sthorpej 			UPDATE(&ctx1, (const unsigned char *)pw, pwl);
12449de2aa1Sad 
12549de2aa1Sad 		if ((i & 1) != 0)
126d1bb0be3Sthorpej 			UPDATE(&ctx1, final, 16);
12749de2aa1Sad 		else
128d1bb0be3Sthorpej 			UPDATE(&ctx1, (const unsigned char *)pw, pwl);
12949de2aa1Sad 
130d1bb0be3Sthorpej 		FINAL(final, &ctx1);
13149de2aa1Sad 	}
13249de2aa1Sad 
1337babedb7Sdrochner 	/* memset(&ctx1, 0, sizeof(ctx1)); done by MD5Final() */
134487c0196Sdrochner 
13549de2aa1Sad 	p = passwd + sl + MD5_MAGIC_LEN + 1;
13649de2aa1Sad 
1373a0c68edSsjg 	l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; __crypt_to64(p,l,4); p += 4;
1383a0c68edSsjg 	l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; __crypt_to64(p,l,4); p += 4;
1393a0c68edSsjg 	l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; __crypt_to64(p,l,4); p += 4;
1403a0c68edSsjg 	l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; __crypt_to64(p,l,4); p += 4;
1413a0c68edSsjg 	l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; __crypt_to64(p,l,4); p += 4;
1423a0c68edSsjg 	l =		       final[11]		; __crypt_to64(p,l,2); p += 2;
14349de2aa1Sad 	*p = '\0';
14449de2aa1Sad 
14549de2aa1Sad 	/* Don't leave anything around in vm they could use. */
1461239c2bbSriastradh 	explicit_memset(final, 0, sizeof(final));
14749de2aa1Sad 	return (passwd);
14849de2aa1Sad }
149