| #include <iostream> |
| #include <memory> |
| #include <iostream> |
| #include <fstream> |
| #include <algorithm> |
| #include <random> |
| #include <cinttypes> |
| #include <tuple> |
| |
| #include "boot/picobin.h" |
| #include <map> |
| |
| #include "elf_file.h" |
| |
| #if HAS_MBEDTLS |
| #include "mbedtls_wrapper.h" |
| #include <mbedtls/pk.h> |
| #include <mbedtls/ecp.h> |
| #include <mbedtls/error.h> |
| #endif |
| |
| #include "bintool.h" |
| #include "metadata.h" |
| #include "errors.h" |
| |
| // todo test with a holey binary |
| |
| template<typename T> void dumper(const char *msg, const T& foop) { |
| DEBUG_LOG("%s ", msg); |
| for(uint8_t i : foop.bytes) { |
| DEBUG_LOG("%02x", i); |
| } |
| DEBUG_LOG("\n"); |
| } |
| |
| std::vector<const Segment *> sorted_segs(elf_file *elf) { |
| std::vector<const Segment *> phys_sorted_segs; |
| std::transform(elf->segments().begin(), elf->segments().end(), std::back_inserter(phys_sorted_segs), [](const Segment &seg) { |
| return &seg; |
| }); |
| std::sort(phys_sorted_segs.begin(), phys_sorted_segs.end(), [](const Segment *first, const Segment *second) { |
| return first->physical_address() < second->physical_address(); |
| }); |
| return phys_sorted_segs; |
| } |
| |
| #if HAS_MBEDTLS |
| int read_keys(const std::string &filename, public_t *public_key, private_t *private_key) { |
| mbedtls_pk_context pk_ctx; |
| int rc; |
| |
| mbedtls_pk_init(&pk_ctx); |
| #if MBEDTLS_VERSION_MAJOR >= 3 |
| // This rng is only used for blinding when reading the key file |
| // As this should only be done on a secure computer, blinding is not required, so it's fine to not actually seed it with any entropy |
| mbedtls_ctr_drbg_context ctr_drbg; |
| mbedtls_ctr_drbg_init(&ctr_drbg); |
| rc = mbedtls_pk_parse_keyfile(&pk_ctx, filename.c_str(), NULL, mbedtls_ctr_drbg_random, &ctr_drbg); |
| #else |
| rc = mbedtls_pk_parse_keyfile(&pk_ctx, filename.c_str(), NULL); |
| #endif |
| if (rc != 0) { |
| char error_string[128]; |
| mbedtls_strerror(rc, error_string, sizeof(error_string)); |
| fail(ERROR_FORMAT, "Failed to read key file %s, error %s", filename.c_str(), error_string); |
| return -1; |
| } |
| |
| const mbedtls_ecp_keypair *keypair = mbedtls_pk_ec(pk_ctx); |
| if (!keypair) { |
| fail(ERROR_FORMAT, "Failed to parse key file %s", filename.c_str()); |
| } |
| mbedtls_mpi_write_binary(&keypair->d, reinterpret_cast<unsigned char *>(private_key), 32); |
| mbedtls_mpi_write_binary(&keypair->Q.X, reinterpret_cast<unsigned char *>(public_key), 32); |
| mbedtls_mpi_write_binary(&keypair->Q.Y, reinterpret_cast<unsigned char *>(public_key) + 32, 32); |
| return 0; |
| } |
| #endif |
| |
| #define OTP_KEY_YAML_HEADER \ |
| "include:\n" \ |
| " - otp/tc_images/base_chipinfo.yml\n" \ |
| "data:\n" \ |
| " - crit1_secure_boot_enable: [crit, 1]\n" \ |
| " - crit0_riscv_disable: [crit, 1]\n" \ |
| " - crit0_arm_disable: [crit, 0]\n" \ |
| " - BOOT_FLAGS0_SECURE_PARTITION_TABLE: [rbit3, 0]\n" \ |
| " - BOOT_FLAGS0_DISABLE_AUTO_SWITCH_ARCH: [rbit3, 1]\n" \ |
| " # - boot_temp_chicken_bit_opt_in_faster_sigcheck_rosc_div: [rbit3, 1]\n" \ |
| " - boot_flags1_key_valid: [rbit3, 0b0001]\n" \ |
| |
| |
| #if HAS_MBEDTLS |
| void write_otp_key_yaml(const std::string &filename, message_digest_t pub_sha256) { |
| std::ofstream out(filename, std::ios::out | std::ios::trunc); |
| out.exceptions(std::fstream::failbit | std::fstream::badbit); |
| out << std::string(OTP_KEY_YAML_HEADER); |
| |
| // Print public key hash again in the format it is expected to appear in OTP |
| for (int i = 0; i < 16; ++i) { |
| char row[128]; |
| snprintf(row, sizeof(row), " - bootkey0_%-2d: [ecc, 0x%02x%02x]\n", i, pub_sha256.bytes[2 * i + 1], pub_sha256.bytes[2 * i]); |
| out << std::string(row); |
| } |
| } |
| #endif |
| |
| |
| std::unique_ptr<block> find_first_block(elf_file *elf) { |
| std::unique_ptr<block> first_block; |
| for(auto x : sorted_segs(elf)) { |
| if (!x->is_load()) continue; |
| auto data = elf->content(*x); // x->content(*elf); |
| // todo handle alignment (not sure if necessary) |
| if ((x->physical_address() & 3) || (x->physical_size() & 3)) { |
| fail(ERROR_INCOMPATIBLE, "ELF segments must be word aligned"); |
| } |
| std::vector<uint32_t> words = lsb_bytes_to_words(data.begin(), data.end()); |
| auto block_begin = std::find(words.begin(), words.end(), PICOBIN_BLOCK_MARKER_START); |
| while (block_begin != words.end() && !first_block) { |
| DEBUG_LOG("Found possible block at %08x + %08x...", (unsigned int)x->physical_address(), (int)(block_begin - words.begin())*4); |
| block_begin++; |
| for(auto next_item = block_begin; next_item < words.end(); ) { |
| unsigned int size = item::decode_size(*next_item); |
| if ((uint8_t)*next_item == PICOBIN_BLOCK_ITEM_2BS_LAST) { |
| if (size == next_item - block_begin) { |
| if (next_item < words.end() && next_item[2] == PICOBIN_BLOCK_MARKER_END) { |
| DEBUG_LOG(" verified block"); |
| first_block = block::parse(x->physical_address() + 4*(block_begin - words.begin() - 1), |
| next_item + 1, block_begin, block_begin + size); |
| break; |
| } |
| } |
| } else { |
| next_item += size; |
| } |
| } |
| DEBUG_LOG("\n"); |
| block_begin = std::find(block_begin, words.end(), PICOBIN_BLOCK_MARKER_START); |
| } |
| if (first_block) break; |
| } |
| if (!first_block) { |
| DEBUG_LOG("No block found\n"); |
| return NULL; |
| } |
| return first_block; |
| } |
| |
| |
| std::unique_ptr<block> find_first_block(std::vector<uint8_t> bin, uint32_t storage_addr) { |
| std::unique_ptr<block> first_block; |
| |
| std::vector<uint32_t> words = lsb_bytes_to_words(bin.begin(), bin.end()); |
| auto block_begin = std::find(words.begin(), words.end(), PICOBIN_BLOCK_MARKER_START); |
| |
| while (block_begin != words.end() && !first_block) { |
| DEBUG_LOG("Possible block at %08x + %08x\n", storage_addr, (int)(block_begin - words.begin())*4); |
| block_begin++; |
| for(auto next_item = block_begin; next_item < words.end(); ) { |
| unsigned int size = item::decode_size(*next_item); |
| if ((uint8_t)*next_item == PICOBIN_BLOCK_ITEM_2BS_LAST) { |
| if (size == next_item - block_begin) { |
| if (next_item < words.end() && next_item[2] == PICOBIN_BLOCK_MARKER_END) { |
| DEBUG_LOG("is a valid block\n"); |
| first_block = block::parse(storage_addr + 4*(block_begin - words.begin() - 1), |
| next_item + 1, block_begin, block_begin + size); |
| break; |
| } else { |
| printf("WARNING: Invalid block found at 0x%x - no block end marker\n", |
| (int)(block_begin - words.begin())*4 |
| ); |
| } |
| } else { |
| printf("WARNING: Invalid block found at 0x%x - incorrect last item size of %d, expected %d\n", |
| (int)(block_begin - words.begin())*4, size, (int)(next_item - block_begin) |
| ); |
| } |
| // Invalid block, so find the next one |
| next_item = words.end(); |
| } else { |
| next_item += size; |
| } |
| } |
| block_begin = std::find(block_begin, words.end(), PICOBIN_BLOCK_MARKER_START); |
| } |
| if (!first_block) { |
| DEBUG_LOG("NO BLOCK FOUND\n"); |
| return nullptr; |
| } |
| return first_block; |
| } |
| |
| |
| void set_block_ignored(elf_file *elf, uint32_t block_addr) { |
| auto seg = elf->segment_from_physical_address(block_addr); |
| if (seg == nullptr) { |
| fail(ERROR_NOT_POSSIBLE, "The ELF file does not contain the block address %x", block_addr); |
| } |
| std::vector<uint8_t> content = elf->content(*seg); |
| uint32_t offset = block_addr + 4 - seg->physical_address(); |
| if ((content[offset] & 0x7f) != PICOBIN_BLOCK_ITEM_PARTITION_TABLE) { |
| DEBUG_LOG("setting block at %08x to ignored\n", block_addr); |
| content[offset] = 0x7e; |
| } |
| elf->content(*seg, content); |
| } |
| |
| |
| void set_next_block(elf_file *elf, std::unique_ptr<block> &first_block, uint32_t highest_address) { |
| // todo this isn't right, but virtual should be physical for now |
| auto seg = elf->segment_from_physical_address(first_block->physical_addr); |
| if (seg == nullptr) { |
| fail(ERROR_NOT_POSSIBLE, "The ELF file does not contain the next block address %x", first_block->physical_addr); |
| } |
| std::vector<uint8_t> content = elf->content(*seg); |
| uint32_t offset = first_block->physical_addr + first_block->next_block_rel_index * 4 - seg->physical_address(); |
| uint32_t delta = highest_address - first_block->physical_addr; |
| // todo this assumes a 2 word NEXT_BLOCK_ADDR type for now |
| content[offset] = delta & 0xff; |
| content[offset+1] = (delta >> 8) & 0xff; |
| content[offset+2] = (delta >> 16) & 0xff; |
| content[offset+3] = (delta >> 24) & 0xff; |
| DEBUG_LOG("defaulting next_block_addr at %08x to %08x\n", |
| (int)first_block->physical_addr + first_block->next_block_rel_index * 4, |
| (int)(highest_address)); |
| first_block->next_block_rel = delta; |
| elf->content(*seg, content); |
| } |
| |
| |
| void set_block_ignored(std::vector<uint8_t> &bin, uint32_t storage_addr, uint32_t block_addr) { |
| uint32_t offset = block_addr + 4 - storage_addr; |
| if ((bin[offset] & 0x7f) != PICOBIN_BLOCK_ITEM_PARTITION_TABLE) { |
| DEBUG_LOG("setting block at %08x to ignored\n", block_addr); |
| bin[offset] = 0x7e; |
| } |
| } |
| |
| |
| void set_next_block(std::vector<uint8_t> &bin, uint32_t storage_addr, std::unique_ptr<block> &first_block, uint32_t highest_address) { |
| // todo this isn't right, but virtual should be physical for now |
| uint32_t offset = first_block->physical_addr + first_block->next_block_rel_index * 4 - storage_addr; |
| uint32_t delta = highest_address - first_block->physical_addr; |
| // todo this assumes a 2 word NEXT_BLOCK_ADDR type for now |
| bin[offset] = delta & 0xff; |
| bin[offset+1] = (delta >> 8) & 0xff; |
| bin[offset+2] = (delta >> 16) & 0xff; |
| bin[offset+3] = (delta >> 24) & 0xff; |
| DEBUG_LOG("defaulting next_block_addr at %08x to %08x\n", |
| (int)first_block->physical_addr + first_block->next_block_rel_index * 4, |
| (int)(highest_address)); |
| first_block->next_block_rel = delta; |
| } |
| |
| |
| block place_new_block(elf_file *elf, std::unique_ptr<block> &first_block, bool set_others_ignored) { |
| uint32_t highest_ram_address = 0; |
| uint32_t highest_flash_address = 0; |
| bool no_flash = false; |
| |
| for(const auto &seg : elf->segments()) { |
| // std::cout << &seg << " " << to_string(seg) << std::endl; |
| const uint32_t paddr = seg.physical_address(); |
| const uint32_t psize = seg.physical_size(); |
| if (psize == 0) continue; |
| if (paddr >= 0x20000000 && paddr < 0x20080000) { |
| highest_ram_address = std::max(paddr + psize, highest_ram_address); |
| } else if (paddr >= 0x10000000 && paddr < 0x20000000) { |
| highest_flash_address = std::max(paddr + psize, highest_flash_address); |
| } |
| } |
| |
| if (highest_flash_address == 0) { |
| no_flash = true; |
| } |
| uint32_t highest_address = no_flash ? highest_ram_address : highest_flash_address; |
| |
| DEBUG_LOG("RAM %08x ", highest_ram_address); |
| if (no_flash) { |
| DEBUG_LOG("NO FLASH\n"); |
| } else { |
| DEBUG_LOG("FLASH %08x\n", highest_flash_address); |
| } |
| |
| int32_t loop_start_rel = 0; |
| uint32_t new_block_addr = 0; |
| std::unique_ptr<block> new_first_block; |
| if (!first_block->next_block_rel) { |
| set_next_block(elf, first_block, highest_address); |
| loop_start_rel = -first_block->next_block_rel; |
| new_block_addr = first_block->physical_addr + first_block->next_block_rel; |
| if (set_others_ignored) set_block_ignored(elf, first_block->physical_addr); |
| } else { |
| DEBUG_LOG("There is already a block loop\n"); |
| if (set_others_ignored) set_block_ignored(elf, first_block->physical_addr); |
| uint32_t next_block_addr = first_block->physical_addr + first_block->next_block_rel; |
| while (true) { |
| auto segment = elf->segment_from_physical_address(next_block_addr); |
| if (segment == nullptr) { |
| fail(ERROR_NOT_POSSIBLE, "The ELF file does not contain the next block address %x", next_block_addr); |
| } |
| auto data = elf->content(*segment); |
| auto offset = next_block_addr - segment->physical_address(); |
| std::vector<uint32_t> words = lsb_bytes_to_words(data.begin() + offset, data.end()); |
| if (words.front() != PICOBIN_BLOCK_MARKER_START) { |
| fail(ERROR_UNKNOWN, "Block loop is not valid - no block found at %08x\n", (int)(next_block_addr)); |
| } |
| words.erase(words.begin()); |
| DEBUG_LOG("Checking block at %x\n", next_block_addr); |
| for(auto next_item = words.begin(); next_item < words.end(); ) { |
| unsigned int size = item::decode_size(*next_item); |
| if ((uint8_t)*next_item == PICOBIN_BLOCK_ITEM_2BS_LAST) { |
| if (size == next_item - words.begin()) { |
| if (next_item < words.end() && next_item[2] == PICOBIN_BLOCK_MARKER_END) { |
| DEBUG_LOG("is a valid block\n"); |
| new_first_block = block::parse(next_block_addr, next_item + 1, words.begin(), words.begin() + size); |
| break; |
| } |
| } |
| } else { |
| next_item += size; |
| } |
| } |
| if (new_first_block->physical_addr + new_first_block->next_block_rel == first_block->physical_addr) { |
| DEBUG_LOG("Found last block in block loop\n"); |
| break; |
| } else { |
| DEBUG_LOG("Continue looping\n"); |
| if (set_others_ignored) set_block_ignored(elf, new_first_block->physical_addr); |
| next_block_addr = new_first_block->physical_addr + new_first_block->next_block_rel; |
| new_first_block.reset(); |
| } |
| } |
| set_next_block(elf, new_first_block, highest_address); |
| new_block_addr = new_first_block->physical_addr + new_first_block->next_block_rel; |
| loop_start_rel = first_block->physical_addr - new_block_addr; |
| } |
| if (highest_address != new_block_addr) { |
| fail(ERROR_UNKNOWN, "Next block not at highest address %08x %08x\n", (int)highest_address, (int)(new_block_addr)); |
| } |
| |
| // loop back to first block |
| block new_block(new_block_addr, loop_start_rel); |
| // check if last block has an image_def |
| if (new_first_block != nullptr && new_first_block->get_item<image_type_item>() != nullptr) { |
| // copy the last block items |
| std::copy(new_first_block->items.begin(), |
| new_first_block->items.end(), |
| std::back_inserter(new_block.items)); |
| } else { |
| // copy the first block items |
| std::copy(first_block->items.begin(), |
| first_block->items.end(), |
| std::back_inserter(new_block.items)); |
| } |
| |
| // Delete existing signature and hash as these will be replaced with new ones |
| std::shared_ptr<signature_item> signature = new_block.get_item<signature_item>(); |
| if (signature != nullptr) { |
| new_block.items.erase(std::remove(new_block.items.begin(), new_block.items.end(), signature), new_block.items.end()); |
| } |
| std::shared_ptr<hash_value_item> hash_value = new_block.get_item<hash_value_item>(); |
| if (hash_value != nullptr) { |
| new_block.items.erase(std::remove(new_block.items.begin(), new_block.items.end(), hash_value), new_block.items.end()); |
| } |
| std::shared_ptr<hash_def_item> hash_def = new_block.get_item<hash_def_item>(); |
| if (hash_def != nullptr) { |
| new_block.items.erase(std::remove(new_block.items.begin(), new_block.items.end(), hash_def), new_block.items.end()); |
| } |
| |
| return new_block; |
| } |
| |
| |
| std::vector<std::unique_ptr<block>> get_all_blocks(std::vector<uint8_t> &bin, uint32_t storage_addr, std::unique_ptr<block> &first_block, get_more_bin_cb more_cb) { |
| uint32_t next_block_addr = first_block->physical_addr + first_block->next_block_rel; |
| std::vector<std::unique_ptr<block>> all_blocks; |
| uint32_t read_size = PICOBIN_MAX_BLOCK_SIZE; |
| uint32_t current_bin_start = storage_addr; |
| while (true) { |
| std::unique_ptr<block> new_first_block; |
| if (next_block_addr + read_size > current_bin_start + bin.size() && more_cb != nullptr) { |
| DEBUG_LOG("Reading into bin %08x+%x\n", next_block_addr, read_size); |
| more_cb(bin, next_block_addr, read_size); |
| current_bin_start = next_block_addr; |
| } |
| auto offset = next_block_addr - current_bin_start; |
| std::vector<uint32_t> words = lsb_bytes_to_words(bin.begin() + offset, bin.end()); |
| if (words.front() != PICOBIN_BLOCK_MARKER_START) { |
| fail(ERROR_UNKNOWN, "Block loop is not valid - no block found at %08x\n", (int)(next_block_addr)); |
| } |
| words.erase(words.begin()); |
| DEBUG_LOG("Checking block at %x\n", next_block_addr); |
| DEBUG_LOG("Starts with %x %x %x %x\n", words[0], words[1], words[2], words[3]); |
| for(auto next_item = words.begin(); next_item < words.end(); ) { |
| unsigned int size = item::decode_size(*next_item); |
| if ((uint8_t)*next_item == PICOBIN_BLOCK_ITEM_2BS_LAST) { |
| if (size == next_item - words.begin()) { |
| if (next_item < words.end() && next_item[2] == PICOBIN_BLOCK_MARKER_END) { |
| DEBUG_LOG("is a valid block\n"); |
| new_first_block = block::parse(next_block_addr, next_item + 1, words.begin(), words.begin() + size); |
| break; |
| } |
| } |
| } else { |
| next_item += size; |
| } |
| } |
| if (new_first_block == nullptr) { |
| fail(ERROR_UNKNOWN, "Block loop is not valid - incomplete block found at %08x\n", (int)(next_block_addr)); |
| } |
| if (new_first_block->physical_addr + new_first_block->next_block_rel == first_block->physical_addr) { |
| DEBUG_LOG("Found last block in block loop\n"); |
| all_blocks.push_back(std::move(new_first_block)); |
| break; |
| } else { |
| DEBUG_LOG("Continue looping\n"); |
| next_block_addr = new_first_block->physical_addr + new_first_block->next_block_rel; |
| all_blocks.push_back(std::move(new_first_block)); |
| } |
| } |
| return all_blocks; |
| } |
| |
| |
| std::unique_ptr<block> get_last_block(std::vector<uint8_t> &bin, uint32_t storage_addr, std::unique_ptr<block> &first_block, get_more_bin_cb more_cb) { |
| auto all_blocks = get_all_blocks(bin, storage_addr, first_block, more_cb); |
| return std::move(all_blocks.back()); |
| } |
| |
| |
| block place_new_block(std::vector<uint8_t> &bin, uint32_t storage_addr, std::unique_ptr<block> &first_block, bool set_others_ignored) { |
| uint32_t highest_ram_address = 0; |
| uint32_t highest_flash_address = 0; |
| bool no_flash = false; |
| |
| const uint32_t paddr = storage_addr; |
| const uint32_t psize = bin.size(); |
| if (paddr >= 0x20000000 && paddr < 0x20080000) { |
| highest_ram_address = std::max(paddr + psize, highest_ram_address); |
| } else if (paddr >= 0x10000000 && paddr < 0x20000000) { |
| highest_flash_address = std::max(paddr + psize, highest_flash_address); |
| } |
| |
| if (highest_flash_address == 0) { |
| no_flash = true; |
| } |
| uint32_t highest_address = no_flash ? highest_ram_address : highest_flash_address; |
| |
| if (no_flash) { |
| DEBUG_LOG("RAM %08x ", highest_ram_address); |
| DEBUG_LOG("NO FLASH\n"); |
| } else { |
| DEBUG_LOG("FLASH %08x\n", highest_flash_address); |
| } |
| |
| int32_t loop_start_rel = 0; |
| uint32_t new_block_addr = 0; |
| std::unique_ptr<block> new_first_block; |
| if (!first_block->next_block_rel) { |
| set_next_block(bin, storage_addr, first_block, highest_address); |
| loop_start_rel = -first_block->next_block_rel; |
| new_block_addr = first_block->physical_addr + first_block->next_block_rel; |
| if (set_others_ignored) set_block_ignored(bin, storage_addr, first_block->physical_addr); |
| } else { |
| DEBUG_LOG("Ooh, there is already a block loop - lets find it's end\n"); |
| auto all_blocks = get_all_blocks(bin, storage_addr, first_block); |
| for (auto &block : all_blocks) { |
| if (set_others_ignored) set_block_ignored(bin, storage_addr, block->physical_addr); |
| } |
| new_first_block = std::move(all_blocks.back()); |
| set_next_block(bin, storage_addr, new_first_block, highest_address); |
| new_block_addr = new_first_block->physical_addr + new_first_block->next_block_rel; |
| loop_start_rel = first_block->physical_addr - new_block_addr; |
| } |
| if (highest_address != new_block_addr) { |
| fail(ERROR_UNKNOWN, "Next block not at highest address %08x %08x\n", (int)highest_address, (int)(new_block_addr)); |
| } |
| |
| // loop back to first block |
| block new_block(new_block_addr, loop_start_rel); |
| // check if last block has an image_def |
| if (new_first_block != nullptr && new_first_block->get_item<image_type_item>() != nullptr) { |
| // copy the last block items |
| std::copy(new_first_block->items.begin(), |
| new_first_block->items.end(), |
| std::back_inserter(new_block.items)); |
| } else { |
| // copy the first block items |
| std::copy(first_block->items.begin(), |
| first_block->items.end(), |
| std::back_inserter(new_block.items)); |
| } |
| |
| // Delete existing signature and hash as these will be replaced with new ones |
| std::shared_ptr<signature_item> signature = new_block.get_item<signature_item>(); |
| if (signature != nullptr) { |
| new_block.items.erase(std::remove(new_block.items.begin(), new_block.items.end(), signature), new_block.items.end()); |
| } |
| std::shared_ptr<hash_value_item> hash_value = new_block.get_item<hash_value_item>(); |
| if (hash_value != nullptr) { |
| new_block.items.erase(std::remove(new_block.items.begin(), new_block.items.end(), hash_value), new_block.items.end()); |
| } |
| std::shared_ptr<hash_def_item> hash_def = new_block.get_item<hash_def_item>(); |
| if (hash_def != nullptr) { |
| new_block.items.erase(std::remove(new_block.items.begin(), new_block.items.end(), hash_def), new_block.items.end()); |
| } |
| |
| return new_block; |
| } |
| |
| |
| // Checksum stuff |
| uint32_t poly8_lookup[256] = |
| { |
| 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, |
| 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, |
| 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, |
| 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, |
| 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, |
| 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, |
| 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, |
| 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, |
| 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, |
| 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, |
| 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, |
| 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, |
| 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, |
| 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, |
| 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, |
| 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, |
| 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, |
| 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, |
| 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, |
| 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, |
| 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, |
| 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, |
| 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, |
| 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, |
| 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, |
| 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, |
| 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, |
| 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, |
| 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, |
| 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, |
| 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, |
| 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, |
| 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, |
| 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, |
| 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, |
| 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, |
| 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, |
| 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, |
| 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, |
| 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, |
| 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, |
| 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, |
| 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, |
| 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, |
| 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, |
| 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, |
| 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, |
| 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, |
| 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, |
| 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, |
| 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, |
| 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, |
| 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, |
| 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, |
| 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, |
| 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, |
| 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, |
| 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, |
| 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, |
| 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, |
| 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, |
| 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, |
| 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, |
| 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D |
| }; |
| |
| uint32_t crc32_byte(const uint8_t *p, uint32_t bytelength) |
| { |
| uint32_t crc = 0xffffffff; |
| while (bytelength-- !=0) crc = poly8_lookup[((uint8_t) crc ^ *(p++))] ^ (crc >> 8); |
| return crc; |
| } |
| |
| uint8_t rev_8(uint8_t b) { |
| b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; |
| b = (b & 0xCC) >> 2 | (b & 0x33) << 2; |
| b = (b & 0xAA) >> 1 | (b & 0x55) << 1; |
| return b; |
| } |
| |
| uint32_t rev_32(uint32_t b) { |
| uint8_t b0 = rev_8(b >> 24); |
| uint8_t b1 = rev_8(b >> 16); |
| uint8_t b2 = rev_8(b >> 8); |
| uint8_t b3 = rev_8(b); |
| return (b3 << 24) | (b2 << 16) | (b1 << 8) | b0; |
| } |
| |
| void crc32(const uint8_t *data, size_t len, uint32_t* cs_out) { |
| uint8_t rev_data[252] = {}; |
| for (size_t i=0; i < sizeof(rev_data); i++) { |
| rev_data[i] = rev_8(data[i]); |
| } |
| |
| uint32_t crc = crc32_byte(rev_data, sizeof(rev_data)); |
| |
| crc = rev_32(crc); |
| |
| *cs_out = crc; |
| } |
| |
| uint32_t calc_checksum(std::vector<uint8_t> bin) { |
| assert(bin.size() == 252); |
| |
| uint32_t checksum = 0; |
| crc32(bin.data(), bin.size(), &checksum); |
| |
| return checksum; |
| } |
| |
| |
| #if HAS_MBEDTLS |
| void hash_andor_sign_block(block *new_block, const public_t public_key, const private_t private_key, bool hash_value, bool sign, std::vector<uint8_t> to_hash) { |
| if (!(hash_value || sign)) { |
| // Don't need to add anything if not actually hashing or signing |
| return; |
| } |
| |
| std::shared_ptr<hash_def_item> hash_def = std::make_shared<hash_def_item>(PICOBIN_HASH_SHA256); |
| new_block->items.push_back(hash_def); |
| |
| // hash everything up to an including the hash def (todo really we shuold use the value from the hashdef when |
| // we're all done) |
| auto tmp_words = new_block->to_words(); |
| DEBUG_LOG("hash 0 + %08x\n", (int)(tmp_words.size()-3)*4); |
| if (new_block->items[0]->type() == PICOBIN_BLOCK_ITEM_1BS_IMAGE_TYPE) { |
| if (((image_type_item *)new_block->items[0].get())->flags & 0x8000) { |
| DEBUG_LOG("CLEARING TBYB FLAG\n"); |
| assert(tmp_words[1] & 0x80000000); |
| tmp_words[1] &= ~0x80000000; |
| } |
| } |
| auto block_hashed_contents = words_to_lsb_bytes(tmp_words.begin(), tmp_words.end() - 3); // remove stuff at end |
| std::copy(block_hashed_contents.begin(), block_hashed_contents.end(), std::back_inserter(to_hash)); |
| |
| message_digest_t sha256; |
| sha256_buffer(to_hash.data(), to_hash.size(), &sha256); |
| dumper("SHA256", sha256); |
| |
| if (sign) { |
| // dumper("PRIVATE KEY", private_key); |
| dumper("PUBLIC KEY", public_key); |
| |
| uint8_t entropy[32]; |
| std::random_device rand{}; |
| assert(rand.max() - rand.min() >= 256); |
| for(auto &e : entropy) { |
| e = rand(); |
| } |
| |
| signature_t sig; |
| sign_sha256(entropy, sizeof(entropy), &sha256, &public_key, &private_key, &sig); |
| dumper("SIG", sig); |
| |
| uint32_t err = verify_signature_secp256k1(&sig, &public_key, &sha256); |
| if (err) { |
| fail(ERROR_VERIFICATION_FAILED, "Signature verification failed"); |
| } |
| |
| std::shared_ptr<signature_item> signature = std::make_shared<signature_item>(PICOBIN_SIGNATURE_SECP256K1); |
| signature->public_key_bytes = std::vector<uint8_t>(public_key.bytes, public_key.bytes + sizeof(public_key.bytes)); |
| signature->signature_bytes = std::vector<uint8_t>(sig.bytes, sig.bytes + sizeof(sig.bytes)); |
| new_block->items.push_back(signature); |
| } |
| |
| if (hash_value) { |
| std::shared_ptr<hash_value_item> hash = std::make_shared<hash_value_item>(); |
| hash->hash_bytes = std::vector<uint8_t>(sha256.bytes, sha256.bytes + sizeof(sha256.bytes)); |
| new_block->items.push_back(hash); |
| } |
| } |
| |
| |
| bool check_generic_load_map(std::shared_ptr<load_map_item> load_map, model_t model, bool &pin_xip_sram) { |
| if (load_map == nullptr) { |
| return false; |
| } |
| |
| // generic xip pinning from the SDK |
| pin_xip_sram = load_map->entries.size() == 1 |
| && load_map->entries[0].storage_address == 0x0 |
| && load_map->entries[0].runtime_address == model->xip_sram_end() - 0x4 |
| && load_map->entries[0].size == 0x4; |
| return pin_xip_sram; |
| } |
| |
| |
| std::vector<uint8_t> get_lm_hash_data(elf_file *elf, block *new_block, model_t model, bool clear_sram = false, bool pin_xip_sram = false) { |
| std::vector<uint8_t> to_hash; |
| std::shared_ptr<load_map_item> load_map = new_block->get_item<load_map_item>(); |
| if (check_generic_load_map(load_map, model, pin_xip_sram)) { |
| new_block->items.erase(std::remove(new_block->items.begin(), new_block->items.end(), load_map), new_block->items.end()); |
| load_map = nullptr; |
| } |
| if (load_map == nullptr) { |
| std::vector<load_map_item::entry> entries; |
| if (clear_sram) { |
| // todo tidy up this way of hashing the uint32_t |
| std::vector<uint32_t> sram_size_vec = {model->sram_end() - model->sram_start()}; |
| entries.push_back({ |
| 0x0, |
| model->sram_start(), |
| sram_size_vec[0] |
| }); |
| auto sram_size_data = words_to_lsb_bytes(sram_size_vec.begin(), sram_size_vec.end()); |
| std::copy(sram_size_data.begin(), sram_size_data.end(), std::back_inserter(to_hash)); |
| DEBUG_LOG("CLEAR %08x + %08x\n", (int)model->sram_start(), (int)sram_size_vec[0]); |
| } |
| if (pin_xip_sram) { |
| // todo tidy up this way of hashing the uint32_t |
| std::vector<uint32_t> xip_pin_size_vec = {0x4}; |
| entries.push_back({ |
| 0x0, |
| model->xip_sram_end() - 0x4, |
| xip_pin_size_vec[0] |
| }); |
| auto xip_pin_size_data = words_to_lsb_bytes(xip_pin_size_vec.begin(), xip_pin_size_vec.end()); |
| std::copy(xip_pin_size_data.begin(), xip_pin_size_data.end(), std::back_inserter(to_hash)); |
| DEBUG_LOG("PIN XIP SRAM %08x + %08x\n", (int)model->xip_sram_end() - 0x4, (int)xip_pin_size_vec[0]); |
| } |
| for(const auto &seg : sorted_segs(elf)) { |
| if (!seg->is_load()) continue; |
| const auto data = elf->content(*seg); |
| // std::cout << "virt = " << std::hex << seg->virtual_address() << " + " << std::hex << seg->virtual_size() << ", phys = " << std::hex << seg->physical_address() << " + " << std::hex << seg->physical_size() << std::endl; |
| if (data.size() != seg->physical_size()) { |
| fail(ERROR_INCOMPATIBLE, "Elf segment physical size (%" PRIx32 ") does not match data size in file (%zx)", seg->physical_size(), data.size()); |
| } |
| if (seg->physical_size()) { |
| std::copy(data.begin(), data.end(), std::back_inserter(to_hash)); |
| DEBUG_LOG("HASH %08x + %08x\n", (int)seg->physical_address(), (int)seg->physical_size()); |
| entries.push_back( |
| { |
| (uint32_t)seg->physical_address(), |
| (uint32_t)seg->virtual_address(), |
| (uint32_t)seg->physical_size() |
| }); |
| } |
| } |
| |
| load_map = std::make_shared<load_map_item>(false, entries); |
| new_block->items.push_back(load_map); |
| } else { |
| DEBUG_LOG("Already has load map, so hashing that\n"); |
| // todo hash existing load map |
| for(const auto &entry : load_map->entries) { |
| std::vector<uint8_t> data; |
| uint32_t current_storage_address = entry.storage_address; |
| if (current_storage_address == 0) { |
| std::copy( |
| (uint8_t*)&entry.size, |
| (uint8_t*)&entry.size + sizeof(entry.size), |
| std::back_inserter(to_hash)); |
| DEBUG_LOG("CLEAR %08x + %08x\n", (int)entry.runtime_address, (int)entry.size); |
| } else { |
| while (data.size() < entry.size) { |
| auto seg = elf->segment_from_physical_address(current_storage_address); |
| if (seg == nullptr) { |
| fail(ERROR_NOT_POSSIBLE, "The ELF file does not contain the storage address %x", current_storage_address); |
| } |
| const auto new_data = elf->content(*seg); |
| |
| uint32_t offset = current_storage_address - seg->physical_address(); |
| |
| std::copy(new_data.begin()+offset, new_data.end(), std::back_inserter(data)); |
| current_storage_address += new_data.size(); |
| } |
| data.resize(entry.size); |
| std::copy(data.begin(), data.end(), std::back_inserter(to_hash)); |
| DEBUG_LOG("HASH %08x + %08x\n", (int)entry.storage_address, (int)data.size()); |
| } |
| } |
| } |
| |
| return to_hash; |
| } |
| |
| |
| std::vector<uint8_t> get_lm_hash_data(std::vector<uint8_t> bin, uint32_t storage_addr, uint32_t runtime_addr, block *new_block, get_more_bin_cb more_cb, model_t model, bool clear_sram = false, bool pin_xip_sram = false) { |
| std::vector<uint8_t> to_hash; |
| std::shared_ptr<load_map_item> load_map = new_block->get_item<load_map_item>(); |
| if (check_generic_load_map(load_map, model, pin_xip_sram)) { |
| new_block->items.erase(std::remove(new_block->items.begin(), new_block->items.end(), load_map), new_block->items.end()); |
| load_map = nullptr; |
| } |
| if (load_map == nullptr) { |
| std::vector<load_map_item::entry> entries; |
| if (clear_sram) { |
| // todo gate this clearing of SRAM |
| std::vector<uint32_t> sram_size_vec = {model->sram_end() - model->sram_start()}; |
| assert(sram_size_vec[0] % 4 == 0); |
| entries.push_back({ |
| 0x0, |
| model->sram_start(), |
| sram_size_vec[0] |
| }); |
| auto sram_size_data = words_to_lsb_bytes(sram_size_vec.begin(), sram_size_vec.end()); |
| std::copy(sram_size_data.begin(), sram_size_data.end(), std::back_inserter(to_hash)); |
| DEBUG_LOG("CLEAR %08x + %08x\n", (int)model->sram_start(), (int)sram_size_vec[0]); |
| } |
| if (pin_xip_sram) { |
| // todo tidy up this way of hashing the uint32_t |
| std::vector<uint32_t> xip_pin_size_vec = {0x4}; |
| entries.push_back({ |
| 0x0, |
| model->xip_sram_end() - 0x4, |
| xip_pin_size_vec[0] |
| }); |
| auto xip_pin_size_data = words_to_lsb_bytes(xip_pin_size_vec.begin(), xip_pin_size_vec.end()); |
| std::copy(xip_pin_size_data.begin(), xip_pin_size_data.end(), std::back_inserter(to_hash)); |
| DEBUG_LOG("PIN XIP SRAM %08x + %08x\n", (int)model->xip_sram_end() - 0x4, (int)xip_pin_size_vec[0]); |
| } |
| to_hash.insert(to_hash.begin(), bin.begin(), bin.end()); |
| DEBUG_LOG("HASH %08x + %08x\n", (int)storage_addr, (int)bin.size()); |
| entries.push_back( |
| { |
| (uint32_t)storage_addr, |
| (uint32_t)runtime_addr, |
| (uint32_t)bin.size() |
| }); |
| |
| load_map = std::make_shared<load_map_item>(false, entries); |
| new_block->items.push_back(load_map); |
| } else { |
| DEBUG_LOG("Already has load map, so hashing that\n"); |
| // todo hash existing load map |
| uint32_t current_bin_start = storage_addr; |
| for(const auto &entry : load_map->entries) { |
| if (entry.storage_address == 0) { |
| std::copy( |
| (uint8_t*)&entry.size, |
| (uint8_t*)&entry.size + sizeof(entry.size), |
| std::back_inserter(to_hash)); |
| DEBUG_LOG("CLEAR %08x + %08x\n", (int)entry.runtime_address, (int)entry.size); |
| } else { |
| if (entry.storage_address + entry.size > current_bin_start + bin.size()) { |
| if (more_cb == nullptr) { |
| fail(ERROR_NOT_POSSIBLE, "BIN does not contain data for load_map entry %08x->%08x", entry.storage_address, entry.storage_address + entry.size); |
| } |
| DEBUG_LOG("Reading into bin %08x+%x\n", entry.storage_address, entry.size); |
| more_cb(bin, entry.storage_address, entry.size); |
| current_bin_start = entry.storage_address; |
| } |
| uint32_t rel_addr = entry.storage_address - current_bin_start; |
| std::copy( |
| bin.begin() + rel_addr, |
| bin.begin() + rel_addr + entry.size, |
| std::back_inserter(to_hash)); |
| DEBUG_LOG("HASH %08x + %08x\n", (int)entry.storage_address, (int)entry.size); |
| } |
| } |
| } |
| |
| return to_hash; |
| } |
| |
| |
| int hash_andor_sign(elf_file *elf, block *new_block, const public_t public_key, const private_t private_key, model_t model, bool hash_value, bool sign, bool clear_sram, bool pin_xip_sram) { |
| std::vector<uint8_t> to_hash = get_lm_hash_data(elf, new_block, model, clear_sram, pin_xip_sram); |
| |
| hash_andor_sign_block(new_block, public_key, private_key, hash_value, sign, to_hash); |
| |
| auto tmp = new_block->to_words(); |
| std::vector<uint8_t> data = words_to_lsb_bytes(tmp.begin(), tmp.end()); |
| |
| // If multiple signature segments, need different names |
| std::string sigx = ".sigx"; |
| if (elf->get_section(sigx) != NULL) { |
| sigx += '0'; |
| } |
| while (elf->get_section(sigx) != NULL) { |
| sigx[5] += 1; |
| if (sigx[5] > '9') fail(ERROR_INCOMPATIBLE, "Only compatible with up to 10 sigx blocks"); // very unlikely anyone has more than 10 sigx blocks - that would be silly |
| } |
| elf->append_segment(new_block->physical_addr, new_block->physical_addr, data.size(), sigx); |
| auto sig_section = elf->get_section(sigx); |
| assert(sig_section); |
| assert(sig_section->virtual_address() == new_block->physical_addr); |
| |
| if (sig_section->size < data.size()) { |
| fail(ERROR_UNKNOWN, "Block is too big for elf section\n"); |
| } |
| while (data.size() < sig_section->size) { |
| data.push_back(0); |
| } |
| |
| elf->content(*sig_section, data); |
| |
| return 0; |
| } |
| |
| |
| std::vector<uint8_t> hash_andor_sign(std::vector<uint8_t> bin, uint32_t storage_addr, uint32_t runtime_addr, block *new_block, const public_t public_key, const private_t private_key, model_t model, bool hash_value, bool sign, bool clear_sram, bool pin_xip_sram) { |
| std::vector<uint8_t> to_hash = get_lm_hash_data(bin, storage_addr, runtime_addr, new_block, nullptr, model, clear_sram, pin_xip_sram); |
| |
| hash_andor_sign_block(new_block, public_key, private_key, hash_value, sign, to_hash); |
| |
| auto tmp = new_block->to_words(); |
| std::vector<uint8_t> data = words_to_lsb_bytes(tmp.begin(), tmp.end()); |
| |
| bin.insert(bin.end(), data.begin(), data.end()); |
| |
| return bin; |
| } |
| |
| |
| void verify_block(std::vector<uint8_t> bin, uint32_t storage_addr, uint32_t runtime_addr, block *block, model_t model, verified_t &hash_verified, verified_t &sig_verified, get_more_bin_cb more_cb) { |
| std::shared_ptr<load_map_item> load_map = block->get_item<load_map_item>(); |
| std::shared_ptr<hash_def_item> hash_def = block->get_item<hash_def_item>(); |
| hash_verified = none; |
| sig_verified = none; |
| if (load_map == nullptr || hash_def == nullptr) { |
| return; |
| } |
| std::vector<uint8_t> to_hash = get_lm_hash_data(bin, storage_addr, runtime_addr, block, more_cb, model); |
| |
| // auto it = std::find(block->items.begin(), block->items.end(), hash_def); |
| // assert (it != block->items.end()); |
| // int index = it - block->items.begin(); |
| |
| // hash everything specified in the hash def |
| auto tmp_words = block->to_words(); |
| tmp_words.resize(hash_def->block_words_to_hash); |
| DEBUG_LOG("hash 0 + %08x\n", (int)(tmp_words.size())*4); |
| if (block->items[0]->type() == PICOBIN_BLOCK_ITEM_1BS_IMAGE_TYPE) { |
| if (((image_type_item *)block->items[0].get())->flags & 0x8000) { |
| DEBUG_LOG("CLEARING TBYB FLAG\n"); |
| assert(tmp_words[1] & 0x80000000); |
| tmp_words[1] &= ~0x80000000; |
| } |
| } |
| auto block_hashed_contents = words_to_lsb_bytes(tmp_words.begin(), tmp_words.end()); |
| std::copy(block_hashed_contents.begin(), block_hashed_contents.end(), std::back_inserter(to_hash)); |
| |
| message_digest_t sha256; |
| message_digest_t block_sha256; |
| sha256_buffer(to_hash.data(), to_hash.size(), &sha256); |
| dumper("SHA256", sha256); |
| |
| std::shared_ptr<hash_value_item> hash_value = block->get_item<hash_value_item>(); |
| if (hash_value != nullptr) { |
| memcpy(block_sha256.bytes, hash_value->hash_bytes.data(), hash_value->hash_bytes.size()); |
| if (std::equal(hash_value->hash_bytes.begin(), hash_value->hash_bytes.end(), sha256.bytes)) { |
| DEBUG_LOG("It's a match!\n"); |
| hash_verified = passed; |
| } else { |
| hash_verified = failed; |
| } |
| } |
| |
| std::shared_ptr<signature_item> signature = block->get_item<signature_item>(); |
| if (signature != nullptr) { |
| public_t public_key = {}; |
| memcpy(public_key.bytes, signature->public_key_bytes.data(), signature->public_key_bytes.size()); |
| dumper("PUBLIC KEY", public_key); |
| |
| signature_t sig { |
| .bytes = {}, |
| .der = {}, |
| .der_len = 0, |
| }; |
| memcpy(sig.bytes, signature->signature_bytes.data(), signature->signature_bytes.size()); |
| dumper("SIG", sig); |
| |
| uint32_t err = verify_signature_secp256k1(&sig, &public_key, &sha256); |
| if (err) { |
| sig_verified = failed; |
| } else { |
| DEBUG_LOG("It's a match!\n"); |
| sig_verified = passed; |
| } |
| } |
| } |
| |
| |
| void encrypt_guts(elf_file *elf, block *new_block, const aes_key_t aes_key, model_t model, std::vector<uint8_t> &iv_data, std::vector<uint8_t> &enc_data) { |
| std::vector<uint8_t> to_enc = get_lm_hash_data(elf, new_block, model); |
| |
| std::random_device rand{}; |
| assert(rand.max() - rand.min() >= 256); |
| |
| while (to_enc.size() % 16 != 0){ |
| to_enc.push_back(rand()); // todo maybe better padding? random should be fine though |
| } |
| DEBUG_LOG("size %08x\n", (int)to_enc.size()); |
| |
| iv_t iv; |
| for(auto &e : iv.bytes) { |
| e = rand(); |
| } |
| |
| iv_data.resize(sizeof(iv.bytes)); |
| memcpy(iv_data.data(), iv.bytes, sizeof(iv.bytes)); |
| |
| enc_data.resize(to_enc.size()); |
| |
| aes256_buffer(to_enc.data(), to_enc.size(), enc_data.data(), &aes_key, &iv); |
| } |
| |
| |
| int encrypt(elf_file *elf, block *new_block, const aes_key_t aes_key, const public_t public_key, const private_t private_key, model_t model, std::vector<uint8_t> iv_salt, bool hash_value, bool sign) { |
| |
| std::vector<uint8_t> iv_data; |
| std::vector<uint8_t> enc_data; |
| encrypt_guts(elf, new_block, aes_key, model, iv_data, enc_data); |
| |
| // Salt IV |
| assert(iv_data.size() == iv_salt.size()); |
| for (int i=0; i < iv_data.size(); i++) { |
| iv_data[i] ^= iv_salt[i]; |
| } |
| |
| unsigned int i=0; |
| for(const auto &seg : sorted_segs(elf)) { |
| if (!seg->is_load()) continue; |
| std::vector<uint8_t> data(enc_data.begin() + i, enc_data.begin() + i + seg->physical_size()); |
| // std::cout << "virt = " << std::hex << seg->virtual_address() << " + " << std::hex << seg->virtual_size() << ", phys = " << std::hex << seg->physical_address() << " + " << std::hex << seg->physical_size() << std::endl; |
| if (data.size() != seg->physical_size()) { |
| fail(ERROR_INCOMPATIBLE, "Elf segment physical size (%" PRIx32 ") does not match data size in file (%zx)", seg->physical_size(), data.size()); |
| } |
| if (seg->physical_size() && seg->physical_address() < new_block->physical_addr) { |
| DEBUG_LOG("ENCRYPTED %08x + %08x\n", (int)seg->physical_address(), (int)seg->physical_size()); |
| elf->content(*seg, data); |
| i += data.size(); |
| assert(i <= enc_data.size()); |
| } |
| } |
| assert(i <= enc_data.size()); |
| if (i < enc_data.size()) { |
| elf->append_segment(new_block->physical_addr, new_block->physical_addr, enc_data.size() - i, ".enc_pad"); |
| auto pad_section = elf->get_section(".enc_pad"); |
| assert(pad_section); |
| assert(pad_section->virtual_address() == new_block->physical_addr); |
| |
| if (pad_section->size < enc_data.size() - i) { |
| fail(ERROR_UNKNOWN, "Block is too big for elf section\n"); |
| } |
| |
| std::vector<uint8_t> pad_data(enc_data.begin() + i, enc_data.end()); |
| |
| DEBUG_LOG("Adding padding len %d\n", (int)pad_data.size()); |
| for (auto x : pad_data) DEBUG_LOG("%02x", x); |
| DEBUG_LOG("\n"); |
| |
| elf->content(*pad_section, pad_data); |
| } |
| |
| block link_block(0x20000000, enc_data.size()); |
| // ignored_item ign(1, {0}); |
| std::shared_ptr<image_type_item> image_def = new_block->get_item<image_type_item>(); |
| link_block.items.push_back(image_def); |
| |
| link_block.next_block_rel += (link_block.to_words().size())*4 + iv_data.size(); |
| |
| auto tmp = link_block.to_words(); |
| DEBUG_LOG("Link block\n"); |
| for (auto x : tmp) DEBUG_LOG("%08x", x); |
| DEBUG_LOG("\n"); |
| |
| std::vector<uint8_t> link_data = words_to_lsb_bytes(tmp.begin(), tmp.end()); |
| |
| elf->move_all(link_data.size() + iv_data.size()); |
| |
| elf->append_segment(link_block.physical_addr, link_block.physical_addr, link_data.size(), ".enc_link"); |
| auto link_section = elf->get_section(".enc_link"); |
| assert(link_section); |
| assert(link_section->virtual_address() == link_block.physical_addr); |
| if (link_section->size < link_data.size()) { |
| fail(ERROR_UNKNOWN, "Block is too big for elf section\n"); |
| } |
| elf->content(*link_section, link_data); |
| |
| elf->append_segment(link_block.physical_addr + link_data.size(), link_block.physical_addr + link_data.size(), iv_data.size(), ".enc_iv"); |
| auto iv_section = elf->get_section(".enc_iv"); |
| assert(iv_section); |
| if (iv_section->size < iv_data.size()) { |
| fail(ERROR_UNKNOWN, "Block is too big for elf section\n"); |
| } |
| elf->content(*iv_section, iv_data); |
| |
| new_block->physical_addr = link_block.physical_addr + link_block.next_block_rel; |
| new_block->next_block_rel = -link_block.next_block_rel; |
| |
| std::shared_ptr<load_map_item> load_map = new_block->get_item<load_map_item>(); |
| if (load_map != nullptr) { |
| new_block->items.erase(std::remove(new_block->items.begin(), new_block->items.end(), load_map), new_block->items.end()); |
| } |
| |
| hash_andor_sign(elf, new_block, public_key, private_key, model, hash_value, sign); |
| |
| return 0; |
| } |
| |
| |
| std::vector<uint8_t> encrypt(std::vector<uint8_t> bin, uint32_t storage_addr, uint32_t runtime_addr, block *new_block, const aes_key_t aes_key, const public_t public_key, const private_t private_key, model_t model, std::vector<uint8_t> iv_salt, bool hash_value, bool sign) { |
| std::random_device rand{}; |
| assert(rand.max() - rand.min() >= 256); |
| |
| while (bin.size() % 16 != 0){ |
| bin.push_back(rand()); // todo maybe better padding? random should be fine though |
| } |
| DEBUG_LOG("size %08x\n", (int)bin.size()); |
| |
| iv_t iv; |
| for(auto &e : iv.bytes) { |
| e = rand(); |
| } |
| |
| std::vector<uint8_t> iv_data(iv.bytes, iv.bytes + sizeof(iv.bytes)); |
| |
| // Salt IV |
| assert(iv_data.size() == iv_salt.size()); |
| for (int i=0; i < iv_data.size(); i++) { |
| iv_data[i] ^= iv_salt[i]; |
| } |
| |
| std::vector<uint8_t> enc_data; |
| enc_data.resize(bin.size()); |
| |
| aes256_buffer(bin.data(), bin.size(), enc_data.data(), &aes_key, &iv); |
| std::copy(enc_data.begin(), enc_data.end(), bin.begin()); |
| |
| block link_block(0x20000000, enc_data.size()); |
| // ignored_item ign(1, {0}); |
| std::shared_ptr<image_type_item> image_def = new_block->get_item<image_type_item>(); |
| link_block.items.push_back(image_def); |
| |
| link_block.next_block_rel += (link_block.to_words().size())*4 + iv_data.size(); |
| |
| auto tmp = link_block.to_words(); |
| DEBUG_LOG("Link block\n"); |
| for (auto x : tmp) DEBUG_LOG("%08x", x); |
| DEBUG_LOG("\n"); |
| |
| std::vector<uint8_t> link_data = words_to_lsb_bytes(tmp.begin(), tmp.end()); |
| |
| bin.insert(bin.begin(), link_data.begin(), link_data.end()); |
| |
| bin.insert(bin.begin() + link_data.size(), iv_data.begin(), iv_data.end()); |
| |
| new_block->physical_addr = link_block.physical_addr + link_block.next_block_rel; |
| new_block->next_block_rel = -link_block.next_block_rel; |
| |
| std::shared_ptr<load_map_item> load_map = new_block->get_item<load_map_item>(); |
| if (load_map != nullptr) { |
| new_block->items.erase(std::remove(new_block->items.begin(), new_block->items.end(), load_map), new_block->items.end()); |
| } |
| |
| return hash_andor_sign(bin, storage_addr, runtime_addr, new_block, public_key, private_key, model, hash_value, sign); |
| } |
| #endif |