diff -r 2dd6fb04f549 -r fb707e745d56 src/Makefile @@ -41,6 +43,7 @@ JOHN_OBJS_MINIMAL = \ md5.o \ IPB2_fmt.o \ rawSHA1_fmt.o \ + mysqlSHA1_fmt.o \ NSLDAP_fmt.o NSLDAPS_fmt.o sha1.o base64.o \ NT_fmt.o \ md4.o smbencrypt.o \ diff -r 2dd6fb04f549 -r fb707e745d56 src/john.c --- a/src/john.c Sat Nov 03 13:33:52 2007 +0200 +++ b/src/john.c Sat Nov 10 16:31:51 2007 +0200 @@ -47,6 +47,7 @@ extern struct fmt_main fmt_NSLDAPS; extern struct fmt_main fmt_NSLDAPS; extern struct fmt_main fmt_mscash; extern struct fmt_main fmt_rawSHA1; +extern struct fmt_main fmt_mysqlSHA1; extern struct fmt_main fmt_lotus5; extern struct fmt_main fmt_DOMINOSEC; extern struct fmt_main fmt_NETLM; @@ -83,6 +84,7 @@ static void john_register_all(void) john_register_one(&fmt_rawMD5go); john_register_one(&fmt_IPB2); john_register_one(&fmt_rawSHA1); + john_register_one(&fmt_mysqlSHA1); john_register_one(&fmt_KRB5); john_register_one(&fmt_NSLDAP); john_register_one(&fmt_NSLDAPS); diff -r 2dd6fb04f549 -r fb707e745d56 src/mysqlSHA1_fmt.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mysqlSHA1_fmt.c Sat Nov 10 16:31:51 2007 +0200 @@ -0,0 +1,353 @@ +// vim: set ts=8 sw=4 et : +/* + * Copyright (c) 2004 bartavelle, bartavelle@bandecon.com + * Copyright (c) 2007 Marti Raudsepp + * + * Simple MySQL 4.1+ PASSWORD() hash cracker, rev 1. + * Adapted from the original rawSHA1_fmt.c cracker. + * + * Note that many version 4.1 and 5.0 installations still use the old + * homebrewn pre-4.1 hash for compatibility with older clients, notably all + * Red Hat-based distributions. + * + * The new PASSWORD() function is unsalted and equivalent to + * SHA1(SHA1(password)) where the inner is a binary digest (not hex!) This + * means that with the SSE2-boosted SHA1 implementation, it will be several + * times faster than John's cracker for the old hash format. (though the old + * hash had significant weaknesses, John's code does not take advantage of + * that) + * + * It's a slight improvement over the old hash, but still not something a + * reasonable DBMS would use for password storage. + */ + +#include + +#include "arch.h" +#include "misc.h" +#include "common.h" +#include "formats.h" +#include "sha.h" + +//#define X_DEBUG +#ifdef X_DEBUG +# include +#endif + +#define FORMAT_LABEL "mysql-sha1" +#define FORMAT_NAME "MySQL 4.1 double-SHA1" +#ifdef MMX_COEF +# if (MMX_COEF == 2) +# define ALGORITHM_NAME "mysql-sha1 MMX" +# else +# define ALGORITHM_NAME "mysql-sha1 SSE2" +# endif +#else +# define ALGORITHM_NAME "mysql-sha1" +#endif + +#ifdef MMX_TYPE +# define BENCHMARK_COMMENT MMX_TYPE +#else +# define BENCHMARK_COMMENT "" +#endif +#define BENCHMARK_LENGTH -1 + +#define PLAINTEXT_LENGTH 32 +#define CIPHERTEXT_LENGTH 41 + +#define BINARY_SIZE 20 +#define SALT_SIZE 0 + +#ifdef MMX_COEF +# define MIN_KEYS_PER_CRYPT MMX_COEF +# define MAX_KEYS_PER_CRYPT MMX_COEF +//#define GETPOS(i, index) ( (index)*4 + (i& (0xffffffff-3) )*MMX_COEF + ((i)&3) ) //std getpos +# define GETPOS(i, index) ( (index)*4 + (i& (0xffffffff-3) )*MMX_COEF + (3-((i)&3)) ) //for endianity conversion +# define BYTESWAP(n) ( \ + (((n)&0x000000ff) << 24) | \ + (((n)&0x0000ff00) << 8 ) | \ + (((n)&0x00ff0000) >> 8 ) | \ + (((n)&0xff000000) >> 24) ) +#else +# define MIN_KEYS_PER_CRYPT 1 +# define MAX_KEYS_PER_CRYPT 1 +#endif + +static struct fmt_tests mysqlsha1_tests[] = { + {"*5AD8F88516BD021DD43F171E2C785C69F8E54ADB", "tere"}, + {"*2C905879F74F28F8570989947D06A8429FB943E6", "verysecretpassword"}, + {"*A8A397146B1A5F8C8CF26404668EFD762A1B7B82", "________________________________"}, + {"*F9F1470004E888963FB466A5452C9CBD9DF6239C", "12345678123456781234567812345678"}, + {"*97CF7A3ACBE0CA58D5391AC8377B5D9AC11D46D9", "' OR 1 /*'"}, + {"*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19", "password"}, + {"*7534F9EAEE5B69A586D1E9C1ACE3E3F9F6FCC446", "5"}, + {NULL} +}; + +#ifdef MMX_COEF +static char saved_key[PLAINTEXT_LENGTH*MMX_COEF*2 + 1] __attribute__ ((aligned(16))); +static char crypt_key[80*4*MMX_COEF+1] __attribute__ ((aligned(16))); +unsigned long total_len; + +/* Intermediate key which stores the hashes between two SHA1 operations. Don't + * ask me why it has to be so long ;) */ +static char interm_key[80*4*MMX_COEF*2 + 1] __attribute__ ((aligned(16))) = {0}; + +# if MMX_COEF > 2 +/* argument to shammx(); all intermediary plaintexts are 20 bytes long */ +# define TMPKEY_LENGTHS 0x14141414 +# else +# define TMPKEY_LENGTHS 0x00140014 +# endif + +unsigned char out[PLAINTEXT_LENGTH]; +#else +static char saved_key[PLAINTEXT_LENGTH + 1]; +static char crypt_key[BINARY_SIZE+1]; +static SHA_CTX ctx; +#endif + +static int valid(char *ciphertext) +{ + int i; + + if (strlen(ciphertext) != CIPHERTEXT_LENGTH) return 0; + if (ciphertext[0] != '*') + return 0; + for (i = 1; i < CIPHERTEXT_LENGTH; i++){ + if (!( (('0' <= ciphertext[i])&&(ciphertext[i] <= '9')) + || (('a' <= ciphertext[i])&&(ciphertext[i] <= 'f')) + || (('A' <= ciphertext[i])&&(ciphertext[i] <= 'F')))) + { + return 0; + } + } + return 1; +} + +static void mysqlsha1_set_salt(void *salt) { } + +static void mysqlsha1_init(void) +{ +#ifdef MMX_COEF + memset(saved_key, 0, sizeof saved_key); + memset(interm_key, 0, sizeof interm_key); + /* input strings have to be terminated by 0x80. The input strings in + * interm_key have a static length (20 bytes) so we can set them just once. + */ + const int offset = (MMX_COEF*BINARY_SIZE)/4; + + ((unsigned*)interm_key)[offset+0] = BYTESWAP(0x80); + ((unsigned*)interm_key)[offset+1] = BYTESWAP(0x80); +# if MMX_COEF > 2 + ((unsigned*)interm_key)[offset+2] = BYTESWAP(0x80); + ((unsigned*)interm_key)[offset+3] = BYTESWAP(0x80); +# endif +#endif +} + +static void mysqlsha1_set_key(char *key, int index) { +#ifdef MMX_COEF + int len; + int i; + /* FIXME: we're wasting 22% time in set_key with SSE2 (rawSHA1 is wasting + * nearly 50%!). The huge memset() is probably a culprit, but also the + * bytewise byte-order swapping code (see GETPOS macro above). */ + + if(index==0) + { + total_len = 0; + memset(saved_key, 0, sizeof(saved_key)); + } + len = strlen(key); + if(len>PLAINTEXT_LENGTH) + len = PLAINTEXT_LENGTH; + + total_len += len << ( ( (32/MMX_COEF) * index ) ); + for(i=0;i> (((32/MMX_COEF)*(index)))) & 0xff; + for(i=0;i 3) + && ( ((unsigned long *)binary)[i] != ((unsigned long *)crypt_key)[i*MMX_COEF+2]) + && ( ((unsigned long *)binary)[i] != ((unsigned long *)crypt_key)[i*MMX_COEF+3]) +#endif + ) + return 0; + i++; + } +#else + while(i 2 + assert(((unsigned*)interm_key)[i+2] == BYTESWAP(0x80)); + assert(((unsigned*)interm_key)[i+3] == BYTESWAP(0x80)); +# endif +# endif /* X_DEBUG */ + + shammx(crypt_key, interm_key, TMPKEY_LENGTHS); + +#else + SHA1_Init(&ctx); + SHA1_Update(&ctx, saved_key, strlen(saved_key)); + SHA1_Final(crypt_key, &ctx); + + SHA1_Init(&ctx); + SHA1_Update(&ctx, crypt_key, BINARY_SIZE); + SHA1_Final(crypt_key, &ctx); +#endif +} + +static void *mysqlsha1_binary(char *ciphertext) +{ + static char realcipher[BINARY_SIZE]; + int i; + + // ignore first character '*' + ciphertext += 1; + for(i=0;i