-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Oubliette Password Manager support #5680
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
7cfe67c
Oubliette Password Manager
davidedg 23fd65b
Oubliette Password Manager Hash creator
davidedg fbe12fa
Added 0-clause BSD license notice
davidedg ca9ec5c
restored IDEA in comment
davidedg 9c98bfc
fixed trailing whitespaces
davidedg 528e2c0
minor fixes to python code
davidedg 8d53772
Removed SIMD incomplete implementation draft
davidedg 556bc63
Excluded format on --without-openssl builds
davidedg fd6c665
fixed trailing whitespaces
davidedg 7e84a23
Oubliette: Added to NEWS
davidedg 6dd8238
Oubliette: moved python hash converter to run dir
davidedg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
#!/usr/bin/env python3 | ||
|
||
import sys | ||
import struct | ||
import binascii | ||
|
||
def process_file(filename): | ||
try: | ||
with open(filename, 'rb') as f: | ||
# Read and validate header | ||
header = f.read(12) | ||
if len(header) != 12: | ||
sys.stderr.write(f"{filename}: Invalid file size\n") | ||
return -1 | ||
|
||
# Unpack header fields | ||
magic = header[0:5].decode('ascii') | ||
major_ver = header[5] | ||
minor_ver = header[6] | ||
algorithm = header[7] | ||
data_size = struct.unpack("<I", header[8:12])[0] | ||
|
||
if magic != "OUBPF": | ||
sys.stderr.write(f"{filename}: Invalid magic bytes\n") | ||
return -2 | ||
|
||
if algorithm not in [0, 1]: | ||
sys.stderr.write(f"{filename}: Unknown algorithm {algorithm}\n") | ||
return -3 | ||
|
||
# Read the PasswordEncryptedHash | ||
password_hash = f.read(32) | ||
if len(password_hash) != 32: | ||
sys.stderr.write(f"{filename}: Failed to read PasswordEncryptedHash\n") | ||
return -4 | ||
|
||
# Format output for John the Ripper with different tags for Blowfish and IDEA | ||
hex_hash = binascii.hexlify(password_hash).decode('ascii') | ||
format_tag = "oubliette-blowfish" if algorithm == 0 else "oubliette-idea" | ||
print(f"${format_tag}${major_ver}.{minor_ver}${hex_hash}") | ||
return 0 | ||
|
||
except IOError as e: | ||
sys.stderr.write(f"{filename}: {str(e)}\n") | ||
return -5 | ||
|
||
def usage(): | ||
sys.stderr.write("Usage: oubliette2john.py <oubliette files>\n") | ||
sys.exit(-6) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. exit codes are unsigned integers. Please use positive numbers here and above. |
||
|
||
if __name__ == "__main__": | ||
if len(sys.argv) < 2: | ||
usage() | ||
|
||
for filename in sys.argv[1:]: | ||
process_file(filename) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,338 @@ | ||
/* | ||
* Oubliette password manager Blowfish format cracker for JtR. | ||
* Contributed by DavideDG github.com/davidedg | ||
*/ | ||
|
||
#if FMT_EXTERNS_H | ||
extern struct fmt_main fmt_oubliette_blowfish; | ||
#elif FMT_REGISTERS_H | ||
john_register_one(&fmt_oubliette_blowfish); | ||
#else | ||
|
||
#include <string.h> | ||
#include <openssl/blowfish.h> | ||
#include <openssl/sha.h> | ||
|
||
#ifdef _OPENMP | ||
#include <omp.h> | ||
#endif | ||
|
||
#if defined(__SSE4_1__) || defined(__ARM_NEON) | ||
#define USE_SIMD | ||
#endif | ||
|
||
#ifdef USE_SIMD | ||
#ifdef __SSE4_1__ | ||
#include <smmintrin.h> | ||
#define SIMD_WIDTH 4 // Process 4 passwords at once with SSE | ||
#elif defined(__ARM_NEON) | ||
#include <arm_neon.h> | ||
#define SIMD_WIDTH 4 // Process 4 passwords at once with NEON | ||
#endif | ||
#else | ||
#define SIMD_WIDTH 1 // No SIMD, process one at a time | ||
#endif | ||
|
||
#include "arch.h" | ||
#include "misc.h" | ||
#include "common.h" | ||
#include "formats.h" | ||
|
||
#define FORMAT_LABEL "oubliette-blowfish" | ||
#define FORMAT_NAME "Oubliette Blowfish" | ||
#define FORMAT_TAG "$oubliette-blowfish$" | ||
#define TAG_LENGTH (sizeof(FORMAT_TAG)-1) | ||
#ifdef USE_SIMD | ||
#ifdef __SSE4_1__ | ||
#define ALGORITHM_NAME "SHA1 Blowfish 32/" ARCH_BITS_STR " SSE4.1" | ||
#else | ||
#define ALGORITHM_NAME "SHA1 Blowfish 32/" ARCH_BITS_STR " NEON" | ||
#endif | ||
#else | ||
#define ALGORITHM_NAME "SHA1 Blowfish 32/" ARCH_BITS_STR | ||
#endif | ||
#define BENCHMARK_COMMENT "" | ||
#define BENCHMARK_LENGTH 0x107 | ||
#define PLAINTEXT_LENGTH 125 | ||
#define BINARY_SIZE 32 | ||
#define BINARY_ALIGN sizeof(uint32_t) | ||
#define SALT_SIZE 0 | ||
#define SALT_ALIGN 1 | ||
#ifdef USE_SIMD | ||
#define MIN_KEYS_PER_CRYPT (16 * SIMD_WIDTH) | ||
#define MAX_KEYS_PER_CRYPT (256 * SIMD_WIDTH) | ||
#else | ||
#define MIN_KEYS_PER_CRYPT 16 | ||
#define MAX_KEYS_PER_CRYPT 256 | ||
#endif | ||
#define OMP_SCALE 16 | ||
|
||
// Aligned buffer for better cache performance | ||
typedef struct { | ||
// Group frequently accessed data together | ||
unsigned char padded_sha1[32] __attribute__((aligned(16))); | ||
BF_KEY bf_key __attribute__((aligned(16))); | ||
// Less frequently accessed data | ||
unsigned char iv[8] __attribute__((aligned(16))); | ||
unsigned char encrypted_iv[8] __attribute__((aligned(16))); | ||
} oubliette_state; | ||
|
||
static struct fmt_tests tests[] = { | ||
{"$oubliette-blowfish$1.0$16c863009dbc7a89fa26520aeeae5543beda0bbf41622be472d7c7320c8ea66f", "12345678"}, | ||
{"$oubliette-blowfish$1.0$82ea2c652362c380349d6ac0d5a06d2ae9b164becc5a75103c2744b4d27fe35d", "\xd1\xa2\x63\x5e\xac\x4e\x4a\x25\x48\xd5\x45\x73\xae\x79\x53\xb2\xe5\x69\xfc\x59\xce\x40\x21\x6a\x67\x54\x5c\xff\x5c\xfe\x7a\x52\xbe\x77\xd2\x77\xb8\x61\x39\x45\xf0\x2b\x3e\x4a\x3c\xa4\x2e\x53\xd8\xba\x71\xc8\x32\x26\x69\x37\x2f\xfa\x7d\xa1\xff\xdf\xd4\xba\x29\x4d\xd4\x72\x69\x40\xc3\x5c\x22\x2b\x79\x50\x21\x41\x5d\xc7\xdd\x6a\x3f\xed\x26\x7b\x34\x7e\xb9\x21"}, | ||
{NULL} | ||
}; | ||
|
||
static unsigned char (*saved_key)[PLAINTEXT_LENGTH]; | ||
static int *saved_len; | ||
static uint32_t (*crypt_out)[BINARY_SIZE / sizeof(uint32_t)]; | ||
static oubliette_state *state; | ||
|
||
static void init(struct fmt_main *self) | ||
{ | ||
#ifdef _OPENMP | ||
omp_autotune(self, OMP_SCALE); | ||
#endif | ||
saved_key = mem_calloc(self->params.max_keys_per_crypt, sizeof(*saved_key)); | ||
saved_len = mem_calloc(self->params.max_keys_per_crypt, sizeof(*saved_len)); | ||
crypt_out = mem_calloc(self->params.max_keys_per_crypt, sizeof(*crypt_out)); | ||
state = mem_calloc(self->params.max_keys_per_crypt, sizeof(*state)); | ||
} | ||
|
||
static void done(void) | ||
{ | ||
MEM_FREE(state); | ||
MEM_FREE(saved_key); | ||
MEM_FREE(saved_len); | ||
MEM_FREE(crypt_out); | ||
} | ||
|
||
static int valid(char *ciphertext, struct fmt_main *self) | ||
{ | ||
char *p; | ||
char *ctcopy; | ||
char *keeptr; | ||
int version, minor; | ||
|
||
if (strncmp(ciphertext, FORMAT_TAG, TAG_LENGTH) != 0) | ||
return 0; | ||
|
||
ctcopy = xstrdup(ciphertext); | ||
keeptr = ctcopy; | ||
ctcopy += TAG_LENGTH; | ||
|
||
if ((p = strtokm(ctcopy, "$")) == NULL) /* version */ | ||
goto err; | ||
if (sscanf(p, "%d.%d", &version, &minor) != 2) | ||
goto err; | ||
if (version != 1 || minor != 0) | ||
goto err; | ||
|
||
if ((p = strtokm(NULL, "$")) == NULL) /* hash */ | ||
goto err; | ||
if (hexlenl(p, NULL) != BINARY_SIZE * 2) | ||
goto err; | ||
|
||
MEM_FREE(keeptr); | ||
return 1; | ||
|
||
err: | ||
MEM_FREE(keeptr); | ||
return 0; | ||
} | ||
|
||
static void *get_binary(char *ciphertext) | ||
{ | ||
static union { | ||
unsigned char c[BINARY_SIZE]; | ||
uint32_t dummy; | ||
} buf; | ||
char *p; | ||
int i; | ||
p = strrchr(ciphertext, '$') + 1; | ||
for (i = 0; i < BINARY_SIZE; i++) { | ||
buf.c[i] = (atoi16[ARCH_INDEX(*p)] << 4) | atoi16[ARCH_INDEX(p[1])]; | ||
p += 2; | ||
} | ||
return buf.c; | ||
} | ||
|
||
static void set_key(char *key, int index) | ||
{ | ||
saved_len[index] = strlen(key); | ||
memcpy(saved_key[index], key, saved_len[index]); | ||
} | ||
|
||
static char *get_key(int index) | ||
{ | ||
static char out[PLAINTEXT_LENGTH + 1]; | ||
memcpy(out, saved_key[index], saved_len[index]); | ||
out[saved_len[index]] = 0; | ||
return out; | ||
} | ||
|
||
static int get_hash_0(int index) { return crypt_out[index][0] & PH_MASK_0; } | ||
static int get_hash_1(int index) { return crypt_out[index][0] & PH_MASK_1; } | ||
static int get_hash_2(int index) { return crypt_out[index][0] & PH_MASK_2; } | ||
static int get_hash_3(int index) { return crypt_out[index][0] & PH_MASK_3; } | ||
static int get_hash_4(int index) { return crypt_out[index][0] & PH_MASK_4; } | ||
static int get_hash_5(int index) { return crypt_out[index][0] & PH_MASK_5; } | ||
static int get_hash_6(int index) { return crypt_out[index][0] & PH_MASK_6; } | ||
|
||
static int crypt_all(int *pcount, struct db_salt *salt) | ||
{ | ||
const int count = *pcount; | ||
int index; | ||
|
||
#ifdef _OPENMP | ||
#pragma omp parallel for | ||
#endif | ||
for (index = 0; index < count; index += SIMD_WIDTH) { | ||
#ifdef USE_SIMD | ||
int j; | ||
oubliette_state *s = &state[index]; | ||
|
||
#ifdef __SSE4_1__ | ||
// Process passwords in SIMD_WIDTH chunks | ||
// Process SHA1 for all passwords in the chunk | ||
for (j = 0; j < SIMD_WIDTH && (index + j) < count; j++) { | ||
SHA_CTX ctx; | ||
SHA1_Init(&ctx); | ||
SHA1_Update(&ctx, saved_key[index + j], saved_len[index + j]); | ||
SHA1_Final(s[j].padded_sha1, &ctx); | ||
memset(s[j].padded_sha1 + 20, 0xFF, 12); | ||
} | ||
|
||
// Process Blowfish operations in parallel | ||
for (j = 0; j < SIMD_WIDTH && (index + j) < count; j++) { | ||
BF_set_key(&s[j].bf_key, 32, s[j].padded_sha1); | ||
memset(s[j].iv, 0xFF, 8); | ||
BF_ecb_encrypt(s[j].iv, s[j].encrypted_iv, &s[j].bf_key, BF_ENCRYPT); | ||
|
||
unsigned char *out = (unsigned char*)crypt_out[index + j]; | ||
BF_cbc_encrypt(s[j].padded_sha1, out, 32, &s[j].bf_key, s[j].encrypted_iv, BF_ENCRYPT); | ||
} | ||
#elif defined(__ARM_NEON) | ||
// Process passwords in SIMD_WIDTH chunks for NEON | ||
// Process SHA1 for all passwords in the chunk | ||
for (j = 0; j < SIMD_WIDTH && (index + j) < count; j++) { | ||
SHA_CTX ctx; | ||
SHA1_Init(&ctx); | ||
SHA1_Update(&ctx, saved_key[index + j], saved_len[index + j]); | ||
SHA1_Final(s[j].padded_sha1, &ctx); | ||
memset(s[j].padded_sha1 + 20, 0xFF, 12); | ||
} | ||
|
||
// Process Blowfish operations in parallel | ||
for (j = 0; j < SIMD_WIDTH && (index + j) < count; j++) { | ||
BF_set_key(&s[j].bf_key, 32, s[j].padded_sha1); | ||
memset(s[j].iv, 0xFF, 8); | ||
BF_ecb_encrypt(s[j].iv, s[j].encrypted_iv, &s[j].bf_key, BF_ENCRYPT); | ||
|
||
unsigned char *out = (unsigned char*)crypt_out[index + j]; | ||
BF_cbc_encrypt(s[j].padded_sha1, out, 32, &s[j].bf_key, s[j].encrypted_iv, BF_ENCRYPT); | ||
} | ||
#endif | ||
#else | ||
// Original non-SIMD code | ||
oubliette_state *s = &state[index]; | ||
SHA_CTX ctx; | ||
|
||
SHA1_Init(&ctx); | ||
SHA1_Update(&ctx, saved_key[index], saved_len[index]); | ||
SHA1_Final(s->padded_sha1, &ctx); | ||
|
||
memset(s->padded_sha1 + 20, 0xFF, 12); | ||
BF_set_key(&s->bf_key, 32, s->padded_sha1); | ||
memset(s->iv, 0xFF, 8); | ||
BF_ecb_encrypt(s->iv, s->encrypted_iv, &s->bf_key, BF_ENCRYPT); | ||
|
||
unsigned char *out = (unsigned char*)crypt_out[index]; | ||
BF_cbc_encrypt(s->padded_sha1, out, 32, &s->bf_key, s->encrypted_iv, BF_ENCRYPT); | ||
#endif | ||
} | ||
return count; | ||
} | ||
|
||
static int cmp_all(void *binary, int count) | ||
{ | ||
int index; | ||
for (index = 0; index < count; index++) { | ||
if (!memcmp(binary, crypt_out[index], BINARY_SIZE)) | ||
return 1; | ||
} | ||
return 0; | ||
} | ||
|
||
static int cmp_one(void *binary, int index) | ||
{ | ||
return !memcmp(binary, crypt_out[index], BINARY_SIZE); | ||
} | ||
|
||
static int cmp_exact(char *source, int index) | ||
{ | ||
return 1; | ||
} | ||
|
||
struct fmt_main fmt_oubliette_blowfish = { | ||
{ | ||
FORMAT_LABEL, | ||
FORMAT_NAME, | ||
ALGORITHM_NAME, | ||
BENCHMARK_COMMENT, | ||
BENCHMARK_LENGTH, | ||
0, | ||
PLAINTEXT_LENGTH, | ||
BINARY_SIZE, | ||
BINARY_ALIGN, | ||
SALT_SIZE, | ||
SALT_ALIGN, | ||
MIN_KEYS_PER_CRYPT, | ||
MAX_KEYS_PER_CRYPT, | ||
FMT_CASE | FMT_8_BIT | FMT_OMP, | ||
{ NULL }, | ||
{ FORMAT_TAG }, | ||
tests | ||
}, { | ||
init, | ||
done, | ||
fmt_default_reset, | ||
fmt_default_prepare, | ||
valid, | ||
fmt_default_split, | ||
get_binary, | ||
fmt_default_salt, | ||
{ NULL }, | ||
fmt_default_source, | ||
{ | ||
fmt_default_binary_hash_0, | ||
fmt_default_binary_hash_1, | ||
fmt_default_binary_hash_2, | ||
fmt_default_binary_hash_3, | ||
fmt_default_binary_hash_4, | ||
fmt_default_binary_hash_5, | ||
fmt_default_binary_hash_6 | ||
}, | ||
fmt_default_salt_hash, | ||
NULL, | ||
fmt_default_set_salt, | ||
set_key, | ||
get_key, | ||
fmt_default_clear_keys, | ||
crypt_all, | ||
{ | ||
get_hash_0, | ||
get_hash_1, | ||
get_hash_2, | ||
get_hash_3, | ||
get_hash_4, | ||
get_hash_5, | ||
get_hash_6 | ||
}, | ||
cmp_all, | ||
cmp_one, | ||
cmp_exact | ||
} | ||
}; | ||
|
||
#endif /* plugin stanza */ |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
str()
function is unnecessary here and will be called implicitly anyway.{e}
is enough for simplification.