If I'm not totally (I mean completely!!!) mistaken you are totally looking at the wrong code.
The code posted by mrfancypants here: https://repl.it/JMbi/4
is not (just) the key generator.
This is a special code (specifically coded for a single check/situation) that was answering this post https://hashcat.net/forum/thread-6170-po...l#pid35744 , e.g. to check if the pwgen () function can generate the correct password candidate if the first 8 letters are known (
mrfancypants even mentioned this here https://hashcat.net/forum/thread-6170-po...l#pid35743).
That means, your code does not need the pw_to_candidate_ints () function.
Instead, it just needs a loop of all x values from 0 to 0x7FFFFFFF as an input for the pwgen(x) function.
This actually should be very obvious.
... and the amount of memory needed (without the search for a specific known and truncated 8 letter password candidate, which was just a test!!!) will be very low (less than 1 KB for sure, it just does some mathematical computation and has a static string pw_charset).
That said, the whole discussion about is return (y for y in cands if pwgen(y)[:l]==x) better than return [y for y in cands if pwgen(y)[:l]==x] makes no sense, because a password generator does not need pw_to_candidate_ints () at all.
pw_to_candidate_ints () could probably also be replaced by just an external "grep"-like filter (that would have probably avoided this funny confusion, but it probably would have needed to call pwgen () way more often, therefore the decision to implement a search function like pw_to_candidate_ints () makes sense, but just to proof that a specific 8-letter-truncated password candidate is within the whole password candidate list).
update: I attach here some example code such that you can get an idea of what *I think* is the actual password generator code:
python code (file: nvg599.py):
little optimized version, ANSI C with custom file I/O buffering (file: nvg599.c):
(please do not blame me for the code, I can also not guarantee that it doesn't contain bugs, but hopefully there won't be any bugs in this small code)
The code posted by mrfancypants here: https://repl.it/JMbi/4
is not (just) the key generator.
This is a special code (specifically coded for a single check/situation) that was answering this post https://hashcat.net/forum/thread-6170-po...l#pid35744 , e.g. to check if the pwgen () function can generate the correct password candidate if the first 8 letters are known (
mrfancypants even mentioned this here https://hashcat.net/forum/thread-6170-po...l#pid35743).
That means, your code does not need the pw_to_candidate_ints () function.
Instead, it just needs a loop of all x values from 0 to 0x7FFFFFFF as an input for the pwgen(x) function.
This actually should be very obvious.
... and the amount of memory needed (without the search for a specific known and truncated 8 letter password candidate, which was just a test!!!) will be very low (less than 1 KB for sure, it just does some mathematical computation and has a static string pw_charset).
That said, the whole discussion about is return (y for y in cands if pwgen(y)[:l]==x) better than return [y for y in cands if pwgen(y)[:l]==x] makes no sense, because a password generator does not need pw_to_candidate_ints () at all.
pw_to_candidate_ints () could probably also be replaced by just an external "grep"-like filter (that would have probably avoided this funny confusion, but it probably would have needed to call pwgen () way more often, therefore the decision to implement a search function like pw_to_candidate_ints () makes sense, but just to proof that a specific 8-letter-truncated password candidate is within the whole password candidate list).
update: I attach here some example code such that you can get an idea of what *I think* is the actual password generator code:
python code (file: nvg599.py):
Code:
#!/usr/bin/env python
# author: philsmd
# date: July 2017
# license: public domain
# full credits go to mrfancypants for the actual/original contribution
# to check if a password candidate is within the set:
# python nvg599.py | grep '^b=+#gc5q'
pw_charset = 'abcdefghijkmnpqrstuvwxyz23456789#%+=?'
# helper function (the actual password generator):
def pwgen (x):
x *= 2 ** 32 + 2
x = int (float (x))
pw = ''
for n in range (0, 12):
rem = x
rem %= 37
x /= 37
pw = pw_charset[rem] + pw
return pw
# start:
x = 0
while x <= 0x7fffffff:
print pwgen (x)
x += 1
little optimized version, ANSI C with custom file I/O buffering (file: nvg599.c):
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
/*
* author: philsmd
* date: July 2017
* license: public domain
* full credits go to mrfancypants for the actual/original contribution
*/
typedef uint8_t u8;
typedef uint32_t u32;
typedef uint64_t u64;
const static u8 PW_CHARSET[] = "abcdefghijkmnpqrstuvwxyz23456789#%+=?";
#define PW_LENGTH 12
#define LINE_LENGTH (PW_LENGTH + 1)
#define OUTBUFSZ BUFSIZ
#define EXTRA_BYTES 0x100
#if !defined MIN
#define MIN(x,y) (((u32) x < (u32) y) ? (u32) x : (u32) y)
#endif
int main ()
{
/*
* Initialize output file and I/O buffering
*/
#ifdef WINDOWS
setmode (fileno (stdout), O_BINARY);
setmode (fileno (stderr), O_BINARY);
#endif
FILE *fp_out = stdout;
setbuf (fp_out, NULL);
/*
* Initialize output buffer
*/
u8 buf[OUTBUFSZ + EXTRA_BYTES];
u32 cur_buffer_len = 0;
/*
* Initialize password buffer
*/
u8 pw[LINE_LENGTH] = { 0 };
pw[PW_LENGTH] = '\n'; // new lines are needed too
/*
* start
*/
for (u64 i = 0; i <= 0x7fffffff; i++)
{
u64 x = (double) (i * ((1l << 32) + 2));
for (int n = PW_LENGTH - 1; n >= 0; n--, x /= 37)
{
pw[n] = PW_CHARSET[x % 37];
}
// copy the password (pw) to the output buffer:
u32 copy_size = MIN (LINE_LENGTH, OUTBUFSZ - cur_buffer_len);
memcpy (buf + cur_buffer_len, pw, copy_size);
cur_buffer_len += copy_size;
if (cur_buffer_len < OUTBUFSZ) continue;
// buffer is full, write to the output file:
fwrite (buf, 1, OUTBUFSZ, fp_out);
// reset buffer length and copy the remaining bytes:
cur_buffer_len = LINE_LENGTH - copy_size;
memcpy (buf, pw + copy_size, cur_buffer_len);
}
// we must guarantee that we also write the final buffer to the file:
fwrite (buf, 1, cur_buffer_len, fp_out);
// close the file:
fclose (fp_out);
return 0;
}
(please do not blame me for the code, I can also not guarantee that it doesn't contain bugs, but hopefully there won't be any bugs in this small code)