bump version to 1.3
[cfe_generate_password.git] / cfe_generate_password.c
index 8746cd1..dd5beb4 100644 (file)
   eight digit hexadecimal 'password'.
 
   It is NOT necessary to have the device generate a 'seed' using "ATSE [MODEL-ID]"
-  because this tool can generate the seed from the device's first (base) MAC address
-  *provided* the "ATSE" command has NOT been executed since the device last booted.
+  because this tool can generate the seed from the device's first (base) MAC address.
 
-  Access to the device's console via a serial UART port is required to enter the password.
+  When the device generates a seed it combines the number of seconds since 1970-01-01 00:00:00
+  with the router MAC address. Both are encoded in a single 6-byte hexadecimal
+
+  Each value is truncated to its 3 least significant bytes so, for example:
+
+  $ date +%F.%T; echo "obase=16;$(date +%s)" | bc
+  2016-03-26.23:06:32
+  56F715F8
+  # MAC Address: EC:43:F6:46:C0:80
+
+  becomes F715F8 concatenated with 46C080
+
+  CFE> ATSE DSL-2492GNAU-B1BC
+  F715F846C080   <<<< last 3 bytes of MAC address
+  ^^^^^^
+  seconds since 1970-01-01 00:00:00 (2016-03-26 23:06:32)
+
+  *NOTE: the default seed after power-up is 000000 so no time value needs to be specifed
+  if "ATSE <model-id-string>" has not been executed on the device.
+
+  Access to the device's console via a serial UART port, or a network telnet/ssh session,
+  is required to enter the password.
 
   So, for a device with base MAC address (reported by the CFE during boot) E.g:
 
@@ -48,7 +68,7 @@
     OK
     *** command status = 0
 
-  The tool can accept a timestamp as 6 hexadecimal characters (useful for testing the algorithm):
+  The tool can accept a timestamp as 8 hexadecimal characters (useful for testing the algorithm):
 
     ./cfe_gen_pass -t 0FF020 -s ec:43:f6:46:c0:80 -p
 
 #include <stdlib.h>
 #include <stdarg.h>
 #include <string.h>
+#include <time.h>
 
-static const float VERSION = 1.1f;
-static const size_t TIMESTAMP_SIZE = 6;
+static const float VERSION = 1.3f;
+static const size_t TIMESTAMP_SIZE = 8;
 static const size_t SEED_SIZE = 12;
 static const size_t PASSWORD_SIZE = 8;
 static const size_t MESSAGE_SIZE = 128;
 static const size_t MAC_ADDR_SIZE = 17;
+static const size_t DATESTRING_SIZE = 20;
 
 static void
 pr_usage()
@@ -74,7 +96,7 @@ pr_usage()
     "Usage:\n"
     "  -v                   show version\n"
     "  -s 00:01:02:03:04:05 create seed from MAC address\n"
-    "  -t 000000            millisecond timestamp since boot\n"
+    "  -t [00000000]        seconds since 1970-01-01 (defaults to NOW) \n"
     "  -p [SEED]            generate password (with optional seed)\n\n"
     "  E.g. -s 01:02:03:04:05 \n"
     "       -s 01:02:03:04:05 -p\n"
@@ -86,12 +108,12 @@ static void
 pr_error_exit(unsigned int usage, const char *error, ...)
 {
  va_list args;
- char error_message[MESSAGE_SIZE];
+ char error_message[MESSAGE_SIZE + 1];
 
  if (!error) return;
 
  va_start(args, error);
- (void) vsnprintf(error_message, MESSAGE_SIZE, error, args);
+ (void) vsnprintf(error_message, MESSAGE_SIZE + 1, error, args);
  va_end(args);
  fprintf(stderr, "Error: %s\n", error_message);
 
@@ -119,19 +141,21 @@ generate_seed(char *mac, char *timestamp, char *seed)
     size_t i;
     char *mac_ptr = mac + 9;
     size_t ts_len = strlen(timestamp);
-    for (i = 0; i <= SEED_SIZE; ++i) {
-      /* if no timestamp assume CFE get_time() returned 0 and CFE g_pw_timestamp == 0x00000000 */
-      if (i < 6)
-          seed[i] = ts_len ? timestamp[i] : '0';
-      else {
-        if (*mac_ptr == ':' || *mac_ptr == '-')
-          ++mac_ptr;
-        seed[i] = *mac_ptr++;
+    if (ts_len == TIMESTAMP_SIZE) {
+      for (i = 0; i < SEED_SIZE; ++i) {
+        if (i < 6) // first half of seed is the truncated timestamp
+          seed[i] = timestamp[i+2];
+        else { // second half is the truncated MAC address
+          if (*mac_ptr == ':' || *mac_ptr == '-')
+            ++mac_ptr;
+          seed[i] = *mac_ptr++;
+        }
       }
-    }
-   result = 1;
+      result = 1;
+    } else
+      pr_error_exit(0, "Timestamp ('%s') should be %d hexadecimal characters e.g: 56F715F8", timestamp, TIMESTAMP_SIZE);
   } else
-   pr_error_exit(0, "MAC-ADDR should be 17 characters, e.g: 00:01:02:03:04:05");
+   pr_error_exit(0, "MAC-ADDR should be %d characters, e.g: 00:01:02:03:04:05", MAC_ADDR_SIZE);
 
   return result;
 }
