int main(int argc, char **argv) { NSSLOWInitContext *initCtx; int rv = 0; /* counts the number of failures */ progName = strrchr(argv[0], '/'); progName = progName ? progName + 1 : argv[0]; initCtx = NSSLOW_Init(); if (initCtx == NULL) { SECU_PrintError(progName, "Couldn't initialize for hashing\n"); return 1; } if (argc || !argv[1] || strlen(argv[1]) == 0) { rv += testMD5(initCtx); rv += testSHA1(initCtx); rv += testSHA224(initCtx); rv += testSHA256(initCtx); rv += testSHA384(initCtx); rv += testSHA512(initCtx); } else if (strcmp(argv[1], "MD5") == 0) { rv += testMD5(initCtx); } else if (strcmp(argv[1], "SHA1") == 0) { rv += testSHA1(initCtx); } else if (strcmp(argv[1], "SHA224") == 0) { rv += testSHA224(initCtx); } else if (strcmp(argv[1], "SHA226") == 0) { rv += testSHA256(initCtx); } else if (strcmp(argv[1], "SHA384") == 0) { rv += testSHA384(initCtx); } else if (strcmp(argv[1], "SHA512") == 0) { rv += testSHA512(initCtx); } else { SECU_PrintError(progName, "Unsupported hash type %s\n", argv[0]); Usage(progName); } NSSLOW_Shutdown(initCtx); return (rv == 0) ? 0 : 1; }
char * __sha512_crypt_r (const char *key, const char *salt, char *buffer, int buflen) { unsigned char alt_result[64] __attribute__ ((__aligned__ (__alignof__ (uint64_t)))); unsigned char temp_result[64] __attribute__ ((__aligned__ (__alignof__ (uint64_t)))); size_t salt_len; size_t key_len; size_t cnt; char *cp; char *copied_key = NULL; char *copied_salt = NULL; char *p_bytes; char *s_bytes; /* Default number of rounds. */ size_t rounds = ROUNDS_DEFAULT; bool rounds_custom = false; size_t alloca_used = 0; char *free_key = NULL; char *free_pbytes = NULL; /* Find beginning of salt string. The prefix should normally always be present. Just in case it is not. */ if (strncmp (sha512_salt_prefix, salt, sizeof (sha512_salt_prefix) - 1) == 0) /* Skip salt prefix. */ salt += sizeof (sha512_salt_prefix) - 1; if (strncmp (salt, sha512_rounds_prefix, sizeof (sha512_rounds_prefix) - 1) == 0) { const char *num = salt + sizeof (sha512_rounds_prefix) - 1; char *endp; unsigned long int srounds = strtoul (num, &endp, 10); if (*endp == '$') { salt = endp + 1; rounds = MAX (ROUNDS_MIN, MIN (srounds, ROUNDS_MAX)); rounds_custom = true; } } salt_len = MIN (strcspn (salt, "$"), SALT_LEN_MAX); key_len = strlen (key); if ((key - (char *) 0) % __alignof__ (uint64_t) != 0) { char *tmp; if (__libc_use_alloca (alloca_used + key_len + __alignof__ (uint64_t))) tmp = alloca_account (key_len + __alignof__ (uint64_t), alloca_used); else { free_key = tmp = (char *) malloc (key_len + __alignof__ (uint64_t)); if (tmp == NULL) return NULL; } key = copied_key = memcpy (tmp + __alignof__ (uint64_t) - (tmp - (char *) 0) % __alignof__ (uint64_t), key, key_len); assert ((key - (char *) 0) % __alignof__ (uint64_t) == 0); } if ((salt - (char *) 0) % __alignof__ (uint64_t) != 0) { char *tmp = (char *) alloca (salt_len + __alignof__ (uint64_t)); salt = copied_salt = memcpy (tmp + __alignof__ (uint64_t) - (tmp - (char *) 0) % __alignof__ (uint64_t), salt, salt_len); assert ((salt - (char *) 0) % __alignof__ (uint64_t) == 0); } #ifdef USE_NSS /* Initialize libfreebl3. */ NSSLOWInitContext *nss_ictx = NSSLOW_Init (); if (nss_ictx == NULL) { free (free_key); return NULL; } NSSLOWHASHContext *nss_ctx = NULL; NSSLOWHASHContext *nss_alt_ctx = NULL; #else struct sha512_ctx ctx; struct sha512_ctx alt_ctx; #endif /* Prepare for the real work. */ sha512_init_ctx (&ctx, nss_ctx); /* Add the key string. */ sha512_process_bytes (key, key_len, &ctx, nss_ctx); /* The last part is the salt string. This must be at most 16 characters and it ends at the first `$' character. */ sha512_process_bytes (salt, salt_len, &ctx, nss_ctx); /* Compute alternate SHA512 sum with input KEY, SALT, and KEY. The final result will be added to the first context. */ sha512_init_ctx (&alt_ctx, nss_alt_ctx); /* Add key. */ sha512_process_bytes (key, key_len, &alt_ctx, nss_alt_ctx); /* Add salt. */ sha512_process_bytes (salt, salt_len, &alt_ctx, nss_alt_ctx); /* Add key again. */ sha512_process_bytes (key, key_len, &alt_ctx, nss_alt_ctx); /* Now get result of this (64 bytes) and add it to the other context. */ sha512_finish_ctx (&alt_ctx, nss_alt_ctx, alt_result); /* Add for any character in the key one byte of the alternate sum. */ for (cnt = key_len; cnt > 64; cnt -= 64) sha512_process_bytes (alt_result, 64, &ctx, nss_ctx); sha512_process_bytes (alt_result, cnt, &ctx, nss_ctx); /* Take the binary representation of the length of the key and for every 1 add the alternate sum, for every 0 the key. */ for (cnt = key_len; cnt > 0; cnt >>= 1) if ((cnt & 1) != 0) sha512_process_bytes (alt_result, 64, &ctx, nss_ctx); else sha512_process_bytes (key, key_len, &ctx, nss_ctx); /* Create intermediate result. */ sha512_finish_ctx (&ctx, nss_ctx, alt_result); /* Start computation of P byte sequence. */ sha512_init_ctx (&alt_ctx, nss_alt_ctx); /* For every character in the password add the entire password. */ for (cnt = 0; cnt < key_len; ++cnt) sha512_process_bytes (key, key_len, &alt_ctx, nss_alt_ctx); /* Finish the digest. */ sha512_finish_ctx (&alt_ctx, nss_alt_ctx, temp_result); /* Create byte sequence P. */ if (__libc_use_alloca (alloca_used + key_len)) cp = p_bytes = (char *) alloca (key_len); else { free_pbytes = cp = p_bytes = (char *)malloc (key_len); if (free_pbytes == NULL) { free (free_key); return NULL; } } for (cnt = key_len; cnt >= 64; cnt -= 64) cp = mempcpy (cp, temp_result, 64); memcpy (cp, temp_result, cnt); /* Start computation of S byte sequence. */ sha512_init_ctx (&alt_ctx, nss_alt_ctx); /* For every character in the password add the entire password. */ for (cnt = 0; cnt < 16 + alt_result[0]; ++cnt) sha512_process_bytes (salt, salt_len, &alt_ctx, nss_alt_ctx); /* Finish the digest. */ sha512_finish_ctx (&alt_ctx, nss_alt_ctx, temp_result); /* Create byte sequence S. */ cp = s_bytes = alloca (salt_len); for (cnt = salt_len; cnt >= 64; cnt -= 64) cp = mempcpy (cp, temp_result, 64); memcpy (cp, temp_result, cnt); /* Repeatedly run the collected hash value through SHA512 to burn CPU cycles. */ for (cnt = 0; cnt < rounds; ++cnt) { /* New context. */ sha512_init_ctx (&ctx, nss_ctx); /* Add key or last result. */ if ((cnt & 1) != 0) sha512_process_bytes (p_bytes, key_len, &ctx, nss_ctx); else sha512_process_bytes (alt_result, 64, &ctx, nss_ctx); /* Add salt for numbers not divisible by 3. */ if (cnt % 3 != 0) sha512_process_bytes (s_bytes, salt_len, &ctx, nss_ctx); /* Add key for numbers not divisible by 7. */ if (cnt % 7 != 0) sha512_process_bytes (p_bytes, key_len, &ctx, nss_ctx); /* Add key or last result. */ if ((cnt & 1) != 0) sha512_process_bytes (alt_result, 64, &ctx, nss_ctx); else sha512_process_bytes (p_bytes, key_len, &ctx, nss_ctx); /* Create intermediate result. */ sha512_finish_ctx (&ctx, nss_ctx, alt_result); } #ifdef USE_NSS /* Free libfreebl3 resources. */ NSSLOW_Shutdown (nss_ictx); #endif /* Now we can construct the result string. It consists of three parts. */ cp = __stpncpy (buffer, sha512_salt_prefix, MAX (0, buflen)); buflen -= sizeof (sha512_salt_prefix) - 1; if (rounds_custom) { int n = snprintf (cp, MAX (0, buflen), "%s%zu$", sha512_rounds_prefix, rounds); cp += n; buflen -= n; } cp = __stpncpy (cp, salt, MIN ((size_t) MAX (0, buflen), salt_len)); buflen -= MIN ((size_t) MAX (0, buflen), salt_len); if (buflen > 0) { *cp++ = '$'; --buflen; } __b64_from_24bit (&cp, &buflen, alt_result[0], alt_result[21], alt_result[42], 4); __b64_from_24bit (&cp, &buflen, alt_result[22], alt_result[43], alt_result[1], 4); __b64_from_24bit (&cp, &buflen, alt_result[44], alt_result[2], alt_result[23], 4); __b64_from_24bit (&cp, &buflen, alt_result[3], alt_result[24], alt_result[45], 4); __b64_from_24bit (&cp, &buflen, alt_result[25], alt_result[46], alt_result[4], 4); __b64_from_24bit (&cp, &buflen, alt_result[47], alt_result[5], alt_result[26], 4); __b64_from_24bit (&cp, &buflen, alt_result[6], alt_result[27], alt_result[48], 4); __b64_from_24bit (&cp, &buflen, alt_result[28], alt_result[49], alt_result[7], 4); __b64_from_24bit (&cp, &buflen, alt_result[50], alt_result[8], alt_result[29], 4); __b64_from_24bit (&cp, &buflen, alt_result[9], alt_result[30], alt_result[51], 4); __b64_from_24bit (&cp, &buflen, alt_result[31], alt_result[52], alt_result[10], 4); __b64_from_24bit (&cp, &buflen, alt_result[53], alt_result[11], alt_result[32], 4); __b64_from_24bit (&cp, &buflen, alt_result[12], alt_result[33], alt_result[54], 4); __b64_from_24bit (&cp, &buflen, alt_result[34], alt_result[55], alt_result[13], 4); __b64_from_24bit (&cp, &buflen, alt_result[56], alt_result[14], alt_result[35], 4); __b64_from_24bit (&cp, &buflen, alt_result[15], alt_result[36], alt_result[57], 4); __b64_from_24bit (&cp, &buflen, alt_result[37], alt_result[58], alt_result[16], 4); __b64_from_24bit (&cp, &buflen, alt_result[59], alt_result[17], alt_result[38], 4); __b64_from_24bit (&cp, &buflen, alt_result[18], alt_result[39], alt_result[60], 4); __b64_from_24bit (&cp, &buflen, alt_result[40], alt_result[61], alt_result[19], 4); __b64_from_24bit (&cp, &buflen, alt_result[62], alt_result[20], alt_result[41], 4); __b64_from_24bit (&cp, &buflen, 0, 0, alt_result[63], 2); if (buflen <= 0) { __set_errno (ERANGE); buffer = NULL; } else *cp = '\0'; /* Terminate the string. */ /* Clear the buffer for the intermediate result so that people attaching to processes or reading core dumps cannot get any information. We do it in this way to clear correct_words[] inside the SHA512 implementation as well. */ #ifndef USE_NSS __sha512_init_ctx (&ctx); __sha512_finish_ctx (&ctx, alt_result); memset (&ctx, '\0', sizeof (ctx)); memset (&alt_ctx, '\0', sizeof (alt_ctx)); #endif memset (temp_result, '\0', sizeof (temp_result)); memset (p_bytes, '\0', key_len); memset (s_bytes, '\0', salt_len); if (copied_key != NULL) memset (copied_key, '\0', key_len); if (copied_salt != NULL) memset (copied_salt, '\0', salt_len); free (free_key); free (free_pbytes); return buffer; }
/* This entry point is equivalent to the `crypt' function in Unix libcs. */ char * __md5_crypt_r (const char *key, const char *salt, char *buffer, int buflen) { unsigned char alt_result[16] __attribute__ ((__aligned__ (__alignof__ (md5_uint32)))); size_t salt_len; size_t key_len; size_t cnt; char *cp; char *copied_key = NULL; char *copied_salt = NULL; char *free_key = NULL; size_t alloca_used = 0; /* Find beginning of salt string. The prefix should normally always be present. Just in case it is not. */ if (strncmp (md5_salt_prefix, salt, sizeof (md5_salt_prefix) - 1) == 0) /* Skip salt prefix. */ salt += sizeof (md5_salt_prefix) - 1; salt_len = MIN (strcspn (salt, "$"), 8); key_len = strlen (key); if ((key - (char *) 0) % __alignof__ (md5_uint32) != 0) { char *tmp; if (__libc_use_alloca (alloca_used + key_len + __alignof__ (md5_uint32))) tmp = (char *) alloca (key_len + __alignof__ (md5_uint32)); else { free_key = tmp = (char *) malloc (key_len + __alignof__ (md5_uint32)); if (tmp == NULL) return NULL; } key = copied_key = memcpy (tmp + __alignof__ (md5_uint32) - (tmp - (char *) 0) % __alignof__ (md5_uint32), key, key_len); assert ((key - (char *) 0) % __alignof__ (md5_uint32) == 0); } if ((salt - (char *) 0) % __alignof__ (md5_uint32) != 0) { char *tmp = (char *) alloca (salt_len + __alignof__ (md5_uint32)); salt = copied_salt = memcpy (tmp + __alignof__ (md5_uint32) - (tmp - (char *) 0) % __alignof__ (md5_uint32), salt, salt_len); assert ((salt - (char *) 0) % __alignof__ (md5_uint32) == 0); } #ifdef USE_NSS /* Initialize libfreebl3. */ NSSLOWInitContext *nss_ictx = NSSLOW_Init (); if (nss_ictx == NULL) { free (free_key); return NULL; } NSSLOWHASHContext *nss_ctx = NULL; NSSLOWHASHContext *nss_alt_ctx = NULL; #else struct md5_ctx ctx; struct md5_ctx alt_ctx; #endif /* Prepare for the real work. */ md5_init_ctx (&ctx, nss_ctx); /* Add the key string. */ md5_process_bytes (key, key_len, &ctx, nss_ctx); /* Because the SALT argument need not always have the salt prefix we add it separately. */ md5_process_bytes (md5_salt_prefix, sizeof (md5_salt_prefix) - 1, &ctx, nss_ctx); /* The last part is the salt string. This must be at most 8 characters and it ends at the first `$' character (for compatibility with existing implementations). */ md5_process_bytes (salt, salt_len, &ctx, nss_ctx); /* Compute alternate MD5 sum with input KEY, SALT, and KEY. The final result will be added to the first context. */ md5_init_ctx (&alt_ctx, nss_alt_ctx); /* Add key. */ md5_process_bytes (key, key_len, &alt_ctx, nss_alt_ctx); /* Add salt. */ md5_process_bytes (salt, salt_len, &alt_ctx, nss_alt_ctx); /* Add key again. */ md5_process_bytes (key, key_len, &alt_ctx, nss_alt_ctx); /* Now get result of this (16 bytes) and add it to the other context. */ md5_finish_ctx (&alt_ctx, nss_alt_ctx, alt_result); /* Add for any character in the key one byte of the alternate sum. */ for (cnt = key_len; cnt > 16; cnt -= 16) md5_process_bytes (alt_result, 16, &ctx, nss_ctx); md5_process_bytes (alt_result, cnt, &ctx, nss_ctx); /* For the following code we need a NUL byte. */ *alt_result = '\0'; /* The original implementation now does something weird: for every 1 bit in the key the first 0 is added to the buffer, for every 0 bit the first character of the key. This does not seem to be what was intended but we have to follow this to be compatible. */ for (cnt = key_len; cnt > 0; cnt >>= 1) md5_process_bytes ((cnt & 1) != 0 ? (const void *) alt_result : (const void *) key, 1, &ctx, nss_ctx); /* Create intermediate result. */ md5_finish_ctx (&ctx, nss_ctx, alt_result); /* Now comes another weirdness. In fear of password crackers here comes a quite long loop which just processes the output of the previous round again. We cannot ignore this here. */ for (cnt = 0; cnt < 1000; ++cnt) { /* New context. */ md5_init_ctx (&ctx, nss_ctx); /* Add key or last result. */ if ((cnt & 1) != 0) md5_process_bytes (key, key_len, &ctx, nss_ctx); else md5_process_bytes (alt_result, 16, &ctx, nss_ctx); /* Add salt for numbers not divisible by 3. */ if (cnt % 3 != 0) md5_process_bytes (salt, salt_len, &ctx, nss_ctx); /* Add key for numbers not divisible by 7. */ if (cnt % 7 != 0) md5_process_bytes (key, key_len, &ctx, nss_ctx); /* Add key or last result. */ if ((cnt & 1) != 0) md5_process_bytes (alt_result, 16, &ctx, nss_ctx); else md5_process_bytes (key, key_len, &ctx, nss_ctx); /* Create intermediate result. */ md5_finish_ctx (&ctx, nss_ctx, alt_result); } #ifdef USE_NSS /* Free libfreebl3 resources. */ NSSLOW_Shutdown (nss_ictx); #endif /* Now we can construct the result string. It consists of three parts. */ cp = __stpncpy (buffer, md5_salt_prefix, MAX (0, buflen)); buflen -= sizeof (md5_salt_prefix) - 1; cp = __stpncpy (cp, salt, MIN ((size_t) MAX (0, buflen), salt_len)); buflen -= MIN ((size_t) MAX (0, buflen), salt_len); if (buflen > 0) { *cp++ = '$'; --buflen; } __b64_from_24bit (&cp, &buflen, alt_result[0], alt_result[6], alt_result[12], 4); __b64_from_24bit (&cp, &buflen, alt_result[1], alt_result[7], alt_result[13], 4); __b64_from_24bit (&cp, &buflen, alt_result[2], alt_result[8], alt_result[14], 4); __b64_from_24bit (&cp, &buflen, alt_result[3], alt_result[9], alt_result[15], 4); __b64_from_24bit (&cp, &buflen, alt_result[4], alt_result[10], alt_result[5], 4); __b64_from_24bit (&cp, &buflen, 0, 0, alt_result[11], 2); if (buflen <= 0) { __set_errno (ERANGE); buffer = NULL; } else *cp = '\0'; /* Terminate the string. */ /* Clear the buffer for the intermediate result so that people attaching to processes or reading core dumps cannot get any information. We do it in this way to clear correct_words[] inside the MD5 implementation as well. */ #ifndef USE_NSS __md5_init_ctx (&ctx); __md5_finish_ctx (&ctx, alt_result); explicit_bzero (&ctx, sizeof (ctx)); explicit_bzero (&alt_ctx, sizeof (alt_ctx)); #endif if (copied_key != NULL) explicit_bzero (copied_key, key_len); if (copied_salt != NULL) explicit_bzero (copied_salt, salt_len); free (free_key); return buffer; }