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 += 1little 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)