@@ -172,11 +196,14 @@ main(int argc, char **argv, char **env)
     char timestamp[TIMESTAMP_SIZE + 1];
     char seed[SEED_SIZE + 1];
     char password[PASSWORD_SIZE + 1];
+    char date_string[DATESTRING_SIZE + 1];
     unsigned int opt_seed, opt_pass, opt_ts;
-    seed[0] = password[0] = 0;
-    seed[SEED_SIZE] = password[PASSWORD_SIZE] = timestamp[TIMESTAMP_SIZE] = 0;
+    time_t ts = 0;
+    struct tm *t = NULL;
+    seed[0] = password[0] = timestamp[0] = 0;
+    seed[SEED_SIZE] = password[PASSWORD_SIZE] = 0;
     opt_seed = opt_pass = opt_ts = 0;
-    strncpy(timestamp, "000000", TIMESTAMP_SIZE);
+    strncpy(timestamp, "00000000", TIMESTAMP_SIZE + 1);
 
     for (arg = 1; arg < (unsigned) argc; ++arg) {
       size_t arg_len = strlen(argv[arg]);
@@ -206,7 +233,7 @@ main(int argc, char **argv, char **env)
         ++opt_pass;
       } else if (opt_ts == 1) {
         if (arg_len != TIMESTAMP_SIZE)
-          pr_error_exit(1, "timestamp length must be %d characters", TIMESTAMP_SIZE);
+          pr_error_exit(1, "timestamp length must be %d hexadecimal characters", TIMESTAMP_SIZE);
 
         strncpy(timestamp, argv[arg], TIMESTAMP_SIZE);
         ++opt_ts;
@@ -220,9 +247,29 @@ main(int argc, char **argv, char **env)
       pr_error_exit(1, "password on its own requires a pre-generated seed");
     else if (opt_seed && opt_pass && opt_pass != 1)
       pr_error_exit(1, "generating seed and password; cannot also accept pre-generated seed");
-    else if (opt_ts == 1)
-      pr_error_exit(0, "missing timestamp; assuming 000000");
+    else if (opt_pass == 2 && opt_ts)
+      pr_error_exit(1, "seed already contains a timestamp; cannot over-ride it");
+    else if (opt_ts == 1 || opt_pass == 2) { // no timestamp provided; use NOW
+      ts = time(NULL);
+      if (ts)
+        snprintf(timestamp, TIMESTAMP_SIZE + 1, "%08lX", ts);
+    }
 
+    if (opt_pass == 2) { // try to figure out the correct date-time from the seed
+      // inherits the most significant 2 characters from the NOW time
+      strncpy(timestamp+2, seed, 6);
+      time_t tmp;
+      if (sscanf(timestamp, "%08lx", &tmp))
+        if (tmp > ts-3600 && tmp < ts+3600) // timestamps are so close they must be for the same date
+          ts = tmp;
+    }
+
+    if(opt_ts) { // ts needs to be valid to be converted to a time string
+      if(! sscanf(timestamp, "%08lx", &ts))
+        pr_error_exit(1, "converting timestamp string ('%s') to number", timestamp);
+    }
+    t = gmtime(&ts);
+    strftime(date_string, DATESTRING_SIZE, "%F %T", t);
 
     if (opt_seed)
       if (! generate_seed(MAC_ADDR, timestamp, seed))
@@ -232,7 +279,7 @@ main(int argc, char **argv, char **env)
         pr_error_exit(0, "unable to generate password");
 
     if (opt_seed || opt_pass)
-      printf("MAC address: %s Timestamp: %s Seed: %s Password: %s\n", MAC_ADDR, timestamp, seed, password);
+      printf("MAC address: %s Timestamp: %s (%s) Seed: %s Password: %s\n", MAC_ADDR, timestamp, date_string, seed, password);
   }
 
   return result;