Header And Logo

PostgreSQL
| The world's most advanced open source database.

crypt-md5.c

Go to the documentation of this file.
00001 /*
00002  * File imported from FreeBSD, original by Poul-Henning Kamp.
00003  *
00004  * $FreeBSD: src/lib/libcrypt/crypt-md5.c,v 1.5 1999/12/17 20:21:45 peter Exp $
00005  *
00006  * contrib/pgcrypto/crypt-md5.c
00007  */
00008 
00009 #include "postgres.h"
00010 
00011 #include "px.h"
00012 #include "px-crypt.h"
00013 
00014 #define MD5_SIZE 16
00015 
00016 static const char _crypt_a64[] =
00017 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
00018 
00019 static void
00020 _crypt_to64(char *s, unsigned long v, int n)
00021 {
00022     while (--n >= 0)
00023     {
00024         *s++ = _crypt_a64[v & 0x3f];
00025         v >>= 6;
00026     }
00027 }
00028 
00029 /*
00030  * UNIX password
00031  */
00032 
00033 char *
00034 px_crypt_md5(const char *pw, const char *salt, char *passwd, unsigned dstlen)
00035 {
00036     static char *magic = "$1$"; /* This string is magic for this algorithm.
00037                                  * Having it this way, we can get better later
00038                                  * on */
00039     static char *p;
00040     static const char *sp,
00041                *ep;
00042     unsigned char final[MD5_SIZE];
00043     int         sl,
00044                 pl,
00045                 i;
00046     PX_MD      *ctx,
00047                *ctx1;
00048     int         err;
00049     unsigned long l;
00050 
00051     if (!passwd || dstlen < 120)
00052         return NULL;
00053 
00054     /* Refine the Salt first */
00055     sp = salt;
00056 
00057     /* If it starts with the magic string, then skip that */
00058     if (strncmp(sp, magic, strlen(magic)) == 0)
00059         sp += strlen(magic);
00060 
00061     /* It stops at the first '$', max 8 chars */
00062     for (ep = sp; *ep && *ep != '$' && ep < (sp + 8); ep++)
00063         continue;
00064 
00065     /* get the length of the true salt */
00066     sl = ep - sp;
00067 
00068     /* */
00069     err = px_find_digest("md5", &ctx);
00070     if (err)
00071         return NULL;
00072     err = px_find_digest("md5", &ctx1);
00073 
00074     /* The password first, since that is what is most unknown */
00075     px_md_update(ctx, (const uint8 *) pw, strlen(pw));
00076 
00077     /* Then our magic string */
00078     px_md_update(ctx, (uint8 *) magic, strlen(magic));
00079 
00080     /* Then the raw salt */
00081     px_md_update(ctx, (const uint8 *) sp, sl);
00082 
00083     /* Then just as many characters of the MD5(pw,salt,pw) */
00084     px_md_update(ctx1, (const uint8 *) pw, strlen(pw));
00085     px_md_update(ctx1, (const uint8 *) sp, sl);
00086     px_md_update(ctx1, (const uint8 *) pw, strlen(pw));
00087     px_md_finish(ctx1, final);
00088     for (pl = strlen(pw); pl > 0; pl -= MD5_SIZE)
00089         px_md_update(ctx, final, pl > MD5_SIZE ? MD5_SIZE : pl);
00090 
00091     /* Don't leave anything around in vm they could use. */
00092     memset(final, 0, sizeof final);
00093 
00094     /* Then something really weird... */
00095     for (i = strlen(pw); i; i >>= 1)
00096         if (i & 1)
00097             px_md_update(ctx, final, 1);
00098         else
00099             px_md_update(ctx, (const uint8 *) pw, 1);
00100 
00101     /* Now make the output string */
00102     strcpy(passwd, magic);
00103     strncat(passwd, sp, sl);
00104     strcat(passwd, "$");
00105 
00106     px_md_finish(ctx, final);
00107 
00108     /*
00109      * and now, just to make sure things don't run too fast On a 60 Mhz
00110      * Pentium this takes 34 msec, so you would need 30 seconds to build a
00111      * 1000 entry dictionary...
00112      */
00113     for (i = 0; i < 1000; i++)
00114     {
00115         px_md_reset(ctx1);
00116         if (i & 1)
00117             px_md_update(ctx1, (const uint8 *) pw, strlen(pw));
00118         else
00119             px_md_update(ctx1, final, MD5_SIZE);
00120 
00121         if (i % 3)
00122             px_md_update(ctx1, (const uint8 *) sp, sl);
00123 
00124         if (i % 7)
00125             px_md_update(ctx1, (const uint8 *) pw, strlen(pw));
00126 
00127         if (i & 1)
00128             px_md_update(ctx1, final, MD5_SIZE);
00129         else
00130             px_md_update(ctx1, (const uint8 *) pw, strlen(pw));
00131         px_md_finish(ctx1, final);
00132     }
00133 
00134     p = passwd + strlen(passwd);
00135 
00136     l = (final[0] << 16) | (final[6] << 8) | final[12];
00137     _crypt_to64(p, l, 4);
00138     p += 4;
00139     l = (final[1] << 16) | (final[7] << 8) | final[13];
00140     _crypt_to64(p, l, 4);
00141     p += 4;
00142     l = (final[2] << 16) | (final[8] << 8) | final[14];
00143     _crypt_to64(p, l, 4);
00144     p += 4;
00145     l = (final[3] << 16) | (final[9] << 8) | final[15];
00146     _crypt_to64(p, l, 4);
00147     p += 4;
00148     l = (final[4] << 16) | (final[10] << 8) | final[5];
00149     _crypt_to64(p, l, 4);
00150     p += 4;
00151     l = final[11];
00152     _crypt_to64(p, l, 2);
00153     p += 2;
00154     *p = '\0';
00155 
00156     /* Don't leave anything around in vm they could use. */
00157     memset(final, 0, sizeof final);
00158 
00159     px_md_free(ctx1);
00160     px_md_free(ctx);
00161 
00162     return passwd;
00163 }