Keyspace List for WPA on Default Routers
If I'm not totally (I mean completely!!!) mistaken you are totally looking at the wrong code.

The code posted by mrfancypants here:
is not (just) the key generator.

This is a special code (specifically coded for a single check/situation) that was answering this post , 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

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:
#!/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 | 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):
#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 EXTRA_BYTES 0x100

#if !defined MIN
  #define MIN(x,y) (((u32) x < (u32) y) ? (u32) x : (u32) y)

int main ()
   * Initialize output file and I/O buffering

  #ifdef WINDOWS
  setmode (fileno (stdout), O_BINARY);
  setmode (fileno (stderr), O_BINARY);

  FILE *fp_out = stdout;

  setbuf (fp_out, NULL);

   * Initialize output buffer


  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)
Please be more specific at which code you are refering to. There are a lot of code snippets within this thread, e.g. 2 for different algorithms, one for searching a (8 char) substring of a specific password candidate etc

Furthermore, counting the GB is not always a good idea because the code might create different "types of newlines" (carriage return and line feed vs just line feed etc). This could already "waste" several MB/GB of disk space. It's more clever to count the lines instead.
According to Mrfancypants it should have 2.1 billion lines (ranging from 0 to 0x7fffffff, e.g 2147483648 lines). If you multiply 2147483648 * 13 (e.g. 12 char password + 1 new line) = 27917287424 ~ 26GiB (calculated like this: 27917287424 / 1024 / 1024 / 1024).

Therefore, 26 GiB (or 28GB, calculate like this: 27917287424 / 1000 / 1000 / 1000) seems to be the correct file size (at least if Mrfancypants's statement about 2.1 billion possibilities is correct).

update: writing the password candidates to disk might not be needed and maybe even be much more slower (depending on your disk speed), you could just pipe the output of the compiled version of nvg599.c to hashcat). Using a pipe you would avoid wasting disk space and too much disk I/O. It depends on many factors which of them is slower and which one is faster (or in general: which one has more advantages compared to the disadvantages).
I don't know if anyone is looking at the algorithm for the 5268AC (another default device offering from AT&T), but I have noticed that it appears to be a variation on the 599 scheme.

I have noticed that both the python and C code for the nvg599 are correctly calculating the first 6 characters for the 5268AC, but the last 6 characters are not.

It seems that if you want to be able to brute force a default wifi password for the 5268AC, then you can get all of the output from the 599 python and/or C code, truncate every line to only the first 6 characters, sort, uniq, and then run a hybrid attack (mode 6, Hybrid Wordlist + Mask) with the applicable character set (37 characters).

Number of characters ^ positions = 37 ^ 6 = 2565726409

This method is far from time efficient given the number of unique 6 character prefixes that would be produced (multiplied by the number above, a little over 2.5 billion), but could work if you have no functional password calculator.

I welcome anyone to prove me wrong. Cross check the first 6 characters from passwords from 5268 devices with the output of the 599 code (both python and C) and see if this pattern is consistent. This pattern has a 100 percent hit rate for me.
it could be that for the 5268AC the formula is just a little bit different, e.g while we have the formula 2 ^ 32 + 2 for the 599, it could be that the formula for 5268AC is almost the same i.e. 2 ^ 32 + y with an y different from 2.

If the formula is indeed still 2^32 + y also for the 5268AC, you could probably just find the x value (for the function pwgen (x) within my python code, within my C code I used the variable i) that generated the almost correct password candidate and adjust the y value accordingly such that it generates the correct password.
Since there are 2.1 billion 599-type passwords and 37^6 ~ 2.6 billion possible 6-character strings, odds are good that there's a 599-type password that starts at any randomly picked 6-char string. So, unless you get 100% hit rate for a large number of passwords, this may be completely coincidental.
Also, if you used my python snippet to find extensions, some of your hits were invalid (I've just noticed that it does not check if 'cands' fall into 0..0x7FFFFFFF range).
I haven't looked too hard into 5268s but I suspect that they are using a very different approach. I did discover that their SSIDs always start with a digit and they never have more than 3 letters in a row. I think that they construct the SSID by picking entries from a large chunk dictionary according to some unknown logic, concatenating them, and truncating to 7. Their passwords likewise always start with a digit.
Do you have any info about Tenda and Huawei Routers default wifi keyspace?
SSID format (Tenda): Tenda_3B3E06
SSID format (Huawei): T-C82C87 (MAC: fc:e3:3c:c8:2c:94) Note: C82C is a part of MAC
TCT mobile ltd vendor, MY WIFI routers the WPA password is MYWIFI and four digits.
The SSID: MY WIFI + the last 4 digits of the MAC (e.g. MY WIFI C057,)
I find a list of the default key-space used by routers.

Pay close attention to the char-sets used, some are UPPERCASE, some a lowercase.
SpeedTouchXXXXXX - [0-9a-f] Len: 10 (Broken algo!)

BTHomeHub-XXXX - [0-9a-f] Len: 10 (Broken algo!)
BTHomeHub2-XXXX - [2-9a-f] Len: 10
BTHub3 - [2-9a-f] Len: 10
BTHub4 - [2-9a-f] Len: 10
BTHub5 - [2-9a-f] Len: 10
BTHub6 - [0-9a-zA-Z] Len: 10 or 12 see here and here.
ThomsonXXXXXX - [0-9a-f] Len: 10
PlusnetWireless-XXXXXX - [0-9A-F] Len: 10
PLUSNET-###### - [0-9a-f] Len: 10
MEO-###### - [0-9A-F] Len: 10 See image here. - [2-9a-f] Len: 8
belkin.xxxx - [2-9a-f] Len: 8
Belkin.XXXX - [0-9A-F] Len: 8
Belkin_XXXXXX - [0-9A-F] Len: 8
BELL### - [0-9A-F] Len: 8
BELL#### - [0-9A-F] Len: 10
TP-LINK_###### - [0-9A-F] Len: 8
TP-LINK_#### - [0-9] Len: 8 (Easy!)
TDC-#### - [0-9a-f] Len: 9 (Seen 2 and no 0 so maybe [1-9a-f] reduced key-space?)
TNCAPXXXXXX - [0-9A-F] Len: 10
TRKASHI-###### - ?d?d###### [Broken]

ATTXXX - [0-9] Len: 10. Common AT&T DSL routers in the USA.

WebCube#### - [0-9A-F] Len: 8

3WebCube#### - [0-9a-fA-F] Len: 8

WLAN1-XXXXXX - [0-9A-F] Len: 11

TelstraXXXXXX - [0-9A-F] Len:10

BigPondXXXXXX - [0-9A-F] Len:10

Useful info on TNCAP

2WIREXXX - [0-9] Len: 10. Common AT&T DSL routers in the USA.
ONOXXXX - [0-9] Len: 10
DJAWEB_##### - [0-9] Len: 10
TIM_PN51T_XXXX - [0-9] Len: 8 - WPS pin is 12345670... and WPS can't be disabled...

INFINITUM#### - [0-9] Len: 10
(07-30-2017, 02:06 AM)fart-box Wrote: It seems I've offended the Hashcat forum Gods, but all is well now. They've let me back in. My apologies to all.

We've all heard the expression "Give a man a fish and he'll eat for a day, but teach a man to fish and he'll eat for a lifetime."

It seems the internet is full of information, but the actual knowledge behind that information is scarce. And that seems to be the problem here.

Since there are several brands and models of routers out there that use the ATTxxxxxxx format, and they all seem to use their very own special seed to create their pass phrases, cracking ATT routers is a job left only "half" done.

Mrfancypants has given us the information (seeds) we need to build two key-gens, and I'm very thankful for those, but he did not share the methods (knowledge) he used to find those seeds, or to determine their values.

To be more explicit, no matter how carefully I follow the math Mrfancypants has described so well in previous posts, I can't seem to locate or re-create the numbers "465661287.5245797" or "2^32+2" from the information given. It seems they just suddenly appear in text or in code. Is there something I'm just not seeing? Knowing where those numbers came from would be a big step towards finding similar numbers in the other router models.

So, Mrfancypants, if you would kindly share your methods, we could all work together on this project instead of banging our heads against the wall while you do all the hard work.

I'm working on merging all my code to one project, though I'm not experienced enough to do it modularly. Updates are being pushed as we speak. Code still functions the same but I still haven't completed the 599 code. Would like to wait for mrfancypants... and would also like to know where those 'magic' numbers came from.
I am not sure I even understand your difficulty.

Let's take NVG589 specifically. We have an algorithm that takes in a 64-bit integer 'x' and spits out a 12-letter password:

for n in range(0,6):
pw=pw_charset[x%37] + pw
pw=chr(50+(x%8)) + pw

For whatever reason, AT&T people don't just pull an 'x' out of a RNG or something, instead they pull a 31-bit int and multiply it by a magic number that is approximately 465661287.5245797. (Or possibly do some slightly longer sequence of multiplications and additions which amounts to the same thing, because simply multiplying by that number does not always yield the exact result. But ignore that for now.)

Now, where did they get 465661287.5245797? Beats me. All I know is: (1) if they are starting with a 31-bit number, they had to multiply by _something_ (to span the whole range of passwords), since feeding a 31-bit value direct into code above would always result in passwords that start with 2a2a2a..; (2) that exact value reproduces many of the passwords I see in the wild and that can't be a coincidence. (To calculate it, I basically had the computer run through all possible values until it found one that gave lots of hits.)

for NVG599, they tweak the number->password conversion algorithm and replace the 465... value with 2^32+2. Again why 2^32+2? Not a clue.