Merge remote-tracking branch 'goog/upstream-master' into nos-merge-upstream

* goog/upstream-master:
  keymaster: update ExportKeyResponse message
  Use NUGGET_REBOOT_HARD in citadel_updater --reboot
  Added wait_for_interrupt.
  keymaster: bump proto request buffer size
  Add struct nugget_app_low_power_stats definition
  Add NUGGET_PARAM_GET_LOW_POWER_STATS command
  Fix arg parsing in citadel_updater
  Add password support to citadel_updater
  Add more Nugget Core commands for firmware updates
  keymaster: bump size of params max_count
  Remove unused NUGGET_PARAM_REVERSE command

Change-Id: Ie8d11ddac30bc322b6d2530a055b5a4ba32c9db9
diff --git a/citadel/updater/updater.cpp b/citadel/updater/updater.cpp
index 75c4aaf..fbbcd8d 100644
--- a/citadel/updater/updater.cpp
+++ b/citadel/updater/updater.cpp
@@ -55,6 +55,10 @@
   int ro;
   int rw;
   int reboot;
+  int enable_ro;
+  int enable_rw;
+  int change_pw;
+  uint32_t erase_code;
   /* generic connection options */
   const char *device;
 } options;
@@ -64,6 +68,10 @@
   OPT_RO,
   OPT_RW,
   OPT_REBOOT,
+  OPT_ENABLE_RO,
+  OPT_ENABLE_RW,
+  OPT_CHANGE_PW,
+  OPT_ERASE,
 };
 
 const char *short_opts = ":hv";
@@ -73,6 +81,10 @@
   {"ro",          0, NULL, OPT_RO},
   {"rw",          0, NULL, OPT_RW},
   {"reboot",      0, NULL, OPT_REBOOT},
+  {"enable_ro",   0, NULL, OPT_ENABLE_RO},
+  {"enable_rw",   0, NULL, OPT_ENABLE_RW},
+  {"change_pw",   0, NULL, OPT_CHANGE_PW},
+  {"erase",       1, NULL, OPT_ERASE},
   {"device",      1, NULL, OPT_DEVICE},
   {"help",        0, NULL, 'h'},
   {NULL, 0, NULL, 0},
@@ -107,6 +119,14 @@
     "      --rw          Update RW firmware from the image file\n"
     "      --ro          Update RO firmware from the image file\n"
     "      --reboot      Tell Citadel to reboot\n"
+    "\n"
+    "      --enable_ro   Mark new RO image as good\n"
+    "      --enable_rw   Mark new RW image as good\n"
+    "\n"
+    "      --change_pw   Change update password\n"
+    "\n\n"
+    "      --erase=CODE  Erase all user secrets and reboot.\n"
+    "                    This skips all other actions.\n"
     "\n",
     progname);
 }
@@ -123,11 +143,11 @@
 {
   va_list ap;
 
-        va_start(ap, format);
-        fprintf(stderr, "ERROR: ");
-        vfprintf(stderr, format, ap);
-        fprintf(stderr, "\n");
-        va_end(ap);
+  va_start(ap, format);
+  fprintf(stderr, "ERROR: ");
+  vfprintf(stderr, format, ap);
+  fprintf(stderr, "\n");
+  va_end(ap);
 
   errorcnt++;
 }
@@ -210,24 +230,30 @@
   return buf;
 }
 
-uint32_t compute_digest(struct nugget_app_flash_block *blk)
+uint32_t compute_digest(void *ptr, size_t len)
 {
-  uint8_t *start_here = ((uint8_t *)blk) +
-    offsetof(struct nugget_app_flash_block, offset);
-  size_t size_to_hash = sizeof(*blk) -
-    offsetof(struct nugget_app_flash_block, offset);
   SHA_CTX ctx;
   uint8_t digest[SHA_DIGEST_LENGTH];
   uint32_t retval;
 
   SHA1_Init(&ctx);
-  SHA1_Update(&ctx, start_here, size_to_hash);
+  SHA1_Update(&ctx, ptr, len);
   SHA1_Final(digest, &ctx);
 
   memcpy(&retval, digest, sizeof(retval));
   return retval;
 }
 
+uint32_t compute_fb_digest(struct nugget_app_flash_block *blk)
+{
+  uint8_t *start_here = ((uint8_t *)blk) +
+    offsetof(struct nugget_app_flash_block, offset);
+  size_t size_to_hash = sizeof(*blk) -
+    offsetof(struct nugget_app_flash_block, offset);
+
+  return compute_digest(start_here, size_to_hash);
+}
+
 uint32_t try_update(AppClient &app, const std::vector<uint8_t> &image,
         uint32_t offset, uint32_t imagesize)
 {
@@ -245,7 +271,7 @@
 
     fb->offset = offset;
     memcpy(fb->payload, image.data() + offset, CHIP_FLASH_BANK_SIZE);
-    fb->block_digest = compute_digest(fb);
+    fb->block_digest = compute_fb_digest(fb);
 
     printf("writing 0x%05x / 0x%05x",
            CHIP_FLASH_BASE + offset, CHIP_FLASH_BASE + stop);
@@ -254,15 +280,19 @@
       if (rv == NUGGET_ERROR_RETRY)
         printf(" retrying");
     } while (rv == NUGGET_ERROR_RETRY && retries--);
-    printf(" %s\n", rv ? "fail" : "ok");
-    if (rv)
+    if (rv) {
+      if (rv == NUGGET_ERROR_LOCKED)
+        printf(" locked\n");
+      else
+        printf(" fail %d\n", rv);
       break;
+    }
+    printf(" ok\n");
   }
 
   return rv;
 }
 
-
 uint32_t do_update(AppClient &app, const std::vector<uint8_t> &image,
        uint32_t offset_A, uint32_t offset_B)
 {
@@ -303,7 +333,7 @@
 uint32_t do_reboot(AppClient &app)
 {
   uint32_t retval;
-  std::vector<uint8_t> data = {0};
+  std::vector<uint8_t> data = {NUGGET_REBOOT_HARD};
 
   retval = app.Call(NUGGET_PARAM_REBOOT, data, nullptr);
 
@@ -314,6 +344,77 @@
   return retval;
 }
 
+static uint32_t do_change_pw(AppClient &app,
+                             const char *old_pw, const char *new_pw)
+{
+  std::vector<uint8_t> data(sizeof(struct nugget_app_change_update_password));
+  struct nugget_app_change_update_password *s =
+    (struct nugget_app_change_update_password*)data.data();
+
+
+  memset(s, 0xff, sizeof(*s));
+  if (old_pw && *old_pw) {
+    int len = strlen(old_pw);
+    memcpy(&s->old_password.password, old_pw, len);
+    s->old_password.digest =
+      compute_digest(&s->old_password.password,
+                     sizeof(s->old_password.password));
+  }
+
+  if (new_pw && *new_pw) {
+    int len = strlen(new_pw);
+    memcpy(&s->new_password.password, new_pw, len);
+    s->new_password.digest =
+      compute_digest(&s->new_password.password,
+                     sizeof(s->new_password.password));
+  }
+
+  uint32_t rv = app.Call(NUGGET_PARAM_CHANGE_UPDATE_PASSWORD, data, nullptr);
+
+  if (is_app_success(rv))
+    printf("Password changed\n");
+
+  return rv;
+}
+
+static uint32_t do_enable(AppClient &app, const char *pw)
+{
+  std::vector<uint8_t> data(sizeof(struct nugget_app_enable_update));
+  struct nugget_app_enable_update *s =
+    (struct nugget_app_enable_update*)data.data();
+
+  memset(&s->password, 0xff, sizeof(s->password));
+  if (pw && *pw) {
+    int len = strlen(pw);
+    memcpy(&s->password.password, pw, len);
+    s->password.digest =
+      compute_digest(&s->password.password,
+                     sizeof(s->password.password));
+  }
+  s->which_headers = options.enable_ro ? NUGGET_ENABLE_HEADER_RO : 0;
+  s->which_headers |= options.enable_rw ? NUGGET_ENABLE_HEADER_RW : 0;
+
+  uint32_t rv = app.Call(NUGGET_PARAM_ENABLE_UPDATE, data, nullptr);
+
+  if (is_app_success(rv))
+    printf("Update enabled\n");
+
+  return rv;
+}
+
+static uint32_t do_erase(AppClient &app)
+{
+  std::vector<uint8_t> data(sizeof(uint32_t));
+  memcpy(data.data(), &options.erase_code, data.size());
+
+  uint32_t rv = app.Call(NUGGET_PARAM_NUKE_FROM_ORBIT, data, nullptr);
+
+  if (is_app_success(rv))
+    printf("Citadel erase and reboot requested\n");
+
+  return rv;
+}
+
 std::unique_ptr<NuggetClientInterface> select_client()
 {
 #ifdef ANDROID
@@ -324,7 +425,8 @@
 #endif
 }
 
-int update_to_image(const std::vector<uint8_t> &image)
+int execute_commands(const std::vector<uint8_t> &image,
+                     const char *old_passwd, const char *passwd)
 {
   auto client = select_client();
   client->Open();
@@ -336,6 +438,11 @@
 
   /* Try all requested actions in reasonable order, bail out on error */
 
+  if (options.erase_code) {                     /* zero doesn't count */
+    /* whether we succeed or not, only do this */
+    return do_erase(app);
+  }
+
   if (options.version &&
       do_version(app) != APP_SUCCESS) {
     return 2;
@@ -353,10 +460,19 @@
     return 4;
   }
 
+  if (options.change_pw &&
+      do_change_pw(app, old_passwd, passwd) != APP_SUCCESS)
+    return 5;
+
+  if ((options.enable_ro || options.enable_rw) &&
+      do_enable(app, passwd) != APP_SUCCESS)
+    return 6;
+
   if (options.reboot &&
       do_reboot(app) != APP_SUCCESS) {
-    return 5;
+    return 7;
   }
+
   return 0;
 }
 
@@ -367,8 +483,11 @@
   int i;
   int idx = 0;
   char *this_prog;
+  char *old_passwd = 0;
+  char *passwd = 0;
   std::vector<uint8_t> image;
   int got_action = 0;
+  char *e = 0;
 
   this_prog= strrchr(argv[0], '/');
         if (this_prog) {
@@ -398,6 +517,26 @@
       options.reboot = 1;
       got_action = 1;
       break;
+    case OPT_ENABLE_RO:
+      options.enable_ro = 1;
+      got_action = 1;
+      break;
+    case OPT_ENABLE_RW:
+      options.enable_rw = 1;
+      got_action = 1;
+      break;
+    case OPT_CHANGE_PW:
+      options.change_pw = 1;
+      got_action = 1;
+      break;
+    case OPT_ERASE:
+      options.erase_code = (uint32_t)strtoul(optarg, &e, 0);
+      if (!*optarg || (e && *e)) {
+        Error("Invalid argument: \"%s\"\n", optarg);
+        errorcnt++;
+      }
+      got_action = 1;
+      break;
 
       /* generic options below */
     case OPT_DEVICE:
@@ -437,17 +576,42 @@
   if (options.ro || options.rw) {
     if (optind < argc) {
       /* Sets errorcnt on failure */
-      image = read_image_from_file(argv[optind]);
+      image = read_image_from_file(argv[optind++]);
+      if (errorcnt)
+        goto out;
     } else {
       Error("An image file is required with --ro and --rw");
+      goto out;
     }
   }
 
-  if (errorcnt)
-    goto out;
+  if (options.change_pw) {
+    /* one arg provided, maybe the old password isn't set */
+    if (optind < argc) {
+      passwd = argv[optind++];
+    } else {
+      Error("Need a new password at least."
+            " Use '' to clear it.");
+      goto out;
+    }
+    /* two args provided, use both old & new passwords */
+    if (optind < argc) {
+      old_passwd = passwd;
+      passwd = argv[optind++];
+    }
+  }
 
-  /* Okay, let's do something */
-  (void) update_to_image(image);
+  if ((options.enable_ro || options.enable_rw) && !passwd) {
+    if (optind < argc)
+      passwd = argv[optind++];
+    else {
+      Error("Need a password to enable images. Use '' if none.");
+      goto out;
+    }
+  }
+
+  /* Okay, let's do it! */
+  (void) execute_commands(image, old_passwd, passwd);
   /* This is the last action, so fall through either way */
 
 out:
diff --git a/libnos_datagram/include/nos/device.h b/libnos_datagram/include/nos/device.h
index 542908c..21366cc 100644
--- a/libnos_datagram/include/nos/device.h
+++ b/libnos_datagram/include/nos/device.h
@@ -42,6 +42,11 @@
   int (*write)(void *ctx, uint32_t command, const uint8_t *buf, uint32_t len);
 
   /**
+   * Block until an event has happened on the device.
+   */
+  void (*wait_for_interrupt)(void *ctx);
+
+  /**
    * Close the connection to the device.
    *
    * The device must not be used after closing.
diff --git a/nugget/include/app_nugget.h b/nugget/include/app_nugget.h
index aeb9943..c6acfc0 100644
--- a/nugget/include/app_nugget.h
+++ b/nugget/include/app_nugget.h
@@ -85,6 +85,78 @@
   NUGGET_REBOOT_HARD = 1,
 };
 
+
+/*********
+ * Firmware updates are written to flash with invalid headers. If an update
+ * password exists, headers can only be marked valid by providing that
+ * password.
+ */
+
+/*
+ * An unassigned password is defined to be all 0xff, with a don't-care digest.
+ * Anything else must have a valid digest over all password bytes. The password
+ * length is chosen arbitrarily for now, but should always be a fixed size with
+ * all bytes used, to resist brute-force guesses.
+ */
+#define NUGGET_UPDATE_PASSWORD_LEN 32
+struct nugget_app_password {
+  uint32_t digest;      /* first 4 bytes of sha1 of password (little endian) */
+  uint8_t password[NUGGET_UPDATE_PASSWORD_LEN];
+} __packed;
+
+
+enum NUGGET_ENABLE_HEADER {
+  NUGGET_ENABLE_HEADER_RO = 0x01,
+  NUGGET_ENABLE_HEADER_RW = 0x02,
+};
+struct nugget_app_enable_update {
+  struct nugget_app_password  password;
+  uint8_t which_headers;                        /* bit 0 = RO, bit 1 = RW */
+} __packed;
+#define NUGGET_PARAM_ENABLE_UPDATE 0x0003
+/*
+ * Mark the specified image header(s) as valid, if the provided password
+ * matches.
+ *
+ * @param args         struct nugget_app_enable_update
+ * @param arg_len      sizeof(struct nugget_app_enable_update)
+ * @param reply        <none>
+ * @param reply_len    0
+ *
+ * @errors             APP_ERROR_BOGUS_ARGS
+ */
+
+
+struct nugget_app_change_update_password {
+  struct nugget_app_password  old_password;
+  struct nugget_app_password  new_password;
+} __packed;
+#define NUGGET_PARAM_CHANGE_UPDATE_PASSWORD 0x0004
+/*
+ * Change the update password.
+ *
+ * @param args         struct nugget_app_change_update_password
+ * @param arg_len      sizeof(struct nugget_app_change_update_password)
+ * @param reply        <none>
+ * @param reply_len    0
+ *
+ * @errors             APP_ERROR_BOGUS_ARGS
+ */
+
+#define NUGGET_PARAM_NUKE_FROM_ORBIT 0x0005
+#define ERASE_CONFIRMATION 0xc05fefee
+/*
+ * This will erase ALL user secrets and reboot.
+ *
+ * @param args         uint32_t containing the ERASE_CONFIRMATION value
+ * @param arg_len      sizeof(uint32_t)
+ * @param reply        <none>
+ * @param reply_len    0
+ *
+ * @errors             APP_ERROR_BOGUS_ARGS
+ */
+
+
 /****************************************************************************/
 /* Test related commands */
 
@@ -99,23 +171,47 @@
  */
 
 /****************************************************************************/
+/* Support for Power 1.1 HAL */
+
+/*
+ * This struct is specific to Citadel and Nugget OS, but it's enough for the
+ * AP-side implementation to translate into the info required for the HAL
+ * structs.
+ */
+struct nugget_app_low_power_stats {
+  /* All times in usecs */
+  uint64_t hard_reset_count;                    /* Cleared by power loss */
+  uint64_t time_since_hard_reset;
+  /* Below are only since the last hard reset */
+  uint64_t wake_count;
+  uint64_t time_at_last_wake;
+  uint64_t time_spent_awake;
+  uint64_t deep_sleep_count;
+  uint64_t time_at_last_deep_sleep;
+  uint64_t time_spent_in_deep_sleep;
+} __packed;
+
+#define NUGGET_PARAM_GET_LOW_POWER_STATS 0x200
+/*
+ * Return information regarding deep sleep transitions
+ *
+ * @param args         <none>
+ * @param arg_len      0
+ * @param reply        struct nugget_param_get_low_power_stats
+ * @param reply_len    sizeof(struct nugget_param_get_low_power_stats)
+ */
+
+/* UNIMPLEMENTED */
+/* Reseved just in case we decide we need it */
+#define NUGGET_PARAM_CLEAR_LOW_POWER_STATS 0x201
+/* UNIMPLEMENTED */
+
+/****************************************************************************/
 /* These are bringup / debug functions only.
  *
  * TODO(b/65067435): Remove all of these.
  */
 
-#define NUGGET_PARAM_REVERSE 0xbeef
-/*
- * Reverse a sequence of bytes, just to have something to demonstrate.
- *
- * @param args         any arbitrary bytes
- * @param arg_len      any arbitrary length, within reason
- * @param reply        input bytes, in reverse order
- * @param reply_len    same as arg_len
- *
- * @errors             APP_ERROR_TOO_MUCH
- */
-
 #define NUGGET_PARAM_READ32 0xF000
 /*
  * Read a 32-bit value from memory.
diff --git a/nugget/include/signed_header.h b/nugget/include/signed_header.h
index a2746ac..9ce33e6 100644
--- a/nugget/include/signed_header.h
+++ b/nugget/include/signed_header.h
@@ -7,6 +7,8 @@
 
 /* This is citadel */
 #define CHIP_C
+#define MAGIC_DEFAULT (-1u)
+#define MAGIC_VALID  (-2u)
 
 #ifdef __cplusplus
 #include <endian.h>
diff --git a/nugget/proto/nugget/app/keymaster/keymaster.proto b/nugget/proto/nugget/app/keymaster/keymaster.proto
index 65f7b3b..d20d4c5 100644
--- a/nugget/proto/nugget/app/keymaster/keymaster.proto
+++ b/nugget/proto/nugget/app/keymaster/keymaster.proto
@@ -40,7 +40,7 @@
    * require that the keymaster app switch from the
    * transport API to the datagram API.
    */
-  option (nugget.protobuf.request_buffer_size) = 2048;
+  option (nugget.protobuf.request_buffer_size) = 3072;
   option (nugget.protobuf.response_buffer_size) = 2048;
 
   /*
@@ -132,7 +132,9 @@
 };
 message ExportKeyResponse {
   ErrorCode error_code = 1;
-  bytes key_material = 2;
+  Algorithm algorithm = 2;
+  RSAKey rsa = 3;
+  ECKey ec = 4;
 };
 
 // AttestKey
diff --git a/nugget/proto/nugget/app/keymaster/keymaster_types.options b/nugget/proto/nugget/app/keymaster/keymaster_types.options
index f823eb2..18678aa 100644
--- a/nugget/proto/nugget/app/keymaster/keymaster_types.options
+++ b/nugget/proto/nugget/app/keymaster/keymaster_types.options
@@ -1 +1 @@
-nugget.app.keymaster.KeyParameters.params max_count:16
\ No newline at end of file
+nugget.app.keymaster.KeyParameters.params max_count:20
\ No newline at end of file
diff --git a/nugget/proto/nugget/app/keymaster/keymaster_types.proto b/nugget/proto/nugget/app/keymaster/keymaster_types.proto
index 8224e61..f5399ee 100644
--- a/nugget/proto/nugget/app/keymaster/keymaster_types.proto
+++ b/nugget/proto/nugget/app/keymaster/keymaster_types.proto
@@ -77,6 +77,7 @@
 }
 
 message ECKey {
+  /* TODO: should this be EcCurve. */
   uint32 curve_id = 1;
   bytes d = 2;
   bytes x = 3;