Add SHA1 verification support to upgrade-apply 82/275782/4
authorMateusz Majewski <m.majewski2@samsung.com>
Wed, 1 Jun 2022 05:43:52 +0000 (07:43 +0200)
committerMateusz Majewski <m.majewski2@samsung.com>
Thu, 2 Jun 2022 05:55:30 +0000 (07:55 +0200)
Change-Id: I5c53427b8096874c7b5e6a0abc725a9b6bcaa162

upgrade-apply/CMakeLists.txt
upgrade-apply/main.c

index e7cf40e..305cd72 100644 (file)
@@ -8,6 +8,7 @@ set(
        upgrade-apply_SRCS
        main.c
        ss_brotli_patch/ss_brotli_patch.c
+       sha1/sha1.c
 )
 add_executable(upgrade-apply ${upgrade-apply_SRCS})
 target_link_libraries(upgrade-apply PRIVATE PkgConfig::DEPS)
index 63f13d9..fac021f 100644 (file)
@@ -29,6 +29,7 @@
 #include <zlib.h>
 
 #include "ss_brotli_patch/ss_brotli_patch.h"
+#include "sha1/sha1.h"
 
 void fd_cleanup(int *fd)
 {
@@ -64,6 +65,7 @@ struct parse_result {
        const char *dest;
        const char *archive_file;
        const char *patch_orig;
+       const char *dest_sha1;
        enum archive_kind kind;
 };
 
@@ -73,6 +75,7 @@ struct parse_result parse_args(int argc, char **argv)
        const char *dest = NULL;
        const char *archive_file = NULL;
        const char *patch_orig = NULL;
+       const char *dest_sha1 = NULL;
        enum archive_kind kind = -1;
        bool help = false;
 
@@ -83,6 +86,7 @@ struct parse_result parse_args(int argc, char **argv)
                        {"archive-file", required_argument, NULL, 2  },
                        {"kind",         required_argument, NULL, 3  },
                        {"patch-orig",   required_argument, NULL, 4  },
+                       {"dest-sha1",    required_argument, NULL, 5  },
                        {"help",         no_argument,       NULL, 'h'},
                        {0}
                };
@@ -126,6 +130,12 @@ struct parse_result parse_args(int argc, char **argv)
                        patch_orig = optarg;
                        break;
 
+               case 5: // dest-sha1
+                       if (dest_sha1 != NULL)
+                               return (struct parse_result) { .result = PARSE_REPEATED_ARGUMENT };
+                       dest_sha1 = optarg;
+                       break;
+
                case 'h': // help
                        if (help)
                                return (struct parse_result) { .result = PARSE_REPEATED_ARGUMENT };
@@ -163,6 +173,7 @@ struct parse_result parse_args(int argc, char **argv)
                .dest = dest,
                .archive_file = archive_file,
                .patch_orig = patch_orig,
+               .dest_sha1 = dest_sha1,
                .kind = kind,
        };
 }
@@ -229,7 +240,7 @@ ssize_t gzip_write(__attribute__((unused)) int useless_fd, __attribute__((unused
 void help(char *name)
 {
        fprintf(stderr,
-               "Usage: %s --archive ARCHIVE --archive-file FILE --dest DESTINATION --kind KIND [--patch-orig ORIGIN]\n"
+               "Usage: %s --archive ARCHIVE --archive-file FILE --dest DESTINATION --kind KIND [--patch-orig ORIGIN] [--sha1 SHA1]\n"
                "\n"
                "Patches a partition using an archive.\n"
                "It will look for a file named FILE in an archive called ARCHIVE; the archive should have an extension\n"
@@ -238,7 +249,8 @@ void help(char *name)
                " - raw means that the file should be treated as an image to be written,\n"
                " - ss_brotli_patch means that the file should be treated as a delta in the (Tizen-specific) ss_brotli_patch\n"
                "   format. In this case, ORIGIN is needed and specifies the partition that a delta is based on.\n"
-               "The results will be written to the DESTINATION partition.\n",
+               "The results will be written to the DESTINATION partition.\n"
+               "If SHA1 is provided (lowercase hexadecimal), it is compared to the one calculated on DESTINATION after writing.\n",
        name);
 }
 
@@ -283,6 +295,61 @@ int apply_raw(const char *dest, TAR *tar)
        return 0;
 }
 
+int check_sha1(const char *dest, const char *sha1)
+{
+       const size_t SHA1_LEN = 20;
+       const char HEX_DIGITS[] = "0123456789abcdef";
+
+       if (strlen(sha1) != 2 * SHA1_LEN) {
+               fprintf(stderr, "Invalid SHA1 length\n");
+               return -1;
+       }
+
+       __attribute__((cleanup(fd_cleanup))) int dest_fd = open(dest, O_RDONLY);
+       if (dest_fd == -1) {
+               fprintf(stderr, "Couldn't open the target (errno: %m)\n");
+               return -1;
+       }
+
+       SHA1_CTX context;
+       SHA1Init(&context);
+
+       for (;;) {
+               // This size doesn't really matter, so let's just pick a value that is big enough
+               // to not use too many syscalls.
+               unsigned char buf[1 << 16];
+
+               int r_read = read(dest_fd, buf, sizeof(buf));
+               switch (r_read) {
+               case -1:
+                       fprintf(stderr, "Couldn't read from the destination (errno: %m)\n");
+                       return -1;
+               case 0:
+                       // We read all the data. Let's check the results!
+                       {
+                               unsigned char correct_sha1[SHA1_LEN + 1];
+                               SHA1Final(correct_sha1, &context);
+
+                               char correct_sha1_hex[2 * SHA1_LEN + 1];
+                               for (int i = 0; i < SHA1_LEN; ++i) {
+                                       correct_sha1_hex[2 * i]     = HEX_DIGITS[correct_sha1[i] / 16];
+                                       correct_sha1_hex[2 * i + 1] = HEX_DIGITS[correct_sha1[i] % 16];
+                               }
+                               correct_sha1_hex[sizeof(correct_sha1_hex) - 1] = '\0';
+
+                               if (strcmp(correct_sha1_hex, sha1) != 0) {
+                                       fprintf(stderr, "Malformed or invalid SHA1: correct one is %s\n", correct_sha1_hex);
+                                       return -1;
+                               }
+
+                               return 0;
+                       }
+               }
+
+               SHA1Update(&context, buf, r_read);
+       }
+}
+
 int main(int argc, char **argv)
 {
        struct parse_result parsed = parse_args(argc, argv);
@@ -389,6 +456,14 @@ int main(int argc, char **argv)
                        return EXIT_FAILURE;
                }
 
+               if (parsed.dest_sha1 != NULL) {
+                       fprintf(stderr, "Write successful. Now checking SHA1\n");
+                       if (check_sha1(parsed.dest, parsed.dest_sha1) != 0) {
+                               fprintf(stderr, "Couldn't check SHA1; you might need to restore from the backup!\n");
+                               return EXIT_FAILURE;
+                       }
+               }
+
                fprintf(stderr, "Everything written successfully\n");
                return EXIT_SUCCESS;
        }