7c5fb052e566c98b88a0f65615828d98f3612146
[cfe_generate_password.git] / cfe_generate_password.c
1 /*
2   Generate Broadcom CFE seeds and passwords for many popular modem/router devices
3
4   Copyright 2015 TJ <hacker@iam.tj>
5   Licenced on the terms of the GNU General Public Licence version 3
6
7   To build:
8
9     gcc -o cfe_gen_pass cfe_generate_password.c
10
11   Or:
12
13     make
14
15   To use:
16
17     ./cfe_gen_pass [options]
18
19   This tool can generate passwords for use with many devices that contain
20   Broadcom Common Firmware Environment (CFE) bootbase which has a debug mode
21   that is enabled using the "ATEN 1 XXXXXXXX" command, where XXXXXXXX is an
22   eight digit hexadecimal 'password'.
23
24   It is NOT necessary to have the device generate a 'seed' using "ATSE [MODEL-ID]"
25   because this tool can generate the seed from the device's first (base) MAC address
26   *provided* the "ATSE" command has NOT been executed since the device last booted.
27
28   Access to the device's console via a serial UART port is required to enter the password.
29
30   So, for a device with base MAC address (reported by the CFE during boot) E.g:
31
32     CFE version 1.0.38-112.118 for BCM963268 (32bit,SP,BE)
33     ...
34     Base MAC Address                  : ec:43:f6:46:c0:80
35     ...
36     *** Press any key to stop auto run (1 seconds) ***
37     CFE>
38
39   Using this tool do:
40
41     ./cfe_gen_pass -s ec:43:f6:46:c0:80 -p
42
43     MAC address: ec:43:f6:46:c0:80 Timestamp: 000000 Seed: 00000046c080 Password: 10f0a563
44
45   And on the device do:
46
47     CFE> ATEN 1 10f0a563
48     OK
49     *** command status = 0
50
51   The tool can accept a timestamp as 6 hexadecimal characters (useful for testing the algorithm):
52
53     ./cfe_gen_pass -t 0FF020 -s ec:43:f6:46:c0:80 -p
54
55     MAC address: ec:43:f6:46:c0:80 Timestamp: 0FF020 Seed: 0FF02046c080 Password: 110f65a3
56  */
57
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <stdarg.h>
61 #include <string.h>
62
63 static const float VERSION = 1.2f;
64 static const size_t TIMESTAMP_SIZE = 6;
65 static const size_t SEED_SIZE = 12;
66 static const size_t PASSWORD_SIZE = 8;
67 static const size_t MESSAGE_SIZE = 128;
68 static const size_t MAC_ADDR_SIZE = 17;
69
70 static void
71 pr_usage()
72 {
73   fprintf(stderr, "%s\n",
74     "Usage:\n"
75     "  -v                   show version\n"
76     "  -s 00:01:02:03:04:05 create seed from MAC address\n"
77     "  -t 000000            millisecond timestamp since boot\n"
78     "  -p [SEED]            generate password (with optional seed)\n\n"
79     "  E.g. -s 01:02:03:04:05 \n"
80     "       -s 01:02:03:04:05 -p\n"
81     "       -p 000000030405\n"
82   );
83 }
84
85 static void
86 pr_error_exit(unsigned int usage, const char *error, ...)
87 {
88  va_list args;
89  char error_message[MESSAGE_SIZE + 1];
90
91  if (!error) return;
92
93  va_start(args, error);
94  (void) vsnprintf(error_message, MESSAGE_SIZE + 1, error, args);
95  va_end(args);
96  fprintf(stderr, "Error: %s\n", error_message);
97
98  if (usage) pr_usage();
99
100  exit(EXIT_FAILURE);
101 }
102
103 static const unsigned int passwords[8] = {
104   0x10F0A563,
105   0x887852B1,
106   0xC43C2958,
107   0x621E14AC,
108   0x310F0A56,
109   0x1887852B,
110   0x8C43C295,
111   0xC621E14A
112 };
113
114 static unsigned int
115 generate_seed(char *mac, char *timestamp, char *seed)
116 {
117   unsigned int result = 0;
118   if (mac && strlen(mac) == MAC_ADDR_SIZE) {
119     size_t i;
120     char *mac_ptr = mac + 9;
121     size_t ts_len = strlen(timestamp);
122     for (i = 0; i < SEED_SIZE; ++i) {
123       /* if no timestamp assume CFE get_time() returned 0 and CFE g_pw_timestamp == 0x00000000 */
124       if (i < 6)
125           seed[i] = ts_len ? timestamp[i] : '0';
126       else {
127         if (*mac_ptr == ':' || *mac_ptr == '-')
128           ++mac_ptr;
129         seed[i] = *mac_ptr++;
130       }
131     }
132    result = 1;
133   } else
134    pr_error_exit(0, "MAC-ADDR should be %d characters, e.g: 00:01:02:03:04:05", MAC_ADDR_SIZE);
135
136   return result;
137 }
138
139 static unsigned int
140 generate_pass(char *seed, char *password)
141 {
142   unsigned int result = 0;
143
144   if (seed && strlen(seed) == SEED_SIZE) {
145     unsigned int timestamp, byte, key, pass;
146     timestamp = byte = 0;
147     if(! sscanf(seed, "%06x", &timestamp))
148       pr_error_exit(1, "unable to parse seed's timestamp");
149     if (! sscanf(&seed[10], "%02x", &byte))
150       pr_error_exit(1, "unable to parse seed's MAC address");
151     key = byte & 0x07;
152     pass = (passwords[key] + timestamp) ^ timestamp;
153     snprintf(password, PASSWORD_SIZE + 1,  "%08x", pass);
154     result = 1;
155   } else
156     pr_error_exit(0, "Seed should be %d hex characters", SEED_SIZE);
157
158   return result;
159 }
160
161 int
162 main(int argc, char **argv, char **env)
163 {
164   int result = 0;
165
166   if (argc == 1) {
167     pr_usage();
168   }
169   else {
170     unsigned int arg;
171     char *MAC_ADDR = NULL;
172     char timestamp[TIMESTAMP_SIZE + 1];
173     char seed[SEED_SIZE + 1];
174     char password[PASSWORD_SIZE + 1];
175     unsigned int opt_seed, opt_pass, opt_ts;
176     seed[0] = password[0] = 0;
177     seed[SEED_SIZE] = password[PASSWORD_SIZE] = timestamp[TIMESTAMP_SIZE] = 0;
178     opt_seed = opt_pass = opt_ts = 0;
179     strncpy(timestamp, "000000", TIMESTAMP_SIZE);
180
181     for (arg = 1; arg < (unsigned) argc; ++arg) {
182       size_t arg_len = strlen(argv[arg]);
183
184       if (argv[arg][0] == '-') {
185         switch (argv[arg][1]) {
186           case 's':
187             opt_seed = 1;
188             break;
189           case 'p':
190             opt_pass = 1;
191             break;
192           case 't':
193             opt_ts = 1;
194             break;
195           case 'v':
196             fprintf(stderr, "Version: %0.2f\n", VERSION);
197         }
198       } else if (opt_seed == 1) {
199         MAC_ADDR = argv[arg];
200         ++opt_seed;
201       } else if (opt_pass == 1 && opt_seed == 0) {
202         if (arg_len != SEED_SIZE)
203           pr_error_exit(1, "seed length must be %d characters", SEED_SIZE);
204
205         strncpy(seed, argv[arg], SEED_SIZE);
206         ++opt_pass;
207       } else if (opt_ts == 1) {
208         if (arg_len != TIMESTAMP_SIZE)
209           pr_error_exit(1, "timestamp length must be %d characters", TIMESTAMP_SIZE);
210
211         strncpy(timestamp, argv[arg], TIMESTAMP_SIZE);
212         ++opt_ts;
213       }
214     }
215     if (! opt_seed && ! opt_pass)
216       pr_usage();
217     else if (opt_seed && opt_seed != 2)
218       pr_error_exit(1, "seed requires MAC-ADDRESS");
219     else if (! opt_seed && opt_pass && opt_pass != 2)
220       pr_error_exit(1, "password on its own requires a pre-generated seed");
221     else if (opt_seed && opt_pass && opt_pass != 1)
222       pr_error_exit(1, "generating seed and password; cannot also accept pre-generated seed");
223     else if (opt_ts == 1)
224       pr_error_exit(0, "missing timestamp; assuming 000000");
225
226
227     if (opt_seed)
228       if (! generate_seed(MAC_ADDR, timestamp, seed))
229         pr_error_exit(1, "unable to generate seed; aborting");
230     if (opt_pass)
231       if (! generate_pass(seed, password))
232         pr_error_exit(0, "unable to generate password");
233
234     if (opt_seed || opt_pass)
235       printf("MAC address: %s Timestamp: %s Seed: %s Password: %s\n", MAC_ADDR, timestamp, seed, password);
236   }
237
238   return result;
239 }
240