uri: Add checksum validation
diff --git a/common/lib/misc.h b/common/lib/misc.h
index 17ce04d2..a85e89fc 100644
--- a/common/lib/misc.h
+++ b/common/lib/misc.h
@@ -33,7 +33,7 @@ extern struct volume *boot_volume;
extern bool stage3_loaded;
#endif
-extern bool quiet, serial, editor_enabled;
+extern bool quiet, serial, editor_enabled, hash_mismatch_panic;
bool parse_resolution(size_t *width, size_t *height, size_t *bpp, const char *buf);
diff --git a/common/lib/misc.s2.c b/common/lib/misc.s2.c
index 9c69e0cc..da5c7e8a 100644
--- a/common/lib/misc.s2.c
+++ b/common/lib/misc.s2.c
@@ -6,6 +6,7 @@
bool verbose = false;
bool quiet = false;
bool serial = false;
+bool hash_mismatch_panic = false;
uint8_t bcd_to_int(uint8_t val) {
return (val & 0x0f) + ((val & 0xf0) >> 4) * 10;
diff --git a/common/lib/uri.c b/common/lib/uri.c
index e3517fff..3d6e177a 100644
--- a/common/lib/uri.c
+++ b/common/lib/uri.c
@@ -9,10 +9,13 @@
#include <lib/print.h>
#include <pxe/tftp.h>
#include <tinf.h>
+#include <menu.h>
+#include <lib/readline.h>
+#include <crypt/blake2b.h>
-// A URI takes the form of: resource://root/path
+// A URI takes the form of: resource://root/path#hash
// The following function splits up a URI into its componenets
-bool uri_resolve(char *uri, char **resource, char **root, char **path) {
+bool uri_resolve(char *uri, char **resource, char **root, char **path, char **hash) {
size_t length = strlen(uri) + 1;
char *buf = ext_mem_alloc(length);
memcpy(buf, uri, length);
@@ -51,6 +54,31 @@ bool uri_resolve(char *uri, char **resource, char **root, char **path) {
return false;
*path = uri;
+ // Get hash
+ for (int i = (int)strlen(uri) - 1; i >= 0; i--) {
+ if (uri[i] != '#') {
+ continue;
+ }
+
+ uri[i++] = 0;
+
+ if (hash != NULL) {
+ *hash = uri + i;
+ }
+
+ if (strlen(uri + i) != 128) {
+ panic(true, "Blake2b hash must be 128 characters long");
+ return false;
+ }
+
+ break;
+ }
+
+ for (size_t i = 0; ; i++) {
+ if (uri[i] == 0)
+ break;
+ }
+
return true;
}
@@ -190,8 +218,10 @@ static struct file_handle *uri_boot_dispatch(char *s_part, char *path) {
struct file_handle *uri_open(char *uri) {
struct file_handle *ret;
- char *resource, *root, *path;
- uri_resolve(uri, &resource, &root, &path);
+ char *resource = NULL, *root = NULL, *path = NULL, *hash = NULL;
+ if (!uri_resolve(uri, &resource, &root, &path, &hash)) {
+ return NULL;
+ }
if (resource == NULL) {
panic(true, "No resource specified for URI `%#`.", uri);
@@ -225,6 +255,32 @@ struct file_handle *uri_open(char *uri) {
panic(true, "Resource `%s` not valid.", resource);
}
+ if (hash != NULL && ret != NULL) {
+ uint8_t out_buf[BLAKE2B_OUT_BYTES];
+ void *file_buf = freadall(ret, MEMMAP_BOOTLOADER_RECLAIMABLE);
+ blake2b(out_buf, file_buf, ret->size);
+ uint8_t hash_buf[BLAKE2B_OUT_BYTES];
+
+ for (size_t i = 0; i < sizeof(hash_buf); i++) {
+ hash_buf[i] = digit_to_int(hash[i * 2]) << 4 | digit_to_int(hash[i * 2 + 1]);
+ }
+
+ if (memcmp(hash_buf, out_buf, sizeof(out_buf)) != 0) {
+ if (hash_mismatch_panic) {
+ panic(true, "Blake2b hash for URI `%#` does not match!", uri);
+ } else {
+ print("WARNING: Blake2b hash for URI `%#` does not match!\n"
+ " Press Y to continue, press any other key otherwise...", uri);
+
+ char ch = getchar();
+ if (ch != 'Y' && ch != 'y') {
+ menu(false);
+ }
+ print("\n");
+ }
+ }
+ }
+
if (compressed && ret != NULL) {
struct file_handle *compressed_fd = ext_mem_alloc(sizeof(struct file_handle));
fread(ret, &compressed_fd->size, ret->size - 4, sizeof(uint32_t));
diff --git a/common/lib/uri.h b/common/lib/uri.h
index a883a53a..4f15907a 100644
--- a/common/lib/uri.h
+++ b/common/lib/uri.h
@@ -4,7 +4,7 @@
#include <stdbool.h>
#include <fs/file.h>
-bool uri_resolve(char *uri, char **resource, char **root, char **path);
+bool uri_resolve(char *uri, char **resource, char **root, char **path, char **hash);
struct file_handle *uri_open(char *uri);
#endif
diff --git a/common/menu.c b/common/menu.c
index 4977c33c..6fcbad26 100644
--- a/common/menu.c
+++ b/common/menu.c
@@ -653,6 +653,9 @@ noreturn void _menu(bool first_run) {
char *serial_str = config_get_value(NULL, 0, "SERIAL");
serial = serial_str != NULL && strcmp(serial_str, "yes") == 0;
+ char *hash_mismatch_panic_str = config_get_value(NULL, 0, "HASH_MISMATCH_PANIC");
+ hash_mismatch_panic = hash_mismatch_panic_str == NULL || strcmp(hash_mismatch_panic_str, "yes") == 0;
+
char *randomise_mem_str = config_get_value(NULL, 0, "RANDOMISE_MEMORY");
if (randomise_mem_str == NULL)
randomise_mem_str = config_get_value(NULL, 0, "RANDOMIZE_MEMORY");
