int s3crypt_gen_salt(TALLOC_CTX *memctx, char **_salt) { uint8_t rb[SALT_RAND_LEN]; char *salt, *cp; size_t slen; int ret; ret = nspr_nss_init(); if (ret != EOK) { return EIO; } salt = talloc_size(memctx, SALT_LEN_MAX + 1); if (!salt) { return ENOMEM; } ret = PK11_GenerateRandom(rb, SALT_RAND_LEN); if (ret != SECSuccess) { return EIO; } slen = SALT_LEN_MAX; cp = salt; b64_from_24bit(&cp, &slen, 4, rb[0], rb[1], rb[2]); b64_from_24bit(&cp, &slen, 4, rb[3], rb[4], rb[5]); b64_from_24bit(&cp, &slen, 4, rb[6], rb[7], rb[8]); b64_from_24bit(&cp, &slen, 4, rb[9], rb[10], rb[11]); *cp = '\0'; *_salt = salt; return EOK; }
char *yahoo_crypt(const char *key, const char *salt) { static char *buffer = NULL; static int buflen = 0; int needed = 3 + strlen (salt) + 1 + 26 + 1; size_t salt_len; size_t key_len; size_t cnt; char *cp; if (buflen < needed) { buflen = needed; if ((buffer = (char*)realloc(buffer, buflen)) == NULL) return NULL; } /* 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); string ct = key; ct += md5_salt_prefix; ct += salt; string ct_alt = key; ct_alt += salt; ct_alt += key; ct_alt = md5(ct_alt.c_str()); /* Add for any character in the key one byte of the alternate sum. */ for (cnt = key_len; cnt > 16; cnt -= 16) ct.append(ct_alt.c_str(), 16); ct.append(ct_alt.c_str(), cnt); char nil[] = ""; /* 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) ct.append((cnt & 1) != 0 ? nil : key, 1); /* Create intermediate result. */ ct_alt = md5(ct.c_str(), ct.length()); /* 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. */ ct = ""; /* Add key or last result. */ if ((cnt & 1) != 0) ct += key; else ct.append(ct_alt.c_str(), 16); /* Add salt for numbers not divisible by 3. */ if (cnt % 3 != 0) ct += salt; /* Add key for numbers not divisible by 7. */ if (cnt % 7 != 0) ct += key; /* Add key or last result. */ if ((cnt & 1) != 0) ct.append(ct_alt.c_str(), 16); else ct += key; /* Create intermediate result. */ ct_alt = md5(ct.c_str(), ct.length()); } /* Now we can construct the result string. It consists of three parts. */ strncpy(buffer, md5_salt_prefix, MAX (0, buflen)); cp = buffer + strlen(buffer); buflen -= sizeof (md5_salt_prefix); strncpy(cp, salt, MIN ((size_t) buflen, salt_len)); cp = cp + strlen(cp); buflen -= MIN ((size_t) buflen, salt_len); if (buflen > 0) { *cp++ = '$'; --buflen; } #define b64_from_24bit(B2, B1, B0, N) \ { \ unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \ int n = (N); \ while (n-- > 0 && buflen > 0) { \ *cp++ = b64t[w & 0x3f]; \ --buflen; \ w >>= 6; \ }\ } b64_from_24bit (ct_alt[0], ct_alt[6], ct_alt[12], 4); b64_from_24bit (ct_alt[1], ct_alt[7], ct_alt[13], 4); b64_from_24bit (ct_alt[2], ct_alt[8], ct_alt[14], 4); b64_from_24bit (ct_alt[3], ct_alt[9], ct_alt[15], 4); b64_from_24bit (ct_alt[4], ct_alt[10], ct_alt[5], 4); b64_from_24bit (0, 0, ct_alt[11], 2); if (buflen <= 0) { free(buffer); buffer = NULL; } else *cp = '\0'; /* Terminate the string. */ return buffer; }
static char * crypt_sha512_r(const char *key, const char *salt, char *buffer, int buflen) { u_long srounds; int n; uint8_t alt_result[64], temp_result[64]; SHA512_CTX ctx, alt_ctx; size_t salt_len, key_len, cnt, rounds; char *cp, *copied_key, *copied_salt, *p_bytes, *s_bytes, *endp; const char *num; bool rounds_custom; copied_key = NULL; copied_salt = NULL; /* Default number of rounds. */ rounds = ROUNDS_DEFAULT; rounds_custom = false; /* 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) { num = salt + sizeof(sha512_rounds_prefix) - 1; 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); /* Prepare for the real work. */ SHA512_Init(&ctx); /* Add the key string. */ SHA512_Update(&ctx, key, key_len); /* 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). */ SHA512_Update(&ctx, salt, salt_len); /* Compute alternate SHA512 sum with input KEY, SALT, and KEY. The * final result will be added to the first context. */ SHA512_Init(&alt_ctx); /* Add key. */ SHA512_Update(&alt_ctx, key, key_len); /* Add salt. */ SHA512_Update(&alt_ctx, salt, salt_len); /* Add key again. */ SHA512_Update(&alt_ctx, key, key_len); /* Now get result of this (64 bytes) and add it to the other context. */ SHA512_Final(alt_result, &alt_ctx); /* Add for any character in the key one byte of the alternate sum. */ for (cnt = key_len; cnt > 64; cnt -= 64) SHA512_Update(&ctx, alt_result, 64); SHA512_Update(&ctx, alt_result, cnt); /* 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_Update(&ctx, alt_result, 64); else SHA512_Update(&ctx, key, key_len); /* Create intermediate result. */ SHA512_Final(alt_result, &ctx); /* Start computation of P byte sequence. */ SHA512_Init(&alt_ctx); /* For every character in the password add the entire password. */ for (cnt = 0; cnt < key_len; ++cnt) SHA512_Update(&alt_ctx, key, key_len); /* Finish the digest. */ SHA512_Final(temp_result, &alt_ctx); /* Create byte sequence P. */ cp = p_bytes = alloca(key_len); for (cnt = key_len; cnt >= 64; cnt -= 64) { memcpy(cp, temp_result, 64); cp += 64; } memcpy(cp, temp_result, cnt); /* Start computation of S byte sequence. */ SHA512_Init(&alt_ctx); /* For every character in the password add the entire password. */ for (cnt = 0; cnt < 16 + alt_result[0]; ++cnt) SHA512_Update(&alt_ctx, salt, salt_len); /* Finish the digest. */ SHA512_Final(temp_result, &alt_ctx); /* Create byte sequence S. */ cp = s_bytes = alloca(salt_len); for (cnt = salt_len; cnt >= 64; cnt -= 64) { memcpy(cp, temp_result, 64); cp += 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); /* Add key or last result. */ if ((cnt & 1) != 0) SHA512_Update(&ctx, p_bytes, key_len); else SHA512_Update(&ctx, alt_result, 64); /* Add salt for numbers not divisible by 3. */ if (cnt % 3 != 0) SHA512_Update(&ctx, s_bytes, salt_len); /* Add key for numbers not divisible by 7. */ if (cnt % 7 != 0) SHA512_Update(&ctx, p_bytes, key_len); /* Add key or last result. */ if ((cnt & 1) != 0) SHA512_Update(&ctx, alt_result, 64); else SHA512_Update(&ctx, p_bytes, key_len); /* Create intermediate result. */ SHA512_Final(alt_result, &ctx); } /* 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) { 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(alt_result[0], alt_result[21], alt_result[42], 4, &buflen, &cp); b64_from_24bit(alt_result[22], alt_result[43], alt_result[1], 4, &buflen, &cp); b64_from_24bit(alt_result[44], alt_result[2], alt_result[23], 4, &buflen, &cp); b64_from_24bit(alt_result[3], alt_result[24], alt_result[45], 4, &buflen, &cp); b64_from_24bit(alt_result[25], alt_result[46], alt_result[4], 4, &buflen, &cp); b64_from_24bit(alt_result[47], alt_result[5], alt_result[26], 4, &buflen, &cp); b64_from_24bit(alt_result[6], alt_result[27], alt_result[48], 4, &buflen, &cp); b64_from_24bit(alt_result[28], alt_result[49], alt_result[7], 4, &buflen, &cp); b64_from_24bit(alt_result[50], alt_result[8], alt_result[29], 4, &buflen, &cp); b64_from_24bit(alt_result[9], alt_result[30], alt_result[51], 4, &buflen, &cp); b64_from_24bit(alt_result[31], alt_result[52], alt_result[10], 4, &buflen, &cp); b64_from_24bit(alt_result[53], alt_result[11], alt_result[32], 4, &buflen, &cp); b64_from_24bit(alt_result[12], alt_result[33], alt_result[54], 4, &buflen, &cp); b64_from_24bit(alt_result[34], alt_result[55], alt_result[13], 4, &buflen, &cp); b64_from_24bit(alt_result[56], alt_result[14], alt_result[35], 4, &buflen, &cp); b64_from_24bit(alt_result[15], alt_result[36], alt_result[57], 4, &buflen, &cp); b64_from_24bit(alt_result[37], alt_result[58], alt_result[16], 4, &buflen, &cp); b64_from_24bit(alt_result[59], alt_result[17], alt_result[38], 4, &buflen, &cp); b64_from_24bit(alt_result[18], alt_result[39], alt_result[60], 4, &buflen, &cp); b64_from_24bit(alt_result[40], alt_result[61], alt_result[19], 4, &buflen, &cp); b64_from_24bit(alt_result[62], alt_result[20], alt_result[41], 4, &buflen, &cp); b64_from_24bit(0, 0, alt_result[63], 2, &buflen, &cp); if (buflen <= 0) { 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. */ SHA512_Init(&ctx); SHA512_Final(alt_result, &ctx); memset(temp_result, '\0', sizeof(temp_result)); memset(p_bytes, '\0', key_len); memset(s_bytes, '\0', salt_len); memset(&ctx, '\0', sizeof(ctx)); memset(&alt_ctx, '\0', sizeof(alt_ctx)); if (copied_key != NULL) memset(copied_key, '\0', key_len); if (copied_salt != NULL) memset(copied_salt, '\0', salt_len); return buffer; }
static int sha512_crypt_r(const char *key, const char *salt, char *buffer, size_t buflen) { unsigned char temp_result[64] __attribute__((__aligned__(ALIGN64))); unsigned char alt_result[64] __attribute__((__aligned__(ALIGN64))); size_t rounds = ROUNDS_DEFAULT; bool rounds_custom = false; HASHContext *alt_ctx = NULL; HASHContext *ctx = NULL; size_t salt_len; size_t key_len; size_t cnt; char *copied_salt = NULL; char *copied_key = NULL; char *p_bytes = NULL; char *s_bytes = NULL; int p1, p2, p3, pt, n; unsigned int part; char *cp, *tmp; int ret; /* Find beginning of salt string. The prefix should normally always be * present. Just in case it is not. */ if (strncmp(salt, sha512_salt_prefix, SALT_PREF_SIZE) == 0) { /* Skip salt prefix. */ salt += SALT_PREF_SIZE; } if (strncmp(salt, sha512_rounds_prefix, ROUNDS_SIZE) == 0) { unsigned long int srounds; const char *num; char *endp; num = salt + ROUNDS_SIZE; srounds = strtoul(num, &endp, 10); if (*endp == '$') { salt = endp + 1; if (srounds < ROUNDS_MIN) srounds = ROUNDS_MIN; if (srounds > ROUNDS_MAX) srounds = ROUNDS_MAX; rounds = srounds; rounds_custom = true; } } salt_len = MIN(strcspn(salt, "$"), SALT_LEN_MAX); key_len = strlen(key); if ((PTR_2_INT(key) % ALIGN64) != 0) { tmp = (char *)alloca(key_len + ALIGN64); key = copied_key = memcpy(tmp + ALIGN64 - PTR_2_INT(tmp) % ALIGN64, key, key_len); } if (PTR_2_INT(salt) % ALIGN64 != 0) { tmp = (char *)alloca(salt_len + ALIGN64); salt = copied_salt = memcpy(tmp + ALIGN64 - PTR_2_INT(tmp) % ALIGN64, salt, salt_len); } ret = nspr_nss_init(); if (ret != EOK) { ret = EIO; goto done; } ctx = HASH_Create(HASH_AlgSHA512); if (!ctx) { ret = EIO; goto done; } alt_ctx = HASH_Create(HASH_AlgSHA512); if (!alt_ctx) { ret = EIO; goto done; } /* Prepare for the real work. */ HASH_Begin(ctx); /* Add the key string. */ HASH_Update(ctx, (const unsigned char *)key, key_len); /* The last part is the salt string. This must be at most 16 * characters and it ends at the first `$' character (for * compatibility with existing implementations). */ HASH_Update(ctx, (const unsigned char *)salt, salt_len); /* Compute alternate SHA512 sum with input KEY, SALT, and KEY. * The final result will be added to the first context. */ HASH_Begin(alt_ctx); /* Add key. */ HASH_Update(alt_ctx, (const unsigned char *)key, key_len); /* Add salt. */ HASH_Update(alt_ctx, (const unsigned char *)salt, salt_len); /* Add key again. */ HASH_Update(alt_ctx, (const unsigned char *)key, key_len); /* Now get result of this (64 bytes) and add it to the other context. */ HASH_End(alt_ctx, alt_result, &part, HASH_ResultLenContext(alt_ctx)); /* Add for any character in the key one byte of the alternate sum. */ for (cnt = key_len; cnt > 64; cnt -= 64) { HASH_Update(ctx, alt_result, 64); } HASH_Update(ctx, alt_result, cnt); /* 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) { HASH_Update(ctx, alt_result, 64); } else { HASH_Update(ctx, (const unsigned char *)key, key_len); } } /* Create intermediate result. */ HASH_End(ctx, alt_result, &part, HASH_ResultLenContext(ctx)); /* Start computation of P byte sequence. */ HASH_Begin(alt_ctx); /* For every character in the password add the entire password. */ for (cnt = 0; cnt < key_len; cnt++) { HASH_Update(alt_ctx, (const unsigned char *)key, key_len); } /* Finish the digest. */ HASH_End(alt_ctx, temp_result, &part, HASH_ResultLenContext(alt_ctx)); /* Create byte sequence P. */ cp = p_bytes = alloca(key_len); 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. */ HASH_Begin(alt_ctx); /* For every character in the password add the entire salt. */ for (cnt = 0; cnt < 16 + alt_result[0]; cnt++) { HASH_Update(alt_ctx, (const unsigned char *)salt, salt_len); } /* Finish the digest. */ HASH_End(alt_ctx, temp_result, &part, HASH_ResultLenContext(alt_ctx)); /* 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++) { HASH_Begin(ctx); /* Add key or last result. */ if ((cnt & 1) != 0) { HASH_Update(ctx, (const unsigned char *)p_bytes, key_len); } else { HASH_Update(ctx, alt_result, 64); } /* Add salt for numbers not divisible by 3. */ if (cnt % 3 != 0) { HASH_Update(ctx, (const unsigned char *)s_bytes, salt_len); } /* Add key for numbers not divisible by 7. */ if (cnt % 7 != 0) { HASH_Update(ctx, (const unsigned char *)p_bytes, key_len); } /* Add key or last result. */ if ((cnt & 1) != 0) { HASH_Update(ctx, alt_result, 64); } else { HASH_Update(ctx, (const unsigned char *)p_bytes, key_len); } /* Create intermediate result. */ HASH_End(ctx, alt_result, &part, HASH_ResultLenContext(ctx)); } /* Now we can construct the result string. * It consists of three parts. */ if (buflen <= SALT_PREF_SIZE) { ret = ERANGE; goto done; } cp = __stpncpy(buffer, sha512_salt_prefix, SALT_PREF_SIZE); buflen -= SALT_PREF_SIZE; if (rounds_custom) { n = snprintf(cp, buflen, "%s%zu$", sha512_rounds_prefix, rounds); if (n < 0 || n >= buflen) { ret = ERANGE; goto done; } cp += n; buflen -= n; } if (buflen <= salt_len + 1) { ret = ERANGE; goto done; } cp = __stpncpy(cp, salt, salt_len); *cp++ = '$'; buflen -= salt_len + 1; /* fuzzyfill the base 64 string */ p1 = 0; p2 = 21; p3 = 42; for (n = 0; n < 21; n++) { b64_from_24bit(&cp, &buflen, 4, alt_result[p1], alt_result[p2], alt_result[p3]); if (buflen == 0) { ret = ERANGE; goto done; } pt = p1; p1 = p2 + 1; p2 = p3 + 1; p3 = pt + 1; } /* 64th and last byte */ b64_from_24bit(&cp, &buflen, 2, 0, 0, alt_result[p3]); if (buflen == 0) { ret = ERANGE; goto done; } *cp = '\0'; ret = EOK; done: /* 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. */ if (ctx) HASH_Destroy(ctx); if (alt_ctx) HASH_Destroy(alt_ctx); if (p_bytes) memset(p_bytes, '\0', key_len); if (s_bytes) memset(s_bytes, '\0', salt_len); if (copied_key) memset(copied_key, '\0', key_len); if (copied_salt) memset(copied_salt, '\0', salt_len); memset(temp_result, '\0', sizeof(temp_result)); return ret; }
char *yahoo_crypt(const char *key, const char *salt) { PurpleCipher *cipher; PurpleCipherContext *context1, *context2; guchar digest[16]; static char *buffer = NULL; static int buflen = 0; int needed = 3 + strlen (salt) + 1 + 26 + 1; size_t salt_len; size_t key_len; size_t cnt; char *cp; if (buflen < needed) { buflen = needed; if ((buffer = g_realloc(buffer, buflen)) == NULL) return NULL; } cipher = purple_ciphers_find_cipher("md5"); context1 = purple_cipher_context_new(cipher, NULL); context2 = purple_cipher_context_new(cipher, NULL); /* 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); /* Add the key string. */ purple_cipher_context_append(context1, (const guchar *)key, key_len); /* Because the SALT argument need not always have the salt prefix we * add it separately. */ purple_cipher_context_append(context1, (const guchar *)md5_salt_prefix, sizeof(md5_salt_prefix) - 1); /* The last part is the salt string. This must be at most 8 * characters and it ends at the first `$' character (for * compatibility which existing solutions). */ purple_cipher_context_append(context1, (const guchar *)salt, salt_len); /* Compute alternate MD5 sum with input KEY, SALT, and KEY. The * final result will be added to the first context. */ /* Add key. */ purple_cipher_context_append(context2, (const guchar *)key, key_len); /* Add salt. */ purple_cipher_context_append(context2, (const guchar *)salt, salt_len); /* Add key again. */ purple_cipher_context_append(context2, (const guchar *)key, key_len); /* Now get result of this (16 bytes) and add it to the other context. */ purple_cipher_context_digest(context2, sizeof(digest), digest, NULL); /* Add for any character in the key one byte of the alternate sum. */ for (cnt = key_len; cnt > 16; cnt -= 16) purple_cipher_context_append(context1, digest, 16); purple_cipher_context_append(context1, digest, cnt); /* For the following code we need a NUL byte. */ digest[0] = '\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) purple_cipher_context_append(context1, (cnt & 1) != 0 ? digest : (guchar *)key, 1); /* Create intermediate result. */ purple_cipher_context_digest(context1, sizeof(digest), digest, NULL); /* 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. */ purple_cipher_context_reset(context2, NULL); /* Add key or last result. */ if ((cnt & 1) != 0) purple_cipher_context_append(context2, (const guchar *)key, key_len); else purple_cipher_context_append(context2, digest, 16); /* Add salt for numbers not divisible by 3. */ if (cnt % 3 != 0) purple_cipher_context_append(context2, (const guchar *)salt, salt_len); /* Add key for numbers not divisible by 7. */ if (cnt % 7 != 0) purple_cipher_context_append(context2, (const guchar *)key, key_len); /* Add key or last result. */ if ((cnt & 1) != 0) purple_cipher_context_append(context2, digest, 16); else purple_cipher_context_append(context2, (const guchar *)key, key_len); /* Create intermediate result. */ purple_cipher_context_digest(context2, sizeof(digest), digest, NULL); } /* Now we can construct the result string. It consists of three parts. */ strncpy(buffer, md5_salt_prefix, MAX (0, buflen)); cp = buffer + strlen(buffer); buflen -= sizeof (md5_salt_prefix); strncpy(cp, salt, MIN ((size_t) buflen, salt_len)); cp = cp + strlen(cp); buflen -= MIN ((size_t) buflen, salt_len); if (buflen > 0) { *cp++ = '$'; --buflen; } #define b64_from_24bit(B2, B1, B0, N) \ do { \ unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \ int n = (N); \ while (n-- > 0 && buflen > 0) { \ *cp++ = b64t[w & 0x3f]; \ --buflen; \ w >>= 6; \ }\ } while (0) b64_from_24bit (digest[0], digest[6], digest[12], 4); b64_from_24bit (digest[1], digest[7], digest[13], 4); b64_from_24bit (digest[2], digest[8], digest[14], 4); b64_from_24bit (digest[3], digest[9], digest[15], 4); b64_from_24bit (digest[4], digest[10], digest[5], 4); b64_from_24bit (0, 0, digest[11], 2); if (buflen <= 0) { g_free(buffer); 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. */ purple_cipher_context_reset(context1, NULL); purple_cipher_context_digest(context1, sizeof(digest), digest, NULL); purple_cipher_context_destroy(context1); purple_cipher_context_destroy(context2); return buffer; }
NOINLINE sha_crypt(/*const*/ char *key_data, /*const*/ char *salt_data) { void (*sha_begin)(void *ctx) FAST_FUNC; void (*sha_hash)(void *ctx, const void *buffer, size_t len) FAST_FUNC; void (*sha_end)(void *ctx, void *resbuf) FAST_FUNC; int _32or64; char *result, *resptr; /* btw, sha256 needs [32] and uint32_t only */ struct { unsigned char alt_result[64]; unsigned char temp_result[64]; union { sha256_ctx_t x; sha512_ctx_t y; } ctx; union { sha256_ctx_t x; sha512_ctx_t y; } alt_ctx; } L __attribute__((__aligned__(__alignof__(uint64_t)))); #define alt_result (L.alt_result ) #define temp_result (L.temp_result) #define ctx (L.ctx ) #define alt_ctx (L.alt_ctx ) unsigned salt_len; unsigned key_len; unsigned cnt; unsigned rounds; char *cp; char is_sha512; /* Analyze salt, construct already known part of result */ cnt = strlen(salt_data) + 1 + 43 + 1; is_sha512 = salt_data[1]; if (is_sha512 == '6') cnt += 43; result = resptr = xzalloc(cnt); /* will provide NUL terminator */ *resptr++ = '$'; *resptr++ = is_sha512; *resptr++ = '$'; rounds = ROUNDS_DEFAULT; salt_data += 3; if (strncmp(salt_data, str_rounds, 7) == 0) { /* 7 == strlen("rounds=") */ char *endp; cnt = bb_strtou(salt_data + 7, &endp, 10); if (*endp == '$') { salt_data = endp + 1; rounds = cnt; if (rounds < ROUNDS_MIN) rounds = ROUNDS_MIN; if (rounds > ROUNDS_MAX) rounds = ROUNDS_MAX; /* add "rounds=NNNNN$" to result */ resptr += sprintf(resptr, str_rounds, rounds); } } salt_len = strchrnul(salt_data, '$') - salt_data; if (salt_len > SALT_LEN_MAX) salt_len = SALT_LEN_MAX; /* xstrdup assures suitable alignment; also we will use it as a scratch space later. */ salt_data = xstrndup(salt_data, salt_len); /* add "salt$" to result */ strcpy(resptr, salt_data); resptr += salt_len; *resptr++ = '$'; /* key data doesn't need much processing */ key_len = strlen(key_data); key_data = xstrdup(key_data); /* Which flavor of SHAnnn ops to use? */ sha_begin = (void*)sha256_begin; sha_hash = (void*)sha256_hash; sha_end = (void*)sha256_end; _32or64 = 32; if (is_sha512 == '6') { sha_begin = (void*)sha512_begin; sha_hash = (void*)sha512_hash; sha_end = (void*)sha512_end; _32or64 = 64; } /* Add KEY, SALT. */ sha_begin(&ctx); sha_hash(&ctx, key_data, key_len); sha_hash(&ctx, salt_data, salt_len); /* Compute alternate SHA sum with input KEY, SALT, and KEY. The final result will be added to the first context. */ sha_begin(&alt_ctx); sha_hash(&alt_ctx, key_data, key_len); sha_hash(&alt_ctx, salt_data, salt_len); sha_hash(&alt_ctx, key_data, key_len); sha_end(&alt_ctx, alt_result); /* Add result of this to the other context. */ /* Add for any character in the key one byte of the alternate sum. */ for (cnt = key_len; cnt > _32or64; cnt -= _32or64) sha_hash(&ctx, alt_result, _32or64); sha_hash(&ctx, alt_result, cnt); /* 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) sha_hash(&ctx, alt_result, _32or64); else sha_hash(&ctx, key_data, key_len); /* Create intermediate result. */ sha_end(&ctx, alt_result); /* Start computation of P byte sequence. */ /* For every character in the password add the entire password. */ sha_begin(&alt_ctx); for (cnt = 0; cnt < key_len; ++cnt) sha_hash(&alt_ctx, key_data, key_len); sha_end(&alt_ctx, temp_result); /* NB: past this point, raw key_data is not used anymore */ /* Create byte sequence P. */ #define p_bytes key_data /* reuse the buffer as it is of the key_len size */ cp = p_bytes; /* was: ... = alloca(key_len); */ for (cnt = key_len; cnt >= _32or64; cnt -= _32or64) { cp = memcpy(cp, temp_result, _32or64); cp += _32or64; } memcpy(cp, temp_result, cnt); /* Start computation of S byte sequence. */ /* For every character in the password add the entire password. */ sha_begin(&alt_ctx); for (cnt = 0; cnt < 16 + alt_result[0]; ++cnt) sha_hash(&alt_ctx, salt_data, salt_len); sha_end(&alt_ctx, temp_result); /* NB: past this point, raw salt_data is not used anymore */ /* Create byte sequence S. */ #define s_bytes salt_data /* reuse the buffer as it is of the salt_len size */ cp = s_bytes; /* was: ... = alloca(salt_len); */ for (cnt = salt_len; cnt >= _32or64; cnt -= _32or64) { cp = memcpy(cp, temp_result, _32or64); cp += _32or64; } memcpy(cp, temp_result, cnt); /* Repeatedly run the collected hash value through SHA to burn CPU cycles. */ for (cnt = 0; cnt < rounds; ++cnt) { sha_begin(&ctx); /* Add key or last result. */ if ((cnt & 1) != 0) sha_hash(&ctx, p_bytes, key_len); else sha_hash(&ctx, alt_result, _32or64); /* Add salt for numbers not divisible by 3. */ if (cnt % 3 != 0) sha_hash(&ctx, s_bytes, salt_len); /* Add key for numbers not divisible by 7. */ if (cnt % 7 != 0) sha_hash(&ctx, p_bytes, key_len); /* Add key or last result. */ if ((cnt & 1) != 0) sha_hash(&ctx, alt_result, _32or64); else sha_hash(&ctx, p_bytes, key_len); sha_end(&ctx, alt_result); } /* Append encrypted password to result buffer */ //TODO: replace with something like // bb_uuencode(cp, src, length, bb_uuenc_tbl_XXXbase64); #define b64_from_24bit(B2, B1, B0, N) \ do { \ unsigned w = ((B2) << 16) | ((B1) << 8) | (B0); \ resptr = to64(resptr, w, N); \ } while (0) if (is_sha512 == '5') { unsigned i = 0; while (1) { unsigned j = i + 10; unsigned k = i + 20; if (j >= 30) j -= 30; if (k >= 30) k -= 30; b64_from_24bit(alt_result[i], alt_result[j], alt_result[k], 4); if (k == 29) break; i = k + 1; } b64_from_24bit(0, alt_result[31], alt_result[30], 3); /* was: b64_from_24bit(alt_result[0], alt_result[10], alt_result[20], 4); b64_from_24bit(alt_result[21], alt_result[1], alt_result[11], 4); b64_from_24bit(alt_result[12], alt_result[22], alt_result[2], 4); b64_from_24bit(alt_result[3], alt_result[13], alt_result[23], 4); b64_from_24bit(alt_result[24], alt_result[4], alt_result[14], 4); b64_from_24bit(alt_result[15], alt_result[25], alt_result[5], 4); b64_from_24bit(alt_result[6], alt_result[16], alt_result[26], 4); b64_from_24bit(alt_result[27], alt_result[7], alt_result[17], 4); b64_from_24bit(alt_result[18], alt_result[28], alt_result[8], 4); b64_from_24bit(alt_result[9], alt_result[19], alt_result[29], 4); b64_from_24bit(0, alt_result[31], alt_result[30], 3); */ } else { unsigned i = 0; while (1) { unsigned j = i + 21; unsigned k = i + 42; if (j >= 63) j -= 63; if (k >= 63) k -= 63; b64_from_24bit(alt_result[i], alt_result[j], alt_result[k], 4); if (j == 20) break; i = j + 1; } b64_from_24bit(0, 0, alt_result[63], 2); /* was: b64_from_24bit(alt_result[0], alt_result[21], alt_result[42], 4); b64_from_24bit(alt_result[22], alt_result[43], alt_result[1], 4); b64_from_24bit(alt_result[44], alt_result[2], alt_result[23], 4); b64_from_24bit(alt_result[3], alt_result[24], alt_result[45], 4); b64_from_24bit(alt_result[25], alt_result[46], alt_result[4], 4); b64_from_24bit(alt_result[47], alt_result[5], alt_result[26], 4); b64_from_24bit(alt_result[6], alt_result[27], alt_result[48], 4); b64_from_24bit(alt_result[28], alt_result[49], alt_result[7], 4); b64_from_24bit(alt_result[50], alt_result[8], alt_result[29], 4); b64_from_24bit(alt_result[9], alt_result[30], alt_result[51], 4); b64_from_24bit(alt_result[31], alt_result[52], alt_result[10], 4); b64_from_24bit(alt_result[53], alt_result[11], alt_result[32], 4); b64_from_24bit(alt_result[12], alt_result[33], alt_result[54], 4); b64_from_24bit(alt_result[34], alt_result[55], alt_result[13], 4); b64_from_24bit(alt_result[56], alt_result[14], alt_result[35], 4); b64_from_24bit(alt_result[15], alt_result[36], alt_result[57], 4); b64_from_24bit(alt_result[37], alt_result[58], alt_result[16], 4); b64_from_24bit(alt_result[59], alt_result[17], alt_result[38], 4); b64_from_24bit(alt_result[18], alt_result[39], alt_result[60], 4); b64_from_24bit(alt_result[40], alt_result[61], alt_result[19], 4); b64_from_24bit(alt_result[62], alt_result[20], alt_result[41], 4); b64_from_24bit(0, 0, alt_result[63], 2); */ } /* *resptr = '\0'; - xzalloc did it */ #undef b64_from_24bit /* Clear the buffer for the intermediate result so that people attaching to processes or reading core dumps cannot get any information. */ memset(&L, 0, sizeof(L)); /* [alt]_ctx and XXX_result buffers */ memset(key_data, 0, key_len); /* also p_bytes */ memset(salt_data, 0, salt_len); /* also s_bytes */ free(key_data); free(salt_data); #undef p_bytes #undef s_bytes return result; #undef alt_result #undef temp_result #undef ctx #undef alt_ctx }
char* crypt_sha512_r(const char *key, const char *salt, size_t rounds, char *buffer, int buflen) { uint8_t alt_result[64], temp_result[64]; sha4_context ctx, alt_ctx; size_t salt_len, key_len, cnt; char *cp, *copied_key, *copied_salt, *p_bytes, *s_bytes; copied_key = NULL; copied_salt = NULL; salt_len = MIN(strcspn(salt, "$"), SALT_LEN_MAX); key_len = strlen(key); /* Prepare for the real work. */ sha4_starts(&ctx, 0); /* Add the key string. */ sha4_update(&ctx, (unsigned char*)key, key_len); /* 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). */ sha4_update(&ctx, (unsigned char*)salt, salt_len); /* Compute alternate SHA512 sum with input KEY, SALT, and KEY. The * final result will be added to the first context. */ sha4_starts(&alt_ctx, 0); /* Add key. */ sha4_update(&alt_ctx, (unsigned char*)key, key_len); /* Add salt. */ sha4_update(&alt_ctx, (unsigned char*)salt, salt_len); /* Add key again. */ sha4_update(&alt_ctx, (unsigned char*)key, key_len); /* Now get result of this (64 bytes) and add it to the other context. */ sha4_finish(&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) sha4_update(&ctx, (unsigned char*)alt_result, 64); sha4_update(&ctx, (unsigned char*)alt_result, cnt); /* 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) sha4_update(&ctx, (unsigned char*)alt_result, 64); else sha4_update(&ctx, (unsigned char*)key, key_len); /* Create intermediate result. */ sha4_finish(&ctx, alt_result); /* Start computation of P byte sequence. */ sha4_starts(&alt_ctx, 0); /* For every character in the password add the entire password. */ for (cnt = 0; cnt < key_len; ++cnt) sha4_update(&alt_ctx, (unsigned char*)key, key_len); /* Finish the digest. */ sha4_finish(&alt_ctx, temp_result); /* Create byte sequence P. */ cp = p_bytes = alloca(key_len); for (cnt = key_len; cnt >= 64; cnt -= 64) { memcpy(cp, temp_result, 64); cp += 64; } memcpy(cp, temp_result, cnt); /* Start computation of S byte sequence. */ sha4_starts(&alt_ctx, 0); /* For every character in the password add the entire password. */ for (cnt = 0; cnt < 16 + alt_result[0]; ++cnt) sha4_update(&alt_ctx, (unsigned char*)salt, salt_len); /* Finish the digest. */ sha4_finish(&alt_ctx, temp_result); /* Create byte sequence S. */ cp = s_bytes = alloca(salt_len); for (cnt = salt_len; cnt >= 64; cnt -= 64) { memcpy(cp, temp_result, 64); cp += 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. */ sha4_starts(&ctx, 0); /* Add key or last result. */ if ((cnt & 1) != 0) sha4_update(&ctx, (unsigned char*)p_bytes, key_len); else sha4_update(&ctx, (unsigned char*)alt_result, 64); /* Add salt for numbers not divisible by 3. */ if (cnt % 3 != 0) sha4_update(&ctx, (unsigned char*)s_bytes, salt_len); /* Add key for numbers not divisible by 7. */ if (cnt % 7 != 0) sha4_update(&ctx, (unsigned char*)p_bytes, key_len); /* Add key or last result. */ if ((cnt & 1) != 0) sha4_update(&ctx, (unsigned char*)alt_result, 64); else sha4_update(&ctx, (unsigned char*)p_bytes, key_len); /* Create intermediate result. */ sha4_finish(&ctx, alt_result); } cp = buffer; b64_from_24bit(alt_result[0], alt_result[21], alt_result[42], 4, &buflen, &cp); b64_from_24bit(alt_result[22], alt_result[43], alt_result[1], 4, &buflen, &cp); b64_from_24bit(alt_result[44], alt_result[2], alt_result[23], 4, &buflen, &cp); b64_from_24bit(alt_result[3], alt_result[24], alt_result[45], 4, &buflen, &cp); b64_from_24bit(alt_result[25], alt_result[46], alt_result[4], 4, &buflen, &cp); b64_from_24bit(alt_result[47], alt_result[5], alt_result[26], 4, &buflen, &cp); b64_from_24bit(alt_result[6], alt_result[27], alt_result[48], 4, &buflen, &cp); b64_from_24bit(alt_result[28], alt_result[49], alt_result[7], 4, &buflen, &cp); b64_from_24bit(alt_result[50], alt_result[8], alt_result[29], 4, &buflen, &cp); b64_from_24bit(alt_result[9], alt_result[30], alt_result[51], 4, &buflen, &cp); b64_from_24bit(alt_result[31], alt_result[52], alt_result[10], 4, &buflen, &cp); b64_from_24bit(alt_result[53], alt_result[11], alt_result[32], 4, &buflen, &cp); b64_from_24bit(alt_result[12], alt_result[33], alt_result[54], 4, &buflen, &cp); b64_from_24bit(alt_result[34], alt_result[55], alt_result[13], 4, &buflen, &cp); b64_from_24bit(alt_result[56], alt_result[14], alt_result[35], 4, &buflen, &cp); b64_from_24bit(alt_result[15], alt_result[36], alt_result[57], 4, &buflen, &cp); b64_from_24bit(alt_result[37], alt_result[58], alt_result[16], 4, &buflen, &cp); b64_from_24bit(alt_result[59], alt_result[17], alt_result[38], 4, &buflen, &cp); b64_from_24bit(alt_result[18], alt_result[39], alt_result[60], 4, &buflen, &cp); b64_from_24bit(alt_result[40], alt_result[61], alt_result[19], 4, &buflen, &cp); b64_from_24bit(alt_result[62], alt_result[20], alt_result[41], 4, &buflen, &cp); b64_from_24bit(0, 0, alt_result[63], 2, &buflen, &cp); if (buflen <= 0) { errno = ERANGE; buffer = NULL; } else *cp = '\0'; return buffer; }