Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 1 | /* |
| 2 | * MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm |
| 3 | * |
| 4 | * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All |
| 5 | * rights reserved. |
| 6 | * |
| 7 | * License to copy and use this software is granted provided that it |
| 8 | * is identified as the "RSA Data Security, Inc. MD5 Message-Digest |
| 9 | * Algorithm" in all material mentioning or referencing this software |
| 10 | * or this function. |
| 11 | * |
| 12 | * License is also granted to make and use derivative works provided |
| 13 | * that such works are identified as "derived from the RSA Data |
| 14 | * Security, Inc. MD5 Message-Digest Algorithm" in all material |
| 15 | * mentioning or referencing the derived work. |
| 16 | * |
| 17 | * RSA Data Security, Inc. makes no representations concerning either |
| 18 | * the merchantability of this software or the suitability of this |
| 19 | * software for any particular purpose. It is provided "as is" |
| 20 | * without express or implied warranty of any kind. |
| 21 | * |
| 22 | * These notices must be retained in any copies of any part of this |
| 23 | * documentation and/or software. |
| 24 | * |
| 25 | * $FreeBSD: src/lib/libmd/md5c.c,v 1.9.2.1 1999/08/29 14:57:12 peter Exp $ |
| 26 | * |
| 27 | * This code is the same as the code published by RSA Inc. It has been |
| 28 | * edited for clarity and style only. |
| 29 | * |
| 30 | * ---------------------------------------------------------------------------- |
| 31 | * The md5_crypt() function was taken from freeBSD's libcrypt and contains |
| 32 | * this license: |
| 33 | * "THE BEER-WARE LICENSE" (Revision 42): |
| 34 | * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you |
| 35 | * can do whatever you want with this stuff. If we meet some day, and you think |
| 36 | * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp |
| 37 | * |
| 38 | * $FreeBSD: src/lib/libcrypt/crypt.c,v 1.7.2.1 1999/08/29 14:56:33 peter Exp $ |
| 39 | * |
| 40 | * ---------------------------------------------------------------------------- |
| 41 | * On April 19th, 2001 md5_crypt() was modified to make it reentrant |
| 42 | * by Erik Andersen <andersen@uclibc.org> |
| 43 | * |
| 44 | * |
| 45 | * June 28, 2001 Manuel Novoa III |
| 46 | * |
| 47 | * "Un-inlined" code using loops and static const tables in order to |
| 48 | * reduce generated code size (on i386 from approx 4k to approx 2.5k). |
| 49 | * |
| 50 | * June 29, 2001 Manuel Novoa III |
| 51 | * |
| 52 | * Completely removed static PADDING array. |
| 53 | * |
| 54 | * Reintroduced the loop unrolling in MD5_Transform and added the |
| 55 | * MD5_SIZE_OVER_SPEED option for configurability. Define below as: |
| 56 | * 0 fully unrolled loops |
| 57 | * 1 partially unrolled (4 ops per loop) |
| 58 | * 2 no unrolling -- introduces the need to swap 4 variables (slow) |
| 59 | * 3 no unrolling and all 4 loops merged into one with switch |
| 60 | * in each loop (glacial) |
| 61 | * On i386, sizes are roughly (-Os -fno-builtin): |
| 62 | * 0: 3k 1: 2.5k 2: 2.2k 3: 2k |
| 63 | * |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 64 | * Since SuSv3 does not require crypt_r, modified again August 7, 2002 |
| 65 | * by Erik Andersen to remove reentrance stuff... |
| 66 | */ |
| 67 | |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 68 | #define MD5_MAGIC_STR "$1$" |
| 69 | #define MD5_MAGIC_LEN (sizeof(MD5_MAGIC_STR) - 1) |
| 70 | static const unsigned char __md5__magic[] = MD5_MAGIC_STR; |
| 71 | |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 72 | /* |
| 73 | * UNIX password |
| 74 | * |
| 75 | * Use MD5 for what it is best at... |
| 76 | */ |
Denis Vlasenko | 76f8128 | 2008-06-13 15:13:41 +0000 | [diff] [blame] | 77 | #define MD5_OUT_BUFSIZE 36 |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 78 | static char * |
Denis Vlasenko | e235285 | 2008-06-14 22:11:29 +0000 | [diff] [blame] | 79 | NOINLINE |
Denis Vlasenko | e9b9a19 | 2008-11-11 01:38:04 +0000 | [diff] [blame^] | 80 | md5_crypt(char result[MD5_OUT_BUFSIZE], const unsigned char *pw, const unsigned char *salt) |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 81 | { |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 82 | char *p; |
Denis Vlasenko | e9b9a19 | 2008-11-11 01:38:04 +0000 | [diff] [blame^] | 83 | unsigned char final[17]; /* final[16] exists only to aid in looping */ |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 84 | int sl, pl, i, pw_len; |
Denis Vlasenko | e9b9a19 | 2008-11-11 01:38:04 +0000 | [diff] [blame^] | 85 | md5_ctx_t ctx, ctx1; |
| 86 | |
| 87 | /* NB: in busybox, "$1$" in salt is always present */ |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 88 | |
| 89 | /* Refine the Salt first */ |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 90 | |
Denis Vlasenko | e9b9a19 | 2008-11-11 01:38:04 +0000 | [diff] [blame^] | 91 | /* Get the length of the salt including "$1$" */ |
| 92 | sl = 3; |
| 93 | while (salt[sl] && salt[sl] != '$' && sl < (3 + 8)) |
| 94 | sl++; |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 95 | |
Denis Vlasenko | e9b9a19 | 2008-11-11 01:38:04 +0000 | [diff] [blame^] | 96 | /* Hash. the password first, since that is what is most unknown */ |
| 97 | md5_begin(&ctx); |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 98 | pw_len = strlen((char*)pw); |
Denis Vlasenko | e9b9a19 | 2008-11-11 01:38:04 +0000 | [diff] [blame^] | 99 | md5_hash(pw, pw_len, &ctx); |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 100 | |
Denis Vlasenko | e9b9a19 | 2008-11-11 01:38:04 +0000 | [diff] [blame^] | 101 | /* Then the salt including "$1$" */ |
| 102 | md5_hash(salt, sl, &ctx); |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 103 | |
Denis Vlasenko | e9b9a19 | 2008-11-11 01:38:04 +0000 | [diff] [blame^] | 104 | /* Copy salt to result; skip "$1$" */ |
| 105 | memcpy(result, salt, sl); |
| 106 | result[sl] = '$'; |
| 107 | salt += 3; |
| 108 | sl -= 3; |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 109 | |
| 110 | /* Then just as many characters of the MD5(pw, salt, pw) */ |
Denis Vlasenko | e9b9a19 | 2008-11-11 01:38:04 +0000 | [diff] [blame^] | 111 | md5_begin(&ctx1); |
| 112 | md5_hash(pw, pw_len, &ctx1); |
| 113 | md5_hash(salt, sl, &ctx1); |
| 114 | md5_hash(pw, pw_len, &ctx1); |
| 115 | md5_end(final, &ctx1); |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 116 | for (pl = pw_len; pl > 0; pl -= 16) |
Denis Vlasenko | e9b9a19 | 2008-11-11 01:38:04 +0000 | [diff] [blame^] | 117 | md5_hash(final, pl > 16 ? 16 : pl, &ctx); |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 118 | |
| 119 | /* Then something really weird... */ |
Denis Vlasenko | e9b9a19 | 2008-11-11 01:38:04 +0000 | [diff] [blame^] | 120 | memset(final, 0, sizeof(final)); |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 121 | for (i = pw_len; i; i >>= 1) { |
Denis Vlasenko | e9b9a19 | 2008-11-11 01:38:04 +0000 | [diff] [blame^] | 122 | md5_hash(((i & 1) ? final : (const unsigned char *) pw), 1, &ctx); |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 123 | } |
Denis Vlasenko | e9b9a19 | 2008-11-11 01:38:04 +0000 | [diff] [blame^] | 124 | md5_end(final, &ctx); |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 125 | |
Denis Vlasenko | e9b9a19 | 2008-11-11 01:38:04 +0000 | [diff] [blame^] | 126 | /* And now, just to make sure things don't run too fast. |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 127 | * On a 60 Mhz Pentium this takes 34 msec, so you would |
| 128 | * need 30 seconds to build a 1000 entry dictionary... |
| 129 | */ |
| 130 | for (i = 0; i < 1000; i++) { |
Denis Vlasenko | e9b9a19 | 2008-11-11 01:38:04 +0000 | [diff] [blame^] | 131 | md5_begin(&ctx1); |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 132 | if (i & 1) |
Denis Vlasenko | e9b9a19 | 2008-11-11 01:38:04 +0000 | [diff] [blame^] | 133 | md5_hash(pw, pw_len, &ctx1); |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 134 | else |
Denis Vlasenko | e9b9a19 | 2008-11-11 01:38:04 +0000 | [diff] [blame^] | 135 | md5_hash(final, 16, &ctx1); |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 136 | |
| 137 | if (i % 3) |
Denis Vlasenko | e9b9a19 | 2008-11-11 01:38:04 +0000 | [diff] [blame^] | 138 | md5_hash(salt, sl, &ctx1); |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 139 | |
| 140 | if (i % 7) |
Denis Vlasenko | e9b9a19 | 2008-11-11 01:38:04 +0000 | [diff] [blame^] | 141 | md5_hash(pw, pw_len, &ctx1); |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 142 | |
| 143 | if (i & 1) |
Denis Vlasenko | e9b9a19 | 2008-11-11 01:38:04 +0000 | [diff] [blame^] | 144 | md5_hash(final, 16, &ctx1); |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 145 | else |
Denis Vlasenko | e9b9a19 | 2008-11-11 01:38:04 +0000 | [diff] [blame^] | 146 | md5_hash(pw, pw_len, &ctx1); |
| 147 | md5_end(final, &ctx1); |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 148 | } |
| 149 | |
Denis Vlasenko | e9b9a19 | 2008-11-11 01:38:04 +0000 | [diff] [blame^] | 150 | p = result + sl + 4; /* 12 bytes max (sl is up to 8 bytes) */ |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 151 | |
Denis Vlasenko | 76f8128 | 2008-06-13 15:13:41 +0000 | [diff] [blame] | 152 | /* Add 5*4+2 = 22 bytes of hash, + NUL byte. */ |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 153 | final[16] = final[5]; |
| 154 | for (i = 0; i < 5; i++) { |
Denis Vlasenko | e235285 | 2008-06-14 22:11:29 +0000 | [diff] [blame] | 155 | unsigned l = (final[i] << 16) | (final[i+6] << 8) | final[i+12]; |
Denis Vlasenko | 2211d52 | 2008-11-10 18:52:35 +0000 | [diff] [blame] | 156 | p = to64(p, l, 4); |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 157 | } |
Denis Vlasenko | 2211d52 | 2008-11-10 18:52:35 +0000 | [diff] [blame] | 158 | p = to64(p, final[11], 2); |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 159 | *p = '\0'; |
| 160 | |
| 161 | /* Don't leave anything around in vm they could use. */ |
| 162 | memset(final, 0, sizeof(final)); |
| 163 | |
Denis Vlasenko | e9b9a19 | 2008-11-11 01:38:04 +0000 | [diff] [blame^] | 164 | return result; |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 165 | } |
Denis Vlasenko | 4ea83bf | 2008-06-12 16:55:59 +0000 | [diff] [blame] | 166 | #undef MD5_MAGIC_STR |
| 167 | #undef MD5_MAGIC_LEN |