From 32c8eb81af916f04fd42e7294f699b10474beef6 Mon Sep 17 00:00:00 2001 From: Dees_Troy Date: Tue, 11 Sep 2012 15:28:06 -0400 Subject: Make text appear in GUI console, Zip install works. Move TW zip install code to C++ so that it can use the ui->functions. Bring in mincrypt code to fix a crash during signature checking. --- Android.mk | 7 +- common.h | 7 +- extra-functions.c | 481 ----------------------------------------- extra-functions.h | 6 - gui/action.cpp | 3 + gui/console.cpp | 4 + gui/gui.h | 2 + install.cpp | 2 +- partitionmanager.cpp | 4 +- prebuilt/bbinstall.sh | 5 + recovery.cpp | 5 +- screen_ui.cpp | 12 +- twinstall.cpp | 580 ++++++++++++++++++++++++++++++++++++++++++++++++++ twinstall.h | 32 +++ twmincrypt/twrsa.c | 199 +++++++++++++++++ twmincrypt/twrsa.h | 56 +++++ twmincrypt/twsha.c | 307 ++++++++++++++++++++++++++ twmincrypt/twsha.h | 63 ++++++ 18 files changed, 1276 insertions(+), 499 deletions(-) create mode 100644 twinstall.cpp create mode 100644 twinstall.h create mode 100644 twmincrypt/twrsa.c create mode 100644 twmincrypt/twrsa.h create mode 100644 twmincrypt/twsha.c create mode 100644 twmincrypt/twsha.h diff --git a/Android.mk b/Android.mk index c8f453a79..6d3be1dec 100644 --- a/Android.mk +++ b/Android.mk @@ -35,7 +35,10 @@ LOCAL_SRC_FILES += \ firmware.c \ partition.cpp \ partitionmanager.cpp \ - mtdutils/mtdutils.c + mtdutils/mtdutils.c \ + twinstall.cpp \ + twmincrypt/twrsa.c \ + twmincrypt/twsha.c ifeq ($(TARGET_RECOVERY_REBOOT_SRC),) LOCAL_SRC_FILES += reboot.c @@ -64,7 +67,7 @@ LOCAL_STATIC_LIBRARIES := LOCAL_SHARED_LIBRARIES := LOCAL_STATIC_LIBRARIES += libmtdutils -LOCAL_STATIC_LIBRARIES += libext4_utils libminadbd libminzip libunz libmincrypt +LOCAL_STATIC_LIBRARIES += libext4_utils libminadbd libminzip libunz LOCAL_STATIC_LIBRARIES += libminuitwrp libpixelflinger_static libpng libjpegtwrp libgui LOCAL_SHARED_LIBRARIES += libz libc libstlport libcutils libstdc++ diff --git a/common.h b/common.h index 4122e4066..37c0e4cff 100644 --- a/common.h +++ b/common.h @@ -23,11 +23,12 @@ extern "C" { #endif -#define ui_print(...) fprintf(stdout, __VA_ARGS__) -#define ui_print_overwrite(...) fprintf(stdout, __VA_ARGS__) +#define ui_print(...) gui_print(__VA_ARGS__) +#define ui_print_overwrite(...) gui_print_overwrite(__VA_ARGS__) +#include "gui/gui.h" // TODO: restore ui_print for LOGE -#define LOGE(...) fprintf(stdout, "E:" __VA_ARGS__) +#define LOGE(...) gui_print("E:" __VA_ARGS__) #define LOGW(...) fprintf(stdout, "W:" __VA_ARGS__) #define LOGI(...) fprintf(stdout, "I:" __VA_ARGS__) diff --git a/extra-functions.c b/extra-functions.c index 9157e2397..e01ab83b4 100644 --- a/extra-functions.c +++ b/extra-functions.c @@ -222,487 +222,6 @@ int __pclose(FILE *iop) { return (pid == -1 ? -1 : pstat); } -char* get_path (char* path) { - char *s; - - /* Go to the end of the string. */ - s = path + strlen(path) - 1; - - /* Strip off trailing /s (unless it is also the leading /). */ - while (path < s && s[0] == '/') - s--; - - /* Strip the last component. */ - while (path <= s && s[0] != '/') - s--; - - while (path < s && s[0] == '/') - s--; - - if (s < path) - return "."; - - s[1] = '\0'; - return path; -} - -char* basename(char* name) { - const char* base; - for (base = name; *name; name++) - { - if(*name == '/') - { - base = name + 1; - } - } - return (char *) base; -} - -/* - Checks md5 for a path - Return values: - -1 : MD5 does not exist - 0 : Failed - 1 : Success -*/ -int check_md5(char* path) { - int o; - char cmd[PATH_MAX + 30]; - char md5file[PATH_MAX + 40]; - strcpy(md5file, path); - strcat(md5file, ".md5"); - char dirpath[PATH_MAX]; - char* file; - if (access(md5file, F_OK ) != -1) { - strcpy(dirpath, md5file); - get_path(dirpath); - chdir(dirpath); - file = basename(md5file); - sprintf(cmd, "/sbin/busybox md5sum -c '%s'", file); - FILE * cs = __popen(cmd, "r"); - char cs_s[PATH_MAX + 50]; - fgets(cs_s, PATH_MAX + 50, cs); - char* OK = strstr(cs_s, "OK"); - if (OK != NULL) { - printf("MD5 is good. returning 1\n"); - o = 1; - } - else { - printf("MD5 is bad. return -2\n"); - o = -2; - } - - __pclose(cs); - } - else { - //No md5 file - printf("setting o to -1\n"); - o = -1; - } - - return o; -} - -int TWtry_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) { - const ZipEntry* binary_entry = - mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME); - if (binary_entry == NULL) { - mzCloseZipArchive(zip); - return INSTALL_CORRUPT; - } - const char* binary = "/tmp/update_binary"; - unlink(binary); - int fd = creat(binary, 0755); - if (fd < 0) { - mzCloseZipArchive(zip); - LOGE("Can't make %s\n", binary); - return INSTALL_ERROR; - } - bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd); - close(fd); - mzCloseZipArchive(zip); - - if (!ok) { - LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME); - return INSTALL_ERROR; - } - - int pipefd[2]; - pipe(pipefd); - - // When executing the update binary contained in the package, the - // arguments passed are: - // - // - the version number for this interface - // - // - an fd to which the program can write in order to update the - // progress bar. The program can write single-line commands: - // - // progress - // fill up the next part of of the progress bar - // over seconds. If is zero, use - // set_progress commands to manually control the - // progress of this segment of the bar - // - // set_progress - // should be between 0.0 and 1.0; sets the - // progress bar within the segment defined by the most - // recent progress command. - // - // firmware <"hboot"|"radio"> - // arrange to install the contents of in the - // given partition on reboot. - // - // (API v2: may start with "PACKAGE:" to - // indicate taking a file from the OTA package.) - // - // (API v3: this command no longer exists.) - // - // ui_print - // display on the screen. - // - // - the name of the package zip file. - // - - const char** args = (const char**)malloc(sizeof(char*) * 5); - args[0] = binary; - args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mk - char* temp = (char*)malloc(10); - sprintf(temp, "%d", pipefd[1]); - args[2] = temp; - args[3] = (char*)path; - args[4] = NULL; - - pid_t pid = fork(); - if (pid == 0) { - close(pipefd[0]); - execv(binary, (char* const*)args); - fprintf(stdout, "E:Can't run %s (error)\n", binary); - _exit(-1); - } - close(pipefd[1]); - *wipe_cache = 0; - - char buffer[1024]; - FILE* from_child = fdopen(pipefd[0], "r"); - LOGI("8\n"); - while (fgets(buffer, sizeof(buffer), from_child) != NULL) { - char* command = strtok(buffer, " \n"); - if (command == NULL) { - continue; - } else if (strcmp(command, "progress") == 0) { - char* fraction_s = strtok(NULL, " \n"); - char* seconds_s = strtok(NULL, " \n"); - - float fraction = strtof(fraction_s, NULL); - int seconds = strtol(seconds_s, NULL, 10); - - //ui->ShowProgress(fraction * (1-VERIFICATION_PROGRESS_FRACTION), seconds); - } else if (strcmp(command, "set_progress") == 0) { - char* fraction_s = strtok(NULL, " \n"); - float fraction = strtof(fraction_s, NULL); - //ui->SetProgress(fraction); - } else if (strcmp(command, "ui_print") == 0) { - char* str = strtok(NULL, "\n"); - if (str) { - //ui->Print("%s", str); - } else { - //ui->Print("\n"); - } - } else if (strcmp(command, "wipe_cache") == 0) { - *wipe_cache = 1; - } else if (strcmp(command, "clear_display") == 0) { - //ui->SetBackground(RecoveryUI::NONE); - } else { - LOGE("unknown command [%s]\n", command); - } - } - fclose(from_child); - - int status; - waitpid(pid, &status, 0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status)); - return INSTALL_ERROR; - } - return INSTALL_SUCCESS; -} - -// Look for an RSA signature embedded in the .ZIP file comment given -// the path to the zip. Verify it matches one of the given public -// keys. -// -// Return VERIFY_SUCCESS, VERIFY_FAILURE (if any error is encountered -// or no key matches the signature). - -int TWverify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKeys) { - //ui->SetProgress(0.0); - - FILE* f = fopen(path, "rb"); - if (f == NULL) { - LOGE("failed to open %s (%s)\n", path, strerror(errno)); - return VERIFY_FAILURE; - } - - // An archive with a whole-file signature will end in six bytes: - // - // (2-byte signature start) $ff $ff (2-byte comment size) - // - // (As far as the ZIP format is concerned, these are part of the - // archive comment.) We start by reading this footer, this tells - // us how far back from the end we have to start reading to find - // the whole comment. - -#define FOOTER_SIZE 6 - - if (fseek(f, -FOOTER_SIZE, SEEK_END) != 0) { - LOGE("failed to seek in %s (%s)\n", path, strerror(errno)); - fclose(f); - return VERIFY_FAILURE; - } - - unsigned char footer[FOOTER_SIZE]; - if (fread(footer, 1, FOOTER_SIZE, f) != FOOTER_SIZE) { - LOGE("failed to read footer from %s (%s)\n", path, strerror(errno)); - fclose(f); - return VERIFY_FAILURE; - } - - if (footer[2] != 0xff || footer[3] != 0xff) { - fclose(f); - return VERIFY_FAILURE; - } - - size_t comment_size = footer[4] + (footer[5] << 8); - size_t signature_start = footer[0] + (footer[1] << 8); - LOGI("comment is %d bytes; signature %d bytes from end\n", - comment_size, signature_start); - - if (signature_start - FOOTER_SIZE < RSANUMBYTES) { - // "signature" block isn't big enough to contain an RSA block. - LOGE("signature is too short\n"); - fclose(f); - return VERIFY_FAILURE; - } - -#define EOCD_HEADER_SIZE 22 - - // The end-of-central-directory record is 22 bytes plus any - // comment length. - size_t eocd_size = comment_size + EOCD_HEADER_SIZE; - - if (fseek(f, -eocd_size, SEEK_END) != 0) { - LOGE("failed to seek in %s (%s)\n", path, strerror(errno)); - fclose(f); - return VERIFY_FAILURE; - } - - // Determine how much of the file is covered by the signature. - // This is everything except the signature data and length, which - // includes all of the EOCD except for the comment length field (2 - // bytes) and the comment data. - size_t signed_len = ftell(f) + EOCD_HEADER_SIZE - 2; - - unsigned char* eocd = (unsigned char*)malloc(eocd_size); - if (eocd == NULL) { - LOGE("malloc for EOCD record failed\n"); - fclose(f); - return VERIFY_FAILURE; - } - if (fread(eocd, 1, eocd_size, f) != eocd_size) { - LOGE("failed to read eocd from %s (%s)\n", path, strerror(errno)); - fclose(f); - return VERIFY_FAILURE; - } - - // If this is really is the EOCD record, it will begin with the - // magic number $50 $4b $05 $06. - if (eocd[0] != 0x50 || eocd[1] != 0x4b || - eocd[2] != 0x05 || eocd[3] != 0x06) { - LOGE("signature length doesn't match EOCD marker\n"); - fclose(f); - return VERIFY_FAILURE; - } - - size_t i; - for (i = 4; i < eocd_size-3; ++i) { - if (eocd[i ] == 0x50 && eocd[i+1] == 0x4b && - eocd[i+2] == 0x05 && eocd[i+3] == 0x06) { - // if the sequence $50 $4b $05 $06 appears anywhere after - // the real one, minzip will find the later (wrong) one, - // which could be exploitable. Fail verification if - // this sequence occurs anywhere after the real one. - LOGE("EOCD marker occurs after start of EOCD\n"); - fclose(f); - return VERIFY_FAILURE; - } - } - -#define BUFFER_SIZE 4096 - - SHA_CTX ctx; - SHA_init(&ctx); - unsigned char* buffer = (unsigned char*)malloc(BUFFER_SIZE); - if (buffer == NULL) { - LOGE("failed to alloc memory for sha1 buffer\n"); - fclose(f); - return VERIFY_FAILURE; - } - - double frac = -1.0; - size_t so_far = 0; - fseek(f, 0, SEEK_SET); - while (so_far < signed_len) { - size_t size = BUFFER_SIZE; - if (signed_len - so_far < size) size = signed_len - so_far; - if (fread(buffer, 1, size, f) != size) { - LOGE("failed to read data from %s (%s)\n", path, strerror(errno)); - fclose(f); - return VERIFY_FAILURE; - } - SHA_update(&ctx, buffer, size); - so_far += size; - double f = so_far / (double)signed_len; - if (f > frac + 0.02 || size == so_far) { - //ui->SetProgress(f); - frac = f; - } - } - fclose(f); - free(buffer); - - const uint8_t* sha1 = SHA_final(&ctx); - for (i = 0; i < numKeys; ++i) { - // The 6 bytes is the "(signature_start) $ff $ff (comment_size)" that - // the signing tool appends after the signature itself. - if (RSA_verify(pKeys+i, eocd + eocd_size - 6 - RSANUMBYTES, - RSANUMBYTES, sha1)) { - LOGI("whole-file signature verified against key %d\n", i); - free(eocd); - return VERIFY_SUCCESS; - } - } - free(eocd); - LOGE("failed to verify whole-file signature\n"); - return VERIFY_FAILURE; -} - -// Reads a file containing one or more public keys as produced by -// DumpPublicKey: this is an RSAPublicKey struct as it would appear -// as a C source literal, eg: -// -// "{64,0xc926ad21,{1795090719,...,-695002876},{-857949815,...,1175080310}}" -// -// (Note that the braces and commas in this example are actual -// characters the parser expects to find in the file; the ellipses -// indicate more numbers omitted from this example.) -// -// The file may contain multiple keys in this format, separated by -// commas. The last key must not be followed by a comma. -// -// Returns NULL if the file failed to parse, or if it contain zero keys. -static RSAPublicKey* -TWload_keys(const char* filename, int* numKeys) { - RSAPublicKey* out = NULL; - *numKeys = 0; - - FILE* f = fopen(filename, "r"); - if (f == NULL) { - LOGE("opening %s: ERROR\n", filename); - goto exit; - } - - { - int i; - bool done = false; - while (!done) { - ++*numKeys; - out = (RSAPublicKey*)realloc(out, *numKeys * sizeof(RSAPublicKey)); - RSAPublicKey* key = out + (*numKeys - 1); - if (fscanf(f, " { %i , 0x%x , { %u", - &(key->len), &(key->n0inv), &(key->n[0])) != 3) { - goto exit; - } - if (key->len != RSANUMWORDS) { - LOGE("key length (%d) does not match expected size\n", key->len); - goto exit; - } - for (i = 1; i < key->len; ++i) { - if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit; - } - if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit; - for (i = 1; i < key->len; ++i) { - if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit; - } - fscanf(f, " } } "); - - // if the line ends in a comma, this file has more keys. - switch (fgetc(f)) { - case ',': - // more keys to come. - break; - - case EOF: - done = true; - break; - - default: - LOGE("unexpected character between keys\n"); - goto exit; - } - } - } - - fclose(f); - return out; - -exit: - if (f) fclose(f); - free(out); - *numKeys = 0; - return NULL; -} - -int TWinstall_zip(const char* path, int* wipe_cache) { - int err; - - if (DataManager_GetIntValue(TW_SIGNED_ZIP_VERIFY_VAR)) { - int numKeys; - RSAPublicKey* loadedKeys = TWload_keys(PUBLIC_KEYS_FILE, &numKeys); - if (loadedKeys == NULL) { - LOGE("Failed to load keys\n"); - return -1; - } - LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE); - - // Give verification half the progress bar... - //ui->Print("Verifying update package...\n"); - //ui->SetProgressType(RecoveryUI::DETERMINATE); - //ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME); - - err = TWverify_file(path, loadedKeys, numKeys); - free(loadedKeys); - LOGI("verify_file returned %d\n", err); - if (err != VERIFY_SUCCESS) { - LOGE("signature verification failed\n"); - return -1; - } - } - /* Try to open the package. - */ - ZipArchive zip; - err = mzOpenZipArchive(path, &zip); - if (err != 0) { - LOGE("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad"); - return INSTALL_CORRUPT; - } - - /* Verify and install the contents of the package. - */ - //ui->Print("Installing update...\n"); - return TWtry_update_binary(path, &zip, wipe_cache); -} - //partial kangbang from system/vold #ifndef CUSTOM_LUN_FILE #define CUSTOM_LUN_FILE "/sys/devices/platform/usb_mass_storage/lun%d/file" diff --git a/extra-functions.h b/extra-functions.h index d3a43cd99..6145e7722 100644 --- a/extra-functions.h +++ b/extra-functions.h @@ -8,12 +8,6 @@ int __system(const char *command); FILE * __popen(const char *program, const char *type); int __pclose(FILE *iop); -// Install Zip functions -int TWtry_update_binary(const char *path, ZipArchive *zip, int* wipe_cache); -static RSAPublicKey* TWload_keys(const char* filename, int* numKeys); -int TWverify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKeys); -int TWinstall_zip(const char* path, int* wipe_cache); - void wipe_dalvik_cache(); void wipe_battery_stats(); void wipe_rotate_data(); diff --git a/gui/action.cpp b/gui/action.cpp index 3e19a6132..a1f7dd3dc 100644 --- a/gui/action.cpp +++ b/gui/action.cpp @@ -28,7 +28,9 @@ extern "C" { #include "../recovery_ui.h" #include "../extra-functions.h" #include "../variables.h" +#include "../twinstall.h" +int TWinstall_zip(const char* path, int* wipe_cache); void fix_perms(); void wipe_dalvik_cache(void); int check_backup_name(int show_error); @@ -656,6 +658,7 @@ int GUIAction::doAction(Action action, int isThreaded /* = 0 */) ui_print("TWRP injection complete.\n"); } } + PartitionManager.Update_System_Details(); operation_end(ret_val, simulate); return 0; } diff --git a/gui/console.cpp b/gui/console.cpp index 6d53ed101..b7713c70c 100644 --- a/gui/console.cpp +++ b/gui/console.cpp @@ -38,6 +38,8 @@ extern "C" void gui_print(const char *fmt, ...) vsnprintf(buf, 512, fmt, ap); va_end(ap); + fputs(buf, stdout); + char *start, *next; if (buf[0] == '\n' && strlen(buf) < 2) { @@ -75,6 +77,8 @@ extern "C" void gui_print_overwrite(const char *fmt, ...) vsnprintf(buf, 512, fmt, ap); va_end(ap); + fputs(buf, stdout); + // Pop the last line, and we can continue if (!gConsole.empty()) gConsole.pop_back(); diff --git a/gui/gui.h b/gui/gui.h index f6745d7ee..948e11b36 100644 --- a/gui/gui.h +++ b/gui/gui.h @@ -5,6 +5,8 @@ int gui_console_only(); int gui_init(); int gui_loadResources(); int gui_start(); +void gui_print(const char *fmt, ...); +void gui_print_overwrite(const char *fmt, ...); #endif // _GUI_HEADER diff --git a/install.cpp b/install.cpp index 8d36cb553..4d73aa9b0 100644 --- a/install.cpp +++ b/install.cpp @@ -46,7 +46,7 @@ static const float DEFAULT_FILES_PROGRESS_FRACTION = 0.4; static const float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1; // If the package contains an update binary, extract it and run it. -extern "C" int +static int try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) { const ZipEntry* binary_entry = mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME); diff --git a/partitionmanager.cpp b/partitionmanager.cpp index 47cc84746..21752a0cb 100644 --- a/partitionmanager.cpp +++ b/partitionmanager.cpp @@ -629,7 +629,7 @@ void TWPartitionManager::Update_System_Details(void) { std::vector::iterator iter; int data_size = 0; - LOGI("Updating system details...\n"); + ui_print("Updating partition details...\n"); for (iter = Partitions.begin(); iter != Partitions.end(); iter++) { if ((*iter)->Can_Be_Mounted) { (*iter)->Update_Size(true); @@ -696,7 +696,7 @@ int TWPartitionManager::Decrypt_Device(string Password) { DataManager::SetValue(TW_IS_DECRYPTED, 1); dat->Is_Decrypted = true; dat->Decrypted_Block_Device = crypto_blkdev; - LOGI("Data successfully decrypted, new block device: '%s'\n", crypto_blkdev); + ui_print("Data successfully decrypted, new block device: '%s'\n", crypto_blkdev); // Sleep for a bit so that the device will be ready sleep(1); Update_System_Details(); diff --git a/prebuilt/bbinstall.sh b/prebuilt/bbinstall.sh index c59fec2b7..2aab96958 100755 --- a/prebuilt/bbinstall.sh +++ b/prebuilt/bbinstall.sh @@ -1,5 +1,10 @@ #!/sbin/sh +if [ -f "/sbin/[" ]; +then +exit +fi + for cmd in $(/sbin/busybox --list); do /sbin/busybox ln -s /sbin/busybox /sbin/$cmd done diff --git a/recovery.cpp b/recovery.cpp index 450f36330..567a42e54 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -54,7 +54,6 @@ extern "C" { #include "variables.h" TWPartitionManager PartitionManager; -char device_id[64]; struct selabel_handle *sehandle; @@ -793,8 +792,8 @@ main(int argc, char **argv) { // set by init umask(0); - //Device* device = make_device(); - //ui = device->GetUI(); + Device* device = make_device(); + ui = device->GetUI(); //ui->Init(); //ui->SetBackground(RecoveryUI::NONE); diff --git a/screen_ui.cpp b/screen_ui.cpp index 60a0ad496..4441f7abc 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -36,7 +36,9 @@ extern "C" { #include "minuitwrp/minui.h" int twgr_text(int x, int y, const char *s); +#include "gui/gui.h" } +#include "data.hpp" #define CHAR_WIDTH 10 #define CHAR_HEIGHT 18 @@ -220,7 +222,7 @@ void ScreenRecoveryUI::update_screen_locked() // Updates only the progress bar, if possible, otherwise redraws the screen. // Should only be called with updateMutex locked. void ScreenRecoveryUI::update_progress_locked() -{ +{return; if (show_text || !pagesIdentical) { draw_screen_locked(); // Must redraw the whole screen pagesIdentical = true; @@ -364,6 +366,9 @@ void ScreenRecoveryUI::SetProgressType(ProgressType type) void ScreenRecoveryUI::ShowProgress(float portion, float seconds) { + DataManager::SetValue("ui_progress_portion", (float)(portion * 100.0)); + DataManager::SetValue("ui_progress_frames", seconds * 30); + pthread_mutex_lock(&updateMutex); progressBarType = DETERMINATE; progressScopeStart += progressScopeSize; @@ -377,6 +382,8 @@ void ScreenRecoveryUI::ShowProgress(float portion, float seconds) void ScreenRecoveryUI::SetProgress(float fraction) { + DataManager::SetValue("ui_progress", (float) (fraction * 100.0)); return; + pthread_mutex_lock(&updateMutex); if (fraction < 0.0) fraction = 0.0; if (fraction > 1.0) fraction = 1.0; @@ -400,6 +407,9 @@ void ScreenRecoveryUI::Print(const char *fmt, ...) vsnprintf(buf, 256, fmt, ap); va_end(ap); + gui_print("%s", buf); + return; + fputs(buf, stdout); // This can get called before ui_init(), so be careful. diff --git a/twinstall.cpp b/twinstall.cpp new file mode 100644 index 000000000..0c3d8375c --- /dev/null +++ b/twinstall.cpp @@ -0,0 +1,580 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "common.h" +#include "twmincrypt/twrsa.h" +#include "twmincrypt/twsha.h" +#include "minui/minui.h" +#include "minzip/SysUtil.h" +#include "minzip/Zip.h" +#include "mtdutils/mounts.h" +#include "mtdutils/mtdutils.h" +#include "roots.h" +#include "verifier.h" +#include "ui.h" +#include "variables.h" +#include "data.hpp" +#include "partitions.hpp" + +extern "C" { +#include "extra-functions.h" +int __system(const char *command); +}; + +extern RecoveryUI* ui; + +#define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary" +#define PUBLIC_KEYS_FILE "/res/keys" + +// Default allocation of progress bar segments to operations +static const int VERIFICATION_PROGRESS_TIME = 60; +static const float VERIFICATION_PROGRESS_FRACTION = 0.25; +static const float DEFAULT_FILES_PROGRESS_FRACTION = 0.4; +static const float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1; + +enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT }; + +// Look for an RSA signature embedded in the .ZIP file comment given +// the path to the zip. Verify it matches one of the given public +// keys. +// +// Return VERIFY_SUCCESS, VERIFY_FAILURE (if any error is encountered +// or no key matches the signature). + +int TWverify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKeys) { + ui->SetProgress(0.0); + + FILE* f = fopen(path, "rb"); + if (f == NULL) { + LOGE("failed to open %s (%s)\n", path, strerror(errno)); + return VERIFY_FAILURE; + } + + // An archive with a whole-file signature will end in six bytes: + // + // (2-byte signature start) $ff $ff (2-byte comment size) + // + // (As far as the ZIP format is concerned, these are part of the + // archive comment.) We start by reading this footer, this tells + // us how far back from the end we have to start reading to find + // the whole comment. + +#define FOOTER_SIZE 6 + + if (fseek(f, -FOOTER_SIZE, SEEK_END) != 0) { + LOGE("failed to seek in %s (%s)\n", path, strerror(errno)); + fclose(f); + return VERIFY_FAILURE; + } + + unsigned char footer[FOOTER_SIZE]; + if (fread(footer, 1, FOOTER_SIZE, f) != FOOTER_SIZE) { + LOGE("failed to read footer from %s (%s)\n", path, strerror(errno)); + fclose(f); + return VERIFY_FAILURE; + } + + if (footer[2] != 0xff || footer[3] != 0xff) { + fclose(f); + return VERIFY_FAILURE; + } + + size_t comment_size = footer[4] + (footer[5] << 8); + size_t signature_start = footer[0] + (footer[1] << 8); + LOGI("comment is %d bytes; signature %d bytes from end\n", + comment_size, signature_start); + + if (signature_start - FOOTER_SIZE < RSANUMBYTES) { + // "signature" block isn't big enough to contain an RSA block. + LOGE("signature is too short\n"); + fclose(f); + return VERIFY_FAILURE; + } + +#define EOCD_HEADER_SIZE 22 + + // The end-of-central-directory record is 22 bytes plus any + // comment length. + size_t eocd_size = comment_size + EOCD_HEADER_SIZE; + + if (fseek(f, -eocd_size, SEEK_END) != 0) { + LOGE("failed to seek in %s (%s)\n", path, strerror(errno)); + fclose(f); + return VERIFY_FAILURE; + } + + // Determine how much of the file is covered by the signature. + // This is everything except the signature data and length, which + // includes all of the EOCD except for the comment length field (2 + // bytes) and the comment data. + size_t signed_len = ftell(f) + EOCD_HEADER_SIZE - 2; + + unsigned char* eocd = (unsigned char*)malloc(eocd_size); + if (eocd == NULL) { + LOGE("malloc for EOCD record failed\n"); + fclose(f); + return VERIFY_FAILURE; + } + if (fread(eocd, 1, eocd_size, f) != eocd_size) { + LOGE("failed to read eocd from %s (%s)\n", path, strerror(errno)); + fclose(f); + return VERIFY_FAILURE; + } + + // If this is really is the EOCD record, it will begin with the + // magic number $50 $4b $05 $06. + if (eocd[0] != 0x50 || eocd[1] != 0x4b || + eocd[2] != 0x05 || eocd[3] != 0x06) { + LOGE("signature length doesn't match EOCD marker\n"); + fclose(f); + return VERIFY_FAILURE; + } + + size_t i; + for (i = 4; i < eocd_size-3; ++i) { + if (eocd[i ] == 0x50 && eocd[i+1] == 0x4b && + eocd[i+2] == 0x05 && eocd[i+3] == 0x06) { + // if the sequence $50 $4b $05 $06 appears anywhere after + // the real one, minzip will find the later (wrong) one, + // which could be exploitable. Fail verification if + // this sequence occurs anywhere after the real one. + LOGE("EOCD marker occurs after start of EOCD\n"); + fclose(f); + return VERIFY_FAILURE; + } + } + +#define BUFFER_SIZE 4096 + + SHA_CTX ctx; + SHA_init(&ctx); + unsigned char* buffer = (unsigned char*)malloc(BUFFER_SIZE); + if (buffer == NULL) { + LOGE("failed to alloc memory for sha1 buffer\n"); + fclose(f); + return VERIFY_FAILURE; + } + + double frac = -1.0; + size_t so_far = 0; + fseek(f, 0, SEEK_SET); + while (so_far < signed_len) { + size_t size = BUFFER_SIZE; + if (signed_len - so_far < size) size = signed_len - so_far; + if (fread(buffer, 1, size, f) != size) { + LOGE("failed to read data from %s (%s)\n", path, strerror(errno)); + fclose(f); + return VERIFY_FAILURE; + } + SHA_update(&ctx, buffer, size); + so_far += size; + double f = so_far / (double)signed_len; + if (f > frac + 0.02 || size == so_far) { + ui->SetProgress(f); + frac = f; + } + } + fclose(f); + free(buffer); + + const uint8_t* sha1 = SHA_final(&ctx); + for (i = 0; i < numKeys; ++i) { + // The 6 bytes is the "(signature_start) $ff $ff (comment_size)" that + // the signing tool appends after the signature itself. + if (RSA_verify(pKeys+i, eocd + eocd_size - 6 - RSANUMBYTES, + RSANUMBYTES, sha1)) { + LOGI("whole-file signature verified against key %d\n", i); + free(eocd); + return VERIFY_SUCCESS; + } + } + free(eocd); + LOGE("failed to verify whole-file signature\n"); + return VERIFY_FAILURE; +} + +// If the package contains an update binary, extract it and run it. +static int +try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) { + const ZipEntry* binary_entry = + mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME); + if (binary_entry == NULL) { + mzCloseZipArchive(zip); + return INSTALL_CORRUPT; + } + + const char* binary = "/tmp/update_binary"; + unlink(binary); + int fd = creat(binary, 0755); + if (fd < 0) { + mzCloseZipArchive(zip); + LOGE("Can't make %s\n", binary); + return INSTALL_ERROR; + } + bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd); + close(fd); + mzCloseZipArchive(zip); + + if (!ok) { + LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME); + return INSTALL_ERROR; + } + + int pipefd[2]; + pipe(pipefd); + + // When executing the update binary contained in the package, the + // arguments passed are: + // + // - the version number for this interface + // + // - an fd to which the program can write in order to update the + // progress bar. The program can write single-line commands: + // + // progress + // fill up the next part of of the progress bar + // over seconds. If is zero, use + // set_progress commands to manually control the + // progress of this segment of the bar + // + // set_progress + // should be between 0.0 and 1.0; sets the + // progress bar within the segment defined by the most + // recent progress command. + // + // firmware <"hboot"|"radio"> + // arrange to install the contents of in the + // given partition on reboot. + // + // (API v2: may start with "PACKAGE:" to + // indicate taking a file from the OTA package.) + // + // (API v3: this command no longer exists.) + // + // ui_print + // display on the screen. + // + // - the name of the package zip file. + // + + const char** args = (const char**)malloc(sizeof(char*) * 5); + args[0] = binary; + args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mk + char* temp = (char*)malloc(10); + sprintf(temp, "%d", pipefd[1]); + args[2] = temp; + args[3] = (char*)path; + args[4] = NULL; + + pid_t pid = fork(); + if (pid == 0) { + close(pipefd[0]); + execv(binary, (char* const*)args); + fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno)); + _exit(-1); + } + close(pipefd[1]); + + *wipe_cache = 0; + + char buffer[1024]; + FILE* from_child = fdopen(pipefd[0], "r"); + while (fgets(buffer, sizeof(buffer), from_child) != NULL) { + char* command = strtok(buffer, " \n"); + if (command == NULL) { + continue; + } else if (strcmp(command, "progress") == 0) { + char* fraction_s = strtok(NULL, " \n"); + char* seconds_s = strtok(NULL, " \n"); + + float fraction = strtof(fraction_s, NULL); + int seconds = strtol(seconds_s, NULL, 10); + + ui->ShowProgress(fraction * (1-VERIFICATION_PROGRESS_FRACTION), seconds); + } else if (strcmp(command, "set_progress") == 0) { + char* fraction_s = strtok(NULL, " \n"); + float fraction = strtof(fraction_s, NULL); + ui->SetProgress(fraction); + } else if (strcmp(command, "ui_print") == 0) { + char* str = strtok(NULL, "\n"); + if (str) { + ui->Print("%s", str); + } else { + ui->Print("\n"); + } + } else if (strcmp(command, "wipe_cache") == 0) { + *wipe_cache = 1; + } else if (strcmp(command, "clear_display") == 0) { + //ui->SetBackground(RecoveryUI::NONE); + } else { + LOGE("unknown command [%s]\n", command); + } + } + fclose(from_child); + + int status; + waitpid(pid, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status)); + return INSTALL_ERROR; + } + + return INSTALL_SUCCESS; +} + +// Reads a file containing one or more public keys as produced by +// DumpPublicKey: this is an RSAPublicKey struct as it would appear +// as a C source literal, eg: +// +// "{64,0xc926ad21,{1795090719,...,-695002876},{-857949815,...,1175080310}}" +// +// (Note that the braces and commas in this example are actual +// characters the parser expects to find in the file; the ellipses +// indicate more numbers omitted from this example.) +// +// The file may contain multiple keys in this format, separated by +// commas. The last key must not be followed by a comma. +// +// Returns NULL if the file failed to parse, or if it contain zero keys. +static RSAPublicKey* +load_keys(const char* filename, int* numKeys) { + RSAPublicKey* out = NULL; + *numKeys = 0; + + FILE* f = fopen(filename, "r"); + if (f == NULL) { + LOGE("opening %s: %s\n", filename, strerror(errno)); + goto exit; + } + + { + int i; + bool done = false; + while (!done) { + ++*numKeys; + out = (RSAPublicKey*)realloc(out, *numKeys * sizeof(RSAPublicKey)); + RSAPublicKey* key = out + (*numKeys - 1); + if (fscanf(f, " { %i , 0x%x , { %u", + &(key->len), &(key->n0inv), &(key->n[0])) != 3) { + goto exit; + } + if (key->len != RSANUMWORDS) { + LOGE("key length (%d) does not match expected size\n", key->len); + goto exit; + } + for (i = 1; i < key->len; ++i) { + if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit; + } + if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit; + for (i = 1; i < key->len; ++i) { + if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit; + } + fscanf(f, " } } "); + + // if the line ends in a comma, this file has more keys. + switch (fgetc(f)) { + case ',': + // more keys to come. + break; + + case EOF: + done = true; + break; + + default: + LOGE("unexpected character between keys\n"); + goto exit; + } + } + } + + fclose(f); + return out; + +exit: + if (f) fclose(f); + free(out); + *numKeys = 0; + return NULL; +} + +char* get_path (char* path) { + char *s; + + /* Go to the end of the string. */ + s = path + strlen(path) - 1; + + /* Strip off trailing /s (unless it is also the leading /). */ + while (path < s && s[0] == '/') + s--; + + /* Strip the last component. */ + while (path <= s && s[0] != '/') + s--; + + while (path < s && s[0] == '/') + s--; + + if (s < path) + return (char*)("."); + + s[1] = '\0'; + return path; +} + +/* + Checks md5 for a path + Return values: + -1 : MD5 does not exist + 0 : Failed + 1 : Success +*/ +int check_md5(const char* path) { + FILE* fp; + char command[255], line[512], actual_md5[512], md5[512]; + char md5file[PATH_MAX + 40]; + char *ptr; + unsigned int line_len, index = 0; + struct stat st; + + // Check to see if the filename.zip.md5 file exists + strcpy(md5file, path); + strcat(md5file, ".md5"); + if (stat(md5file, &st) != 0) + return -1; // no MD5 file found + + // Dump the md5 of the zip to a text file for reading + sprintf(command, "md5sum '%s' > /tmp/md5output.txt", path); + __system(command); + fp = fopen("/tmp/md5output.txt", "rt"); + if (fp == NULL) { + LOGI("Unable to open /tmp/md5output.txt.\n"); + return false; + } + + while (fgets(line, sizeof(line), fp) != NULL) + { + line_len = strlen(line); + for (index = 0; index < line_len; index++) { + if (line[index] <= 32) + line[index] = '\0'; + } + strcpy(actual_md5, line); + break; + } + fclose(fp); + + // Read the filename.zip.md5 file + fp = fopen(md5file, "rt"); + if (fp == NULL) { + LOGI("Unable to open '%s'.\n", md5file); + return false; + } + + while (fgets(line, sizeof(line), fp) != NULL) + { + line_len = strlen(line); + for (index = 0; index < line_len; index++) { + if (line[index] <= 32) + line[index] = '\0'; + } + strcpy(md5, line); + break; + } + fclose(fp); + + // Comare the 2 MD5 values + if (strcmp(actual_md5, md5) == 0) + return 1; + LOGI("MD5 did not match: '%s' != '%s'\n", actual_md5, md5); + return 0; +} + +extern "C" int TWinstall_zip(const char* path, int* wipe_cache) { + int err, zip_verify, md5_return, md5_verify; + + ui_print("Installing '%s'...\n", path); + + if (!PartitionManager.Mount_By_Path(path, 0)) { + LOGE("Failed to mount '%s'\n", path); + return -1; + } + + ui_print("Checking for MD5 file...\n"); + md5_return = check_md5(path); + if (md5_return == 0) { + // MD5 did not match. + LOGE("Zip MD5 does not match.\nUnable to install zip.\n"); + return INSTALL_CORRUPT; + } else if (md5_return == -1) { + DataManager::GetValue(TW_FORCE_MD5_CHECK_VAR, md5_verify); + if (md5_verify == 1) { + // Forced MD5 checking is on and no MD5 file found. + LOGE("No MD5 file found for '%s'.\nDisable force MD5 check to avoid this error.\n", path); + return INSTALL_CORRUPT; + } else + ui_print("No MD5 file found, this is not an error.\n"); + } else if (md5_return == 1) + ui_print("Zip MD5 matched.\n"); // MD5 found and matched. + + DataManager::GetValue(TW_SIGNED_ZIP_VERIFY_VAR, zip_verify); + if (zip_verify) { + ui_print("Verifying zip signature...\n"); + int numKeys; + RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys); + if (loadedKeys == NULL) { + LOGE("Failed to load keys\n"); + return -1; + } + LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE); + + // Give verification half the progress bar... + ui->Print("Verifying update package...\n"); + ui->SetProgressType(RecoveryUI::DETERMINATE); + ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME); + + err = TWverify_file(path, loadedKeys, numKeys); + free(loadedKeys); + LOGI("verify_file returned %d\n", err); + if (err != VERIFY_SUCCESS) { + LOGE("signature verification failed\n"); + return -1; + } + } + /* Try to open the package. + */ + ZipArchive zip; + err = mzOpenZipArchive(path, &zip); + if (err != 0) { + LOGE("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad"); + return INSTALL_CORRUPT; + } + + /* Verify and install the contents of the package. + */ + return try_update_binary(path, &zip, wipe_cache); +} diff --git a/twinstall.h b/twinstall.h new file mode 100644 index 000000000..8fd940e37 --- /dev/null +++ b/twinstall.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RECOVERY_INSTALL_H_ +#define RECOVERY_INSTALL_H_ + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int TWinstall_zip(const char* path, int* wipe_cache); + +#ifdef __cplusplus +} +#endif + +#endif // RECOVERY_INSTALL_H_ diff --git a/twmincrypt/twrsa.c b/twmincrypt/twrsa.c new file mode 100644 index 000000000..52d6bfbc2 --- /dev/null +++ b/twmincrypt/twrsa.c @@ -0,0 +1,199 @@ +/* rsa.c +** +** Copyright 2008, The Android Open Source Project +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of Google Inc. nor the names of its contributors may +** be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "twrsa.h" +#include "twsha.h" +#include "common.h" + +/* a[] -= mod */ +static void subM(const RSAPublicKey *key, uint32_t *a) { + int64_t A = 0; + int i; + for (i = 0; i < key->len; ++i) { + A += (uint64_t)a[i] - key->n[i]; + a[i] = (uint32_t)A; + A >>= 32; + } +} + +/* return a[] >= mod */ +static int geM(const RSAPublicKey *key, const uint32_t *a) { + int i; + for (i = key->len; i;) { + --i; + if (a[i] < key->n[i]) return 0; + if (a[i] > key->n[i]) return 1; + } + return 1; /* equal */ +} + +/* montgomery c[] += a * b[] / R % mod */ +static void montMulAdd(const RSAPublicKey *key, + uint32_t* c, + const uint32_t a, + const uint32_t* b) { + uint64_t A = (uint64_t)a * b[0] + c[0]; + uint32_t d0 = (uint32_t)A * key->n0inv; + uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A; + int i; + + for (i = 1; i < key->len; ++i) { + A = (A >> 32) + (uint64_t)a * b[i] + c[i]; + B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A; + c[i - 1] = (uint32_t)B; + } + + A = (A >> 32) + (B >> 32); + + c[i - 1] = (uint32_t)A; + + if (A >> 32) { + subM(key, c); + } +} + +/* montgomery c[] = a[] * b[] / R % mod */ +static void montMul(const RSAPublicKey *key, + uint32_t* c, + const uint32_t* a, + const uint32_t* b) { + int i; + for (i = 0; i < key->len; ++i) { + c[i] = 0; + } + for (i = 0; i < key->len; ++i) { + montMulAdd(key, c, a[i], b); + } +} + +/* In-place public exponentiation. +** Input and output big-endian byte array in inout. +*/ +static void modpow3(const RSAPublicKey *key, + uint8_t* inout) { + uint32_t a[RSANUMWORDS]; + uint32_t aR[RSANUMWORDS]; + uint32_t aaR[RSANUMWORDS]; + uint32_t *aaa = aR; /* Re-use location. */ + int i; + + /* Convert from big endian byte array to little endian word array. */ + for (i = 0; i < key->len; ++i) { + uint32_t tmp = + (inout[((key->len - 1 - i) * 4) + 0] << 24) | + (inout[((key->len - 1 - i) * 4) + 1] << 16) | + (inout[((key->len - 1 - i) * 4) + 2] << 8) | + (inout[((key->len - 1 - i) * 4) + 3] << 0); + a[i] = tmp; + } + + montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */ + montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */ + montMul(key, aaa, aaR, a); /* aaa = aaR * a / R mod M */ + + /* Make sure aaa < mod; aaa is at most 1x mod too large. */ + if (geM(key, aaa)) { + subM(key, aaa); + } + + /* Convert to bigendian byte array */ + for (i = key->len - 1; i >= 0; --i) { + uint32_t tmp = aaa[i]; + *inout++ = tmp >> 24; + *inout++ = tmp >> 16; + *inout++ = tmp >> 8; + *inout++ = tmp >> 0; + } +} + +/* Expected PKCS1.5 signature padding bytes, for a keytool RSA signature. +** Has the 0-length optional parameter encoded in the ASN1 (as opposed to the +** other flavor which omits the optional parameter entirely). This code does not +** accept signatures without the optional parameter. +*/ +static const uint8_t padding[RSANUMBYTES - SHA_DIGEST_SIZE] = { + 0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, + 0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00, + 0x04,0x14 +}; + +/* Verify a 2048 bit RSA PKCS1.5 signature against an expected SHA-1 hash. +** Returns 0 on failure, 1 on success. +*/ +int RSA_verify(const RSAPublicKey *key, + const uint8_t *signature, + const int len, + const uint8_t *sha) { + uint8_t buf[RSANUMBYTES]; + int i; + + if (key->len != RSANUMWORDS) { + return 0; /* Wrong key passed in. */ + } + + if (len != sizeof(buf)) { + return 0; /* Wrong input length. */ + } + + for (i = 0; i < len; ++i) { + buf[i] = signature[i]; + } + + modpow3(key, buf); + + /* Check pkcs1.5 padding bytes. */ + for (i = 0; i < (int) sizeof(padding); ++i) { + if (buf[i] != padding[i]) { + return 0; + } + } + + /* Check sha digest matches. */ + for (; i < len; ++i) { + if (buf[i] != *sha++) { + return 0; + } + } + + return 1; +} diff --git a/twmincrypt/twrsa.h b/twmincrypt/twrsa.h new file mode 100644 index 000000000..7d7d57158 --- /dev/null +++ b/twmincrypt/twrsa.h @@ -0,0 +1,56 @@ +/* rsa.h +** +** Copyright 2008, The Android Open Source Project +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of Google Inc. nor the names of its contributors may +** be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _EMBEDDED_RSA_H_ +#define _EMBEDDED_RSA_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define RSANUMBYTES 256 /* 2048 bit key length */ +#define RSANUMWORDS (RSANUMBYTES / sizeof(uint32_t)) + +typedef struct RSAPublicKey { + int len; /* Length of n[] in number of uint32_t */ + uint32_t n0inv; /* -1 / n[0] mod 2^32 */ + uint32_t n[RSANUMWORDS]; /* modulus as little endian array */ + uint32_t rr[RSANUMWORDS]; /* R^2 as little endian array */ +} RSAPublicKey; + +int RSA_verify(const RSAPublicKey *key, + const uint8_t* signature, + const int len, + const uint8_t* sha); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/twmincrypt/twsha.c b/twmincrypt/twsha.c new file mode 100644 index 000000000..ac712c245 --- /dev/null +++ b/twmincrypt/twsha.c @@ -0,0 +1,307 @@ +/* sha.c +** +** Copyright 2008, The Android Open Source Project +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of Google Inc. nor the names of its contributors may +** be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "twsha.h" + +// Some machines lack byteswap.h and endian.h. These have to use the +// slower code, even if they're little-endian. + +#if defined(HAVE_ENDIAN_H) && defined(HAVE_LITTLE_ENDIAN) + +#include +#include + +// This version is about 28% faster than the generic version below, +// but assumes little-endianness. + +static inline uint32_t ror27(uint32_t val) { + return (val >> 27) | (val << 5); +} +static inline uint32_t ror2(uint32_t val) { + return (val >> 2) | (val << 30); +} +static inline uint32_t ror31(uint32_t val) { + return (val >> 31) | (val << 1); +} + +static void SHA1_Transform(SHA_CTX* ctx) { + uint32_t W[80]; + register uint32_t A, B, C, D, E; + int t; + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + +#define SHA_F1(A,B,C,D,E,t) \ + E += ror27(A) + \ + (W[t] = bswap_32(ctx->buf.w[t])) + \ + (D^(B&(C^D))) + 0x5A827999; \ + B = ror2(B); + + for (t = 0; t < 15; t += 5) { + SHA_F1(A,B,C,D,E,t + 0); + SHA_F1(E,A,B,C,D,t + 1); + SHA_F1(D,E,A,B,C,t + 2); + SHA_F1(C,D,E,A,B,t + 3); + SHA_F1(B,C,D,E,A,t + 4); + } + SHA_F1(A,B,C,D,E,t + 0); // 16th one, t == 15 + +#undef SHA_F1 + +#define SHA_F1(A,B,C,D,E,t) \ + E += ror27(A) + \ + (W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \ + (D^(B&(C^D))) + 0x5A827999; \ + B = ror2(B); + + SHA_F1(E,A,B,C,D,t + 1); + SHA_F1(D,E,A,B,C,t + 2); + SHA_F1(C,D,E,A,B,t + 3); + SHA_F1(B,C,D,E,A,t + 4); + +#undef SHA_F1 + +#define SHA_F2(A,B,C,D,E,t) \ + E += ror27(A) + \ + (W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \ + (B^C^D) + 0x6ED9EBA1; \ + B = ror2(B); + + for (t = 20; t < 40; t += 5) { + SHA_F2(A,B,C,D,E,t + 0); + SHA_F2(E,A,B,C,D,t + 1); + SHA_F2(D,E,A,B,C,t + 2); + SHA_F2(C,D,E,A,B,t + 3); + SHA_F2(B,C,D,E,A,t + 4); + } + +#undef SHA_F2 + +#define SHA_F3(A,B,C,D,E,t) \ + E += ror27(A) + \ + (W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \ + ((B&C)|(D&(B|C))) + 0x8F1BBCDC; \ + B = ror2(B); + + for (; t < 60; t += 5) { + SHA_F3(A,B,C,D,E,t + 0); + SHA_F3(E,A,B,C,D,t + 1); + SHA_F3(D,E,A,B,C,t + 2); + SHA_F3(C,D,E,A,B,t + 3); + SHA_F3(B,C,D,E,A,t + 4); + } + +#undef SHA_F3 + +#define SHA_F4(A,B,C,D,E,t) \ + E += ror27(A) + \ + (W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \ + (B^C^D) + 0xCA62C1D6; \ + B = ror2(B); + + for (; t < 80; t += 5) { + SHA_F4(A,B,C,D,E,t + 0); + SHA_F4(E,A,B,C,D,t + 1); + SHA_F4(D,E,A,B,C,t + 2); + SHA_F4(C,D,E,A,B,t + 3); + SHA_F4(B,C,D,E,A,t + 4); + } + +#undef SHA_F4 + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; +} + +void SHA_update(SHA_CTX* ctx, const void* data, int len) { + int i = ctx->count % sizeof(ctx->buf); + const uint8_t* p = (const uint8_t*)data; + + ctx->count += len; + + while (len > sizeof(ctx->buf) - i) { + memcpy(&ctx->buf.b[i], p, sizeof(ctx->buf) - i); + len -= sizeof(ctx->buf) - i; + p += sizeof(ctx->buf) - i; + SHA1_Transform(ctx); + i = 0; + } + + while (len--) { + ctx->buf.b[i++] = *p++; + if (i == sizeof(ctx->buf)) { + SHA1_Transform(ctx); + i = 0; + } + } +} + + +const uint8_t* SHA_final(SHA_CTX* ctx) { + uint64_t cnt = ctx->count * 8; + int i; + + SHA_update(ctx, (uint8_t*)"\x80", 1); + while ((ctx->count % sizeof(ctx->buf)) != (sizeof(ctx->buf) - 8)) { + SHA_update(ctx, (uint8_t*)"\0", 1); + } + for (i = 0; i < 8; ++i) { + uint8_t tmp = cnt >> ((7 - i) * 8); + SHA_update(ctx, &tmp, 1); + } + + for (i = 0; i < 5; i++) { + ctx->buf.w[i] = bswap_32(ctx->state[i]); + } + + return ctx->buf.b; +} + +#else // #if defined(HAVE_ENDIAN_H) && defined(HAVE_LITTLE_ENDIAN) + +#define rol(bits, value) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +static void SHA1_transform(SHA_CTX *ctx) { + uint32_t W[80]; + uint32_t A, B, C, D, E; + uint8_t *p = ctx->buf; + int t; + + for(t = 0; t < 16; ++t) { + uint32_t tmp = *p++ << 24; + tmp |= *p++ << 16; + tmp |= *p++ << 8; + tmp |= *p++; + W[t] = tmp; + } + + for(; t < 80; t++) { + W[t] = rol(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + + for(t = 0; t < 80; t++) { + uint32_t tmp = rol(5,A) + E + W[t]; + + if (t < 20) + tmp += (D^(B&(C^D))) + 0x5A827999; + else if ( t < 40) + tmp += (B^C^D) + 0x6ED9EBA1; + else if ( t < 60) + tmp += ((B&C)|(D&(B|C))) + 0x8F1BBCDC; + else + tmp += (B^C^D) + 0xCA62C1D6; + + E = D; + D = C; + C = rol(30,B); + B = A; + A = tmp; + } + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; +} + +void SHA_update(SHA_CTX *ctx, const void *data, int len) { + int i = ctx->count % sizeof(ctx->buf); + const uint8_t* p = (const uint8_t*)data; + + ctx->count += len; + + while (len--) { + ctx->buf[i++] = *p++; + if (i == sizeof(ctx->buf)) { + SHA1_transform(ctx); + i = 0; + } + } +} +const uint8_t *SHA_final(SHA_CTX *ctx) { + uint8_t *p = ctx->buf; + uint64_t cnt = ctx->count * 8; + int i; + + SHA_update(ctx, (uint8_t*)"\x80", 1); + while ((ctx->count % sizeof(ctx->buf)) != (sizeof(ctx->buf) - 8)) { + SHA_update(ctx, (uint8_t*)"\0", 1); + } + for (i = 0; i < 8; ++i) { + uint8_t tmp = cnt >> ((7 - i) * 8); + SHA_update(ctx, &tmp, 1); + } + + for (i = 0; i < 5; i++) { + uint32_t tmp = ctx->state[i]; + *p++ = tmp >> 24; + *p++ = tmp >> 16; + *p++ = tmp >> 8; + *p++ = tmp >> 0; + } + + return ctx->buf; +} + +#endif // endianness + +void SHA_init(SHA_CTX* ctx) { + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; + ctx->count = 0; +} + +/* Convenience function */ +const uint8_t* SHA(const void *data, int len, uint8_t *digest) { + const uint8_t *p; + int i; + SHA_CTX ctx; + SHA_init(&ctx); + SHA_update(&ctx, data, len); + p = SHA_final(&ctx); + for (i = 0; i < SHA_DIGEST_SIZE; ++i) { + digest[i] = *p++; + } + return digest; +} diff --git a/twmincrypt/twsha.h b/twmincrypt/twsha.h new file mode 100644 index 000000000..af63e8775 --- /dev/null +++ b/twmincrypt/twsha.h @@ -0,0 +1,63 @@ +/* sha.h +** +** Copyright 2008, The Android Open Source Project +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of Google Inc. nor the names of its contributors may +** be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _EMBEDDED_SHA_H_ +#define _EMBEDDED_SHA_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SHA_CTX { + uint64_t count; + uint32_t state[5]; +#if defined(HAVE_ENDIAN_H) && defined(HAVE_LITTLE_ENDIAN) + union { + uint8_t b[64]; + uint32_t w[16]; + } buf; +#else + uint8_t buf[64]; +#endif +} SHA_CTX; + +void SHA_init(SHA_CTX* ctx); +void SHA_update(SHA_CTX* ctx, const void* data, int len); +const uint8_t* SHA_final(SHA_CTX* ctx); + +/* Convenience method. Returns digest parameter value. */ +const uint8_t* SHA(const void* data, int len, uint8_t* digest); + +#define SHA_DIGEST_SIZE 20 + +#ifdef __cplusplus +} +#endif + +#endif -- cgit v1.2.3