diff --git a/include/convert.h b/include/convert.h index c6be2bdd1..26f8f7aa6 100644 --- a/include/convert.h +++ b/include/convert.h @@ -14,12 +14,14 @@ size_t exec_unhexify (const u8 *in_buf, const size_t in_len, u8 *out_buf, const bool need_hexify (const u8 *buf, const size_t len, const char separator, bool always_ascii); void exec_hexify (const u8 *buf, const size_t len, u8 *out); -bool is_valid_base64_string (const u8 *s, const size_t len); -bool is_valid_base64_char (const u8 c); -bool is_valid_hex_string (const u8 *s, const size_t len); -bool is_valid_hex_char (const u8 c); -bool is_valid_digit_string (const u8 *s, const size_t len); -bool is_valid_digit_char (const u8 c); +bool is_valid_base64a_string (const u8 *s, const size_t len); +bool is_valid_base64a_char (const u8 c); +bool is_valid_base64b_string (const u8 *s, const size_t len); +bool is_valid_base64b_char (const u8 c); +bool is_valid_hex_string (const u8 *s, const size_t len); +bool is_valid_hex_char (const u8 c); +bool is_valid_digit_string (const u8 *s, const size_t len); +bool is_valid_digit_char (const u8 c); u8 hex_convert (const u8 c); diff --git a/include/interface.h b/include/interface.h index 375e604be..0fc779e5e 100644 --- a/include/interface.h +++ b/include/interface.h @@ -1046,11 +1046,7 @@ typedef enum display_len DISPLAY_LEN_MAX_200 = 16, DISPLAY_LEN_MIN_300 = 40, DISPLAY_LEN_MAX_300 = 40, - DISPLAY_LEN_MIN_400 = 34, - DISPLAY_LEN_MAX_400 = 34, - DISPLAY_LEN_MIN_500 = 3 + 1 + 0 + 22, DISPLAY_LEN_MIN_501 = 104, - DISPLAY_LEN_MAX_500 = 3 + 1 + 8 + 22, DISPLAY_LEN_MAX_501 = 104, DISPLAY_LEN_MIN_600 = 8 + 128, DISPLAY_LEN_MAX_600 = 8 + 128, @@ -1078,8 +1074,6 @@ typedef enum display_len DISPLAY_LEN_MAX_1450H = 64 + 1 + (SALT_MAX * 2), DISPLAY_LEN_MIN_1500 = 13, DISPLAY_LEN_MAX_1500 = 13, - DISPLAY_LEN_MIN_1600 = 29 + 0, - DISPLAY_LEN_MAX_1600 = 29 + 8, DISPLAY_LEN_MIN_1700 = 128, DISPLAY_LEN_MAX_1700 = 128, DISPLAY_LEN_MIN_1710 = 128 + 1 + 0, @@ -1361,8 +1355,6 @@ typedef enum display_len DISPLAY_LEN_MAX_132 = 6 + 8 + 40, DISPLAY_LEN_MIN_133 = 28, DISPLAY_LEN_MAX_133 = 28, - DISPLAY_LEN_MIN_141 = 14 + 0 + 1 + 28, - DISPLAY_LEN_MAX_141 = 14 + 44 + 1 + 28, DISPLAY_LEN_MIN_1411 = 9 + 44 + 0, DISPLAY_LEN_MAX_1411 = 9 + 44 + 68, DISPLAY_LEN_MIN_1441 = 14 + 0 + 1 + 43, diff --git a/include/types.h b/include/types.h index a5df083df..38a04e063 100644 --- a/include/types.h +++ b/include/types.h @@ -686,11 +686,13 @@ typedef enum user_options_map typedef enum token_attr { TOKEN_ATTR_FIXED_LENGTH = 1 << 0, - TOKEN_ATTR_VERIFY_SIGNATURE = 1 << 1, - TOKEN_ATTR_VERIFY_LENGTH = 1 << 2, - TOKEN_ATTR_VERIFY_DIGIT = 1 << 3, - TOKEN_ATTR_VERIFY_HEX = 1 << 4, - TOKEN_ATTR_VERIFY_BASE64 = 1 << 5, + TOKEN_ATTR_OPTIONAL_ROUNDS = 1 << 1, + TOKEN_ATTR_VERIFY_SIGNATURE = 1 << 2, + TOKEN_ATTR_VERIFY_LENGTH = 1 << 3, + TOKEN_ATTR_VERIFY_DIGIT = 1 << 4, + TOKEN_ATTR_VERIFY_HEX = 1 << 5, + TOKEN_ATTR_VERIFY_BASE64A = 1 << 6, + TOKEN_ATTR_VERIFY_BASE64B = 1 << 7, } token_attr_t; @@ -2102,6 +2104,9 @@ typedef struct token int attr[MAX_TOKENS]; + u8 *opt_buf; + int opt_len; + } token_t; #endif // _TYPES_H diff --git a/src/convert.c b/src/convert.c index 402935bbe..d12c20282 100644 --- a/src/convert.c +++ b/src/convert.c @@ -215,19 +215,44 @@ void exec_hexify (const u8 *buf, const size_t len, u8 *out) out[max_len * 2] = 0; } -bool is_valid_base64_string (const u8 *s, const size_t len) +bool is_valid_base64a_string (const u8 *s, const size_t len) { for (size_t i = 0; i < len; i++) { const u8 c = s[i]; - if (is_valid_base64_char (c) == false) return false; + if (is_valid_base64a_char (c) == false) return false; } return true; } -bool is_valid_base64_char (const u8 c) +bool is_valid_base64a_char (const u8 c) +{ + if ((c >= '0') && (c <= '9')) return true; + if ((c >= 'A') && (c <= 'Z')) return true; + if ((c >= 'a') && (c <= 'z')) return true; + + if (c == '+') return true; + if (c == '/') return true; + if (c == '=') return true; + + return false; +} + +bool is_valid_base64b_string (const u8 *s, const size_t len) +{ + for (size_t i = 0; i < len; i++) + { + const u8 c = s[i]; + + if (is_valid_base64b_char (c) == false) return false; + } + + return true; +} + +bool is_valid_base64b_char (const u8 c) { if ((c >= '0') && (c <= '9')) return true; if ((c >= 'A') && (c <= 'Z')) return true; @@ -235,6 +260,7 @@ bool is_valid_base64_char (const u8 c) if (c == '.') return true; if (c == '/') return true; + if (c == '=') return true; return false; } diff --git a/src/interface.c b/src/interface.c index ff107990a..91566fdd8 100644 --- a/src/interface.c +++ b/src/interface.c @@ -603,7 +603,8 @@ static const char *SIGNATURE_DPAPIMK = "$DPAPImk$"; static const char *SIGNATURE_DRUPAL7 = "$S$"; static const char *SIGNATURE_ECRYPTFS = "$ecryptfs$"; static const char *SIGNATURE_EPISERVER4 = "$episerver$*1*"; -static const char *SIGNATURE_EPISERVER = "$episerver$*0*"; +static const char *SIGNATURE_EPISERVER0 = "$episerver$*0*"; +static const char *SIGNATURE_EPISERVER = "$episerver$"; static const char *SIGNATURE_KEEPASS = "$keepass$"; static const char *SIGNATURE_KRB5PA = "$krb5pa$23"; static const char *SIGNATURE_KRB5TGS = "$krb5tgs$23"; @@ -630,8 +631,6 @@ static const char *SIGNATURE_PBKDF2_SHA1 = "sha1:"; static const char *SIGNATURE_PBKDF2_SHA256 = "sha256:"; static const char *SIGNATURE_PBKDF2_SHA512 = "sha512:"; static const char *SIGNATURE_PDF = "$pdf$"; -static const char *SIGNATURE_PHPASS1 = "$P$"; -static const char *SIGNATURE_PHPASS2 = "$H$"; static const char *SIGNATURE_PHPS = "$PHPS$"; static const char *SIGNATURE_POSTGRESQL_AUTH = "$postgres$"; static const char *SIGNATURE_PSAFE3 = "PWS3"; @@ -2452,6 +2451,27 @@ static void drupal7_encode (const u8 digest[64], u8 buf[43]) * parser */ +static int rounds_count_length (const char *input_buf, const int input_len) +{ + if (input_len >= 9) // 9 is minimum because of "rounds=X$" + { + static const char *rounds = "rounds="; + + if (memcmp (input_buf, rounds, 7) == 0) + { + char *next_pos = strchr (input_buf + 8, '$'); + + if (next_pos == NULL) return -1; + + const int rounds_len = next_pos - input_buf; + + return rounds_len; + } + } + + return -1; +} + static int input_tokenizer (u8 *input_buf, int input_len, token_t *token) { int len_left = input_len; @@ -2474,6 +2494,22 @@ static int input_tokenizer (u8 *input_buf, int input_len, token_t *token) } else { + if (token->attr[token_idx] & TOKEN_ATTR_OPTIONAL_ROUNDS) + { + const int len = rounds_count_length (token->buf[token_idx], len_left); + + token->opt_buf = token->buf[token_idx]; + + token->opt_len = len; // we want an eventual -1 in here, it's used later for verification + + if (len > 0) + { + token->buf[token_idx] += len + 1; // +1 = separator + + len_left -= len + 1; // +1 = separator + } + } + u8 *next_pos = (u8 *) strchr ((const char *) token->buf[token_idx], token->sep[token_idx]); if (next_pos == NULL) return (PARSER_SEPARATOR_UNMATCHED); @@ -2510,9 +2546,14 @@ static int input_tokenizer (u8 *input_buf, int input_len, token_t *token) if (is_valid_hex_string (token->buf[token_idx], token->len[token_idx]) == false) return (PARSER_TOKEN_ENCODING); } - if (token->attr[token_idx] & TOKEN_ATTR_VERIFY_BASE64) + if (token->attr[token_idx] & TOKEN_ATTR_VERIFY_BASE64A) { - if (is_valid_base64_string (token->buf[token_idx], token->len[token_idx]) == false) return (PARSER_TOKEN_ENCODING); + if (is_valid_base64a_string (token->buf[token_idx], token->len[token_idx]) == false) return (PARSER_TOKEN_ENCODING); + } + + if (token->attr[token_idx] & TOKEN_ATTR_VERIFY_BASE64B) + { + if (is_valid_base64b_string (token->buf[token_idx], token->len[token_idx]) == false) return (PARSER_TOKEN_ENCODING); } } @@ -2803,11 +2844,11 @@ int bcrypt_parse_hash (u8 *input_buf, u32 input_len, hash_t *hash_buf, MAYBE_UNU token.len[2] = 22; token.attr[2] = TOKEN_ATTR_FIXED_LENGTH - | TOKEN_ATTR_VERIFY_BASE64; + | TOKEN_ATTR_VERIFY_BASE64B; token.len[3] = 31; token.attr[3] = TOKEN_ATTR_FIXED_LENGTH - | TOKEN_ATTR_VERIFY_BASE64; + | TOKEN_ATTR_VERIFY_BASE64B; const int rc_tokenizer = input_tokenizer (input_buf, input_len, &token); @@ -2867,7 +2908,7 @@ int cisco4_parse_hash (u8 *input_buf, u32 input_len, hash_t *hash_buf, MAYBE_UNU token.len_min[0] = 43; token.len_max[0] = 43; token.attr[0] = TOKEN_ATTR_VERIFY_LENGTH - | TOKEN_ATTR_VERIFY_BASE64; + | TOKEN_ATTR_VERIFY_BASE64B; const int rc_tokenizer = input_tokenizer (input_buf, input_len, &token); @@ -3176,7 +3217,7 @@ int netscreen_parse_hash (u8 *input_buf, u32 input_len, hash_t *hash_buf, MAYBE_ token.len_min[0] = 30; token.len_max[0] = 30; token.attr[0] = TOKEN_ATTR_VERIFY_LENGTH - | TOKEN_ATTR_VERIFY_BASE64; + | TOKEN_ATTR_VERIFY_BASE64B; token.len_min[1] = 1; token.len_max[1] = SALT_MAX; @@ -3933,33 +3974,49 @@ int psafe3_parse_hash (u8 *input_buf, u32 input_len, hash_t *hash_buf, MAYBE_UNU int phpass_parse_hash (u8 *input_buf, u32 input_len, hash_t *hash_buf, MAYBE_UNUSED hashconfig_t *hashconfig) { - if ((input_len < DISPLAY_LEN_MIN_400) || (input_len > DISPLAY_LEN_MAX_400)) return (PARSER_GLOBAL_LENGTH); - - if ((memcmp (SIGNATURE_PHPASS1, input_buf, 3) != 0) && (memcmp (SIGNATURE_PHPASS2, input_buf, 3) != 0)) return (PARSER_SIGNATURE_UNMATCHED); - u32 *digest = (u32 *) hash_buf->digest; salt_t *salt = hash_buf->salt; - u8 *iter_pos = input_buf + 3; + token_t token; + + token.token_cnt = 4; + + token.len[0] = 3; + token.attr[0] = TOKEN_ATTR_FIXED_LENGTH; + + token.len[1] = 1; + token.attr[1] = TOKEN_ATTR_FIXED_LENGTH; + + token.len[2] = 8; + token.attr[2] = TOKEN_ATTR_FIXED_LENGTH; + + token.len[3] = 22; + token.attr[3] = TOKEN_ATTR_FIXED_LENGTH + | TOKEN_ATTR_VERIFY_BASE64B; + + const int rc_tokenizer = input_tokenizer (input_buf, input_len, &token); + + if (rc_tokenizer != PARSER_OK) return (rc_tokenizer); + + memcpy ((u8 *) salt->salt_sign, input_buf, 4); + + u8 *iter_pos = token.buf[1]; u32 salt_iter = 1u << itoa64_to_int (iter_pos[0]); if (salt_iter > 0x80000000) return (PARSER_SALT_ITERATION); - memcpy ((u8 *) salt->salt_sign, input_buf, 4); - salt->salt_iter = salt_iter; - u8 *salt_pos = iter_pos + 1; - - u32 salt_len = 8; + u8 *salt_pos = token.buf[2]; + int salt_len = token.len[2]; memcpy ((u8 *) salt->salt_buf, salt_pos, salt_len); salt->salt_len = salt_len; - u8 *hash_pos = salt_pos + salt_len; + u8 *hash_pos = token.buf[3]; phpass_decode ((u8 *) digest, hash_pos); @@ -3968,59 +4025,48 @@ int phpass_parse_hash (u8 *input_buf, u32 input_len, hash_t *hash_buf, MAYBE_UNU int md5crypt_parse_hash (u8 *input_buf, u32 input_len, hash_t *hash_buf, MAYBE_UNUSED hashconfig_t *hashconfig) { - if (input_len < DISPLAY_LEN_MIN_500) return (PARSER_GLOBAL_LENGTH); - - if (memcmp (SIGNATURE_MD5CRYPT, input_buf, 3) != 0) return (PARSER_SIGNATURE_UNMATCHED); - u32 *digest = (u32 *) hash_buf->digest; salt_t *salt = hash_buf->salt; - u8 *salt_pos = input_buf + 3; + token_t token; - u32 iterations_len = 0; + token.token_cnt = 3; + token.signature = SIGNATURE_MD5CRYPT; - if (memcmp (salt_pos, "rounds=", 7) == 0) + token.len[0] = 3; + token.attr[0] = TOKEN_ATTR_FIXED_LENGTH + | TOKEN_ATTR_VERIFY_SIGNATURE; + + token.len_min[1] = 0; + token.len_max[1] = 8; + token.sep[1] = '$'; + token.attr[1] = TOKEN_ATTR_VERIFY_LENGTH + | TOKEN_ATTR_OPTIONAL_ROUNDS; + + token.len[2] = 22; + token.attr[2] = TOKEN_ATTR_FIXED_LENGTH + | TOKEN_ATTR_VERIFY_BASE64B; + + const int rc_tokenizer = input_tokenizer (input_buf, input_len, &token); + + if (rc_tokenizer != PARSER_OK) return (rc_tokenizer); + + salt->salt_iter = ROUNDS_MD5CRYPT; + + if (token.opt_len != -1) { - salt_pos += 7; - - for (iterations_len = 0; salt_pos[0] >= '0' && salt_pos[0] <= '9' && iterations_len < 7; iterations_len++, salt_pos += 1) continue; - - if (iterations_len == 0 ) return (PARSER_SALT_ITERATION); - if (salt_pos[0] != '$') return (PARSER_SIGNATURE_UNMATCHED); - - salt_pos[0] = 0x0; - - salt->salt_iter = hc_strtoul ((const char *) (salt_pos - iterations_len), NULL, 10); - - salt_pos += 1; - - iterations_len += 8; - } - else - { - salt->salt_iter = ROUNDS_MD5CRYPT; + salt->salt_iter = hc_strtoul ((const char *) token.opt_buf + 7, NULL, 10); // 7 = "rounds=" } - if (input_len > (DISPLAY_LEN_MAX_500 + iterations_len)) return (PARSER_GLOBAL_LENGTH); + u8 *salt_pos = token.buf[1]; + int salt_len = token.len[1]; - u8 *hash_pos = (u8 *) strchr ((const char *) salt_pos, '$'); + const bool parse_rc = parse_and_store_generic_salt ((u8 *) salt->salt_buf, (int *) &salt->salt_len, salt_pos, salt_len, hashconfig); - if (hash_pos == NULL) return (PARSER_SEPARATOR_UNMATCHED); + if (parse_rc == false) return (PARSER_SALT_LENGTH); - u32 salt_len = hash_pos - salt_pos; - - if (salt_len > 8) return (PARSER_SALT_LENGTH); - - memcpy ((u8 *) salt->salt_buf, salt_pos, salt_len); - - salt->salt_len = salt_len; - - hash_pos++; - - u32 hash_len = input_len - 3 - iterations_len - salt_len - 1; - - if (hash_len != 22) return (PARSER_HASH_LENGTH); + u8 *hash_pos = token.buf[2]; md5crypt_decode ((u8 *) digest, hash_pos); @@ -4029,53 +4075,48 @@ int md5crypt_parse_hash (u8 *input_buf, u32 input_len, hash_t *hash_buf, MAYBE_U int md5apr1_parse_hash (u8 *input_buf, u32 input_len, hash_t *hash_buf, MAYBE_UNUSED hashconfig_t *hashconfig) { - if (memcmp (SIGNATURE_MD5APR1, input_buf, 6) != 0) return (PARSER_SIGNATURE_UNMATCHED); - u32 *digest = (u32 *) hash_buf->digest; salt_t *salt = hash_buf->salt; - u8 *salt_pos = input_buf + 6; + token_t token; - u32 iterations_len = 0; + token.token_cnt = 3; + token.signature = SIGNATURE_MD5APR1; - if (memcmp (salt_pos, "rounds=", 7) == 0) + token.len[0] = 6; + token.attr[0] = TOKEN_ATTR_FIXED_LENGTH + | TOKEN_ATTR_VERIFY_SIGNATURE; + + token.len_min[1] = 0; + token.len_max[1] = 8; + token.sep[1] = '$'; + token.attr[1] = TOKEN_ATTR_VERIFY_LENGTH + | TOKEN_ATTR_OPTIONAL_ROUNDS; + + token.len[2] = 22; + token.attr[2] = TOKEN_ATTR_FIXED_LENGTH + | TOKEN_ATTR_VERIFY_BASE64B; + + const int rc_tokenizer = input_tokenizer (input_buf, input_len, &token); + + if (rc_tokenizer != PARSER_OK) return (rc_tokenizer); + + salt->salt_iter = ROUNDS_MD5CRYPT; + + if (token.opt_len != -1) { - salt_pos += 7; - - for (iterations_len = 0; salt_pos[0] >= '0' && salt_pos[0] <= '9' && iterations_len < 7; iterations_len++, salt_pos += 1) continue; - - if (iterations_len == 0 ) return (PARSER_SALT_ITERATION); - if (salt_pos[0] != '$') return (PARSER_SIGNATURE_UNMATCHED); - - salt_pos[0] = 0x0; - - salt->salt_iter = hc_strtoul ((const char *) (salt_pos - iterations_len), NULL, 10); - - salt_pos += 1; - - iterations_len += 8; - } - else - { - salt->salt_iter = ROUNDS_MD5CRYPT; + salt->salt_iter = hc_strtoul ((const char *) token.opt_buf + 7, NULL, 10); // 7 = "rounds=" } - if ((input_len < DISPLAY_LEN_MIN_1600) || (input_len > DISPLAY_LEN_MAX_1600 + iterations_len)) return (PARSER_GLOBAL_LENGTH); + u8 *salt_pos = token.buf[1]; + int salt_len = token.len[1]; - u8 *hash_pos = (u8 *) strchr ((const char *) salt_pos, '$'); + const bool parse_rc = parse_and_store_generic_salt ((u8 *) salt->salt_buf, (int *) &salt->salt_len, salt_pos, salt_len, hashconfig); - if (hash_pos == NULL) return (PARSER_SEPARATOR_UNMATCHED); + if (parse_rc == false) return (PARSER_SALT_LENGTH); - u32 salt_len = hash_pos - salt_pos; - - if (salt_len > 8) return (PARSER_SALT_LENGTH); - - memcpy ((u8 *) salt->salt_buf, salt_pos, salt_len); - - salt->salt_len = salt_len; - - hash_pos++; + u8 *hash_pos = token.buf[2]; md5crypt_decode ((u8 *) digest, hash_pos); @@ -4084,35 +4125,48 @@ int md5apr1_parse_hash (u8 *input_buf, u32 input_len, hash_t *hash_buf, MAYBE_UN int episerver_parse_hash (u8 *input_buf, u32 input_len, hash_t *hash_buf, MAYBE_UNUSED hashconfig_t *hashconfig) { - if ((input_len < DISPLAY_LEN_MIN_141) || (input_len > DISPLAY_LEN_MAX_141)) return (PARSER_GLOBAL_LENGTH); - - if (memcmp (SIGNATURE_EPISERVER, input_buf, 14) != 0) return (PARSER_SIGNATURE_UNMATCHED); - u32 *digest = (u32 *) hash_buf->digest; salt_t *salt = hash_buf->salt; - u8 *salt_pos = input_buf + 14; + token_t token; - u8 *hash_pos = (u8 *) strchr ((const char *) salt_pos, '*'); + token.token_cnt = 4; + token.signature = SIGNATURE_EPISERVER; - if (hash_pos == NULL) return (PARSER_SEPARATOR_UNMATCHED); + token.len_min[0] = 11; + token.len_max[0] = 11; + token.sep[0] = '*'; + token.attr[0] = TOKEN_ATTR_VERIFY_LENGTH + | TOKEN_ATTR_VERIFY_SIGNATURE; - hash_pos++; + token.len_min[1] = 1; + token.len_max[1] = 6; + token.sep[1] = '*'; + token.attr[1] = TOKEN_ATTR_VERIFY_LENGTH + | TOKEN_ATTR_VERIFY_DIGIT; - u32 salt_len = hash_pos - salt_pos - 1; + token.len_min[2] = 0; + token.len_max[2] = 44; + token.sep[2] = '*'; + token.attr[2] = TOKEN_ATTR_VERIFY_LENGTH + | TOKEN_ATTR_VERIFY_BASE64A; - u8 *salt_buf_ptr = (u8 *) salt->salt_buf; + token.len_min[3] = 27; + token.len_max[3] = 27; + token.attr[3] = TOKEN_ATTR_VERIFY_LENGTH + | TOKEN_ATTR_VERIFY_BASE64A; - salt_len = parse_and_store_salt_legacy (salt_buf_ptr, salt_pos, salt_len, hashconfig); + const int rc_tokenizer = input_tokenizer (input_buf, input_len, &token); - if (salt_len == UINT_MAX) return (PARSER_SALT_LENGTH); + if (rc_tokenizer != PARSER_OK) return (rc_tokenizer); - salt->salt_len = salt_len; + u8 *hash_pos = token.buf[3]; + int hash_len = token.len[3]; u8 tmp_buf[100] = { 0 }; - base64_decode (base64_to_int, (const u8 *) hash_pos, 27, tmp_buf); + base64_decode (base64_to_int, hash_pos, hash_len, tmp_buf); memcpy (digest, tmp_buf, 20); @@ -4131,6 +4185,13 @@ int episerver_parse_hash (u8 *input_buf, u32 input_len, hash_t *hash_buf, MAYBE_ digest[4] -= SHA1M_E; } + u8 *salt_pos = token.buf[2]; + int salt_len = token.len[2]; + + const bool parse_rc = parse_and_store_generic_salt ((u8 *) salt->salt_buf, (int *) &salt->salt_len, salt_pos, salt_len, hashconfig); + + if (parse_rc == false) return (PARSER_SALT_LENGTH); + return (PARSER_OK); } @@ -17993,7 +18054,7 @@ int ascii_digest (hashcat_ctx_t *hashcat_ctx, char *out_buf, const size_t out_le ptr_plain[27] = 0; - snprintf (out_buf, out_len - 1, "%s%s*%s", SIGNATURE_EPISERVER, ptr_salt, ptr_plain); + snprintf (out_buf, out_len - 1, "%s%s*%s", SIGNATURE_EPISERVER0, ptr_salt, ptr_plain); } else if (hash_mode == 400) {