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;