avb_storage: fix nonce update; enable clearing

Nonce updating was copying without the
incoming offset and was not using the VERSION_SIZE
constant. This changes fixes the storage as well as
adds a mechanism for resetting the nonce when not
in production mode (without a reinstall) and binds it into
the ese_boot_tool and the boot interface (making a
framework for factory rese without adding it explicitly).

Test: Used increasing nonces and made sure they were single use and only
newer ones worked by: set-carrier, set-production, unlock-last,
unlock-newer, unset-production, set-carrier, ...
Bug: none

Change-Id: I038229c1bb089b2d5f98faeed81714dfc1c32e36
diff --git a/apps/boot/boot.c b/apps/boot/boot.c
index fe381af..c6f8c72 100644
--- a/apps/boot/boot.c
+++ b/apps/boot/boot.c
@@ -36,6 +36,8 @@
 const uint8_t kSetLockState[] = {0x80, 0x08, 0x00, 0x00, 0x00};
 const uint8_t kSetProduction[] = {0x80, 0x0a};
 const uint8_t kCarrierLockTest[] = {0x80, 0x0c, 0x00, 0x00};
+const uint8_t kFactoryReset[] = {0x80, 0x0e, 0x00, 0x00};
+const uint8_t kLockReset[] = {0x80, 0x0e, 0x01, 0x00};
 
 EseAppResult check_apdu_status(uint8_t code[2]) {
   if (code[0] == 0x90 && code[1] == 0x00) {
@@ -607,6 +609,46 @@
   return ESE_APP_RESULT_OK;
 }
 
+ESE_API EseAppResult ese_boot_reset_locks(struct EseBootSession *session) {
+  struct EseSgBuffer tx[2];
+  struct EseSgBuffer rx[1];
+  int rx_len;
+  if (!session || !session->ese || !session->active) {
+    return ESE_APP_RESULT_ERROR_ARGUMENTS;
+  }
+
+  uint8_t chan = kLockReset[0] | session->channel_id;
+  tx[0].base = &chan;
+  tx[0].len = 1;
+  tx[1].base = (uint8_t *)&kLockReset[1];
+  tx[1].len = sizeof(kLockReset) - 1;
+
+  uint8_t reply[4]; // App reply or APDU error.
+  rx[0].base = &reply[0];
+  rx[0].len = sizeof(reply);
+
+  rx_len = ese_transceive_sg(session->ese, tx, 2, rx, 1);
+  if (rx_len < 2 || ese_error(session->ese)) {
+    ALOGE("ese_boot_reset_locks: comms failure.");
+    return ESE_APP_RESULT_ERROR_COMM_FAILED;
+  }
+  if (rx_len == 2) {
+    ALOGE("ese_boot_reset_locks: SE exception");
+    EseAppResult ret = check_apdu_status(&reply[0]);
+    return ret;
+  }
+  // Expect the full payload plus the aplet status and the completion code.
+  if (rx_len != 4) {
+    ALOGE("ese_boot_reset_locks: not enough data (%d)", rx_len);
+    return ese_make_app_result(reply[0], reply[1]);
+  }
+  if (reply[0] != 0x0 || reply[1] != 0x0) {
+    ALOGE("ese_boot_reset_locks: applet error code %x %x", reply[0], reply[1]);
+    return ese_make_app_result(reply[0], reply[1]);
+  }
+  return ESE_APP_RESULT_OK;
+}
+
 ESE_API EseAppResult ese_boot_get_state(struct EseBootSession *session,
                                         uint8_t *state, uint16_t maxSize) {
   struct EseSgBuffer tx[4];
diff --git a/apps/boot/card/src/com/android/verifiedboot/storage/CarrierLock.java b/apps/boot/card/src/com/android/verifiedboot/storage/CarrierLock.java
index db0ac32..1a4c7f7 100644
--- a/apps/boot/card/src/com/android/verifiedboot/storage/CarrierLock.java
+++ b/apps/boot/card/src/com/android/verifiedboot/storage/CarrierLock.java
@@ -433,7 +433,7 @@
             // was seen or if we're not production() to assure it doesn't get
             // rolled forward.
             if (resp == 0) {
-                Util.arrayCopy(lockMeta, (short)1,
+                Util.arrayCopy(lockMeta, (short)(VERSION_SIZE + lockMetaOffset),
                                storage, (short)(1 + storageOffset),
                                (short)NONCE_SIZE);
             }
diff --git a/apps/boot/card/src/com/android/verifiedboot/storage/Storage.java b/apps/boot/card/src/com/android/verifiedboot/storage/Storage.java
index d01680b..9c2bd46 100644
--- a/apps/boot/card/src/com/android/verifiedboot/storage/Storage.java
+++ b/apps/boot/card/src/com/android/verifiedboot/storage/Storage.java
@@ -79,6 +79,10 @@
     private final static byte INS_SET_LOCK = (byte) 0x08;
     private final static byte INS_SET_PRODUCTION = (byte) 0x0a;
     private final static byte INS_CARRIER_LOCK_TEST = (byte) 0x0c;
+    private final static byte INS_RESET = (byte) 0x0e;
+
+    private final static byte RESET_FACTORY = (byte) 0x0;
+    private final static byte RESET_LOCKS = (byte) 0x1;
 
     private final static short NO_METADATA = (short) 0;
     private final static short NO_REQ_LOCKS = (short) 0;
@@ -465,9 +469,23 @@
             if (numBytes != bytesRead) {
                 resp = 0x0100;
             }
-            resp = ((CarrierLock)locks[0]).testVector(buffer, cdataOffset, bytesRead);
+            resp = ((CarrierLock)locks[LOCK_CARRIER]).testVector(buffer, cdataOffset, bytesRead);
             sendResponseCode(apdu, resp);
             return;
+        /* reset(0x0=factory 0x1=locks) {} */
+        case INS_RESET:
+            if (p1 != RESET_LOCKS) {
+              /* Not implemented */
+              resp = 0x0001;
+              sendResponseCode(apdu, resp);
+            }
+            if (globalState.production() == true) {
+              resp = 0x0100;
+              sendResponseCode(apdu, resp);
+            }
+            Util.arrayFillNonAtomic(lockStorage, (short) 0,
+                                    (short) lockStorage.length, (byte) 0x00);
+            return;
         default:
             ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
         }
diff --git a/apps/boot/ese_boot_tool.cpp b/apps/boot/ese_boot_tool.cpp
index 80aa3d9..cffd8ee 100644
--- a/apps/boot/ese_boot_tool.cpp
+++ b/apps/boot/ese_boot_tool.cpp
@@ -46,6 +46,7 @@
     "             boot <byte>\n"
     "             owner 0\n"
     "             owner <non-zero byte> <keyValue>\n"
+    "         reset\n"
     "    verify-key test <blob>\n"
     "    verify-key auto\n"
     "\n"
@@ -269,7 +270,9 @@
   EseBootLockId lockId;
   uint16_t lockMetaLen = 0;
   uint8_t lockMeta[1024];
-  if (args[2] == "carrier") {
+  if (args[1] == "reset") {
+    // No work.
+  } else if (args[2] == "carrier") {
     lockId = kEseBootLockIdCarrier;
   } else if (args[2] == "device") {
     lockId = kEseBootLockIdDevice;
@@ -304,6 +307,16 @@
     fprintf(stderr, "lock: failed to get '%s' (%.8x)\n", args[2].c_str(), res);
     handle_error(session->ese, res);
     return 2;
+  } else if (args[1] == "reset") {
+    res = ese_boot_reset_locks(session);
+    if (res == ESE_APP_RESULT_OK) {
+      printf("done.\n");
+      return 0;
+    } else {
+      fprintf(stderr, "lock: failed to reset (%.8x)\n", res);
+      handle_error(session->ese, res);
+      return 3;
+    }
   } else if (args[1] == "set") {
     if (args.size() < 4) {
       fprintf(stderr, "lock set: not enough arguments supplied\n");
diff --git a/apps/boot/include/ese/app/boot.h b/apps/boot/include/ese/app/boot.h
index 72892bc..c0cbff8 100644
--- a/apps/boot/include/ese/app/boot.h
+++ b/apps/boot/include/ese/app/boot.h
@@ -186,10 +186,19 @@
 /**
  * Reads a uint64_t from |slot| into |value|.
  *
- * @retuns ESE_APP_RESULT_OK on success.
+ * @returns ESE_APP_RESULT_OK on success.
  */
 EseAppResult ese_boot_rollback_index_read(struct EseBootSession *session, uint8_t slot, uint64_t *value);
 
+
+/**
+ * Resets all lock state -- including internal metadata.
+ * This should only be called in factory or under test.
+ *
+ * @returns ESE_APP_RESULT_OK on success.
+ */
+EseAppResult ese_boot_reset_locks(struct EseBootSession *session);
+
 #ifdef __cplusplus
 }  /* extern "C" */
 #endif