diff --git a/utils/imxtools/sbtools/Makefile b/utils/imxtools/sbtools/Makefile index 7a09d86d24..7d00e4b8c1 100644 --- a/utils/imxtools/sbtools/Makefile +++ b/utils/imxtools/sbtools/Makefile @@ -10,7 +10,7 @@ all: $(BINS) %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< -sbtoelf: sbtoelf.o crc.o crypto.o aes128.o sha1.o elf.o misc.o sb.o +sbtoelf: sbtoelf.o crc.o crypto.o aes128.o sha1.o xorcrypt.o elf.o misc.o sb.o sb1.o $(LD) -o $@ $^ $(LDFLAGS) elftosb: elftosb.o crc.o crypto.o aes128.o sha1.o elf.o dbparser.o misc.o sb.o diff --git a/utils/imxtools/sbtools/crypto.c b/utils/imxtools/sbtools/crypto.c index d4afc6c816..d73547de25 100644 --- a/utils/imxtools/sbtools/crypto.c +++ b/utils/imxtools/sbtools/crypto.c @@ -28,6 +28,7 @@ static enum crypto_method_t cur_method = CRYPTO_NONE; static byte key[16]; +static union xorcrypt_key_t xor_key[2]; static uint16_t usb_vid, usb_pid; void crypto_setup(enum crypto_method_t method, void *param) diff --git a/utils/imxtools/sbtools/crypto.h b/utils/imxtools/sbtools/crypto.h index 452db6a28d..599ebf4bf4 100644 --- a/utils/imxtools/sbtools/crypto.h +++ b/utils/imxtools/sbtools/crypto.h @@ -47,6 +47,7 @@ enum crypto_method_t { CRYPTO_NONE, /* disable */ CRYPTO_KEY, /* key */ + CRYPTO_XOR_KEY, /* XOR key */ CRYPTO_USBOTP, /* use usbotp device */ }; @@ -73,6 +74,12 @@ int crypto_apply( byte (*out_cbc_mac)[16], /* CBC-MAC of the result (or NULL) */ int encrypt); +union xorcrypt_key_t +{ + uint8_t key[64]; + uint32_t k[16]; +}; + /* all-in-one function */ struct crypto_key_t { @@ -80,6 +87,7 @@ struct crypto_key_t union { byte key[16]; + union xorcrypt_key_t xor_key[2]; uint32_t vid_pid; byte param[0]; }u; @@ -112,4 +120,10 @@ void sha_1_update(struct sha_1_params_t *params, byte *buffer, int size); void sha_1_finish(struct sha_1_params_t *params); void sha_1_output(struct sha_1_params_t *params, byte *out); +/* xorcrypt.c */ + +// WARNING those functions modifies the keys !! +uint32_t xor_encrypt(union xorcrypt_key_t keys[2], void *data, int size); +uint32_t xor_decrypt(union xorcrypt_key_t keys[2], void *data, int size); + #endif /* __CRYPTO_H__ */ diff --git a/utils/imxtools/sbtools/sb1.c b/utils/imxtools/sbtools/sb1.c new file mode 100644 index 0000000000..19f53b0015 --- /dev/null +++ b/utils/imxtools/sbtools/sb1.c @@ -0,0 +1,288 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2012 Amaury Pouly + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include +#include +#include +#include +#include "misc.h" +#include "crypto.h" +#include "sb1.h" + +static uint16_t swap16(uint16_t t) +{ + return (t << 8) | (t >> 8); +} + +static void fix_version(struct sb1_version_t *ver) +{ + ver->major = swap16(ver->major); + ver->minor = swap16(ver->minor); + ver->revision = swap16(ver->revision); +} + +enum sb1_error_t sb1_write_file(struct sb1_file_t *sb, const char *filename) +{ + return SB1_ERROR; +} + +struct sb1_file_t *sb1_read_file(const char *filename, void *u, + sb1_color_printf cprintf, enum sb1_error_t *err) +{ + return sb1_read_file_ex(filename, 0, -1, u, cprintf, err); +} + +struct sb1_file_t *sb1_read_file_ex(const char *filename, size_t offset, size_t size, void *u, + sb1_color_printf cprintf, enum sb1_error_t *err) +{ + #define fatal(e, ...) \ + do { if(err) *err = e; \ + cprintf(u, true, GREY, __VA_ARGS__); \ + free(buf); \ + return NULL; } while(0) + + FILE *f = fopen(filename, "rb"); + void *buf = NULL; + if(f == NULL) + fatal(SB1_OPEN_ERROR, "Cannot open file for reading\n"); + fseek(f, 0, SEEK_END); + size_t read_size = ftell(f); + fseek(f, offset, SEEK_SET); + if(size != (size_t)-1) + read_size = size; + buf = xmalloc(read_size); + if(fread(buf, read_size, 1, f) != 1) + { + fclose(f); + fatal(SB1_READ_ERROR, "Cannot read file\n"); + } + fclose(f); + + struct sb1_file_t *ret = sb1_read_memory(buf, read_size, u, cprintf, err); + free(buf); + return ret; + + #undef fatal +} + +static const char *sb1_cmd_name(int cmd) +{ + switch(cmd) + { + case SB1_INST_LOAD: return "load"; + case SB1_INST_FILL: return "fill"; + case SB1_INST_JUMP: return "jump"; + case SB1_INST_CALL: return "call"; + case SB1_INST_MODE: return "mode"; + case SB1_INST_SDRAM: return "sdram"; + default: return "unknown"; + } +} + +struct sb1_file_t *sb1_read_memory(void *_buf, size_t filesize, void *u, + sb1_color_printf cprintf, enum sb1_error_t *err) +{ + struct sb1_file_t *sb1_file = NULL; + uint8_t *buf = _buf; + + #define printf(c, ...) cprintf(u, false, c, __VA_ARGS__) + #define fatal(e, ...) \ + do { if(err) *err = e; \ + cprintf(u, true, GREY, __VA_ARGS__); \ + sb1_free(sb1_file); \ + return NULL; } while(0) + #define print_hex(c, p, len, nl) \ + do { printf(c, ""); print_hex(p, len, nl); } while(0) + + sb1_file = xmalloc(sizeof(struct sb1_file_t)); + memset(sb1_file, 0, sizeof(struct sb1_file_t)); + struct sb1_header_t *header = (struct sb1_header_t *)buf; + + if(memcmp(header->signature, "STMP", 4) != 0) + fatal(SB1_FORMAT_ERROR, "Bad signature\n"); + if(header->image_size > filesize) + fatal(SB1_FORMAT_ERROR, "File too small (should be at least %d bytes)\n", + header->image_size); + if(header->header_size != sizeof(struct sb1_header_t)) + fatal(SB1_FORMAT_ERROR, "Bad header size\n"); + + printf(BLUE, "Basic info:\n"); + printf(GREEN, " ROM version: "); + printf(YELLOW, "%x\n", header->rom_version); + printf(GREEN, " Userdata offset: "); + printf(YELLOW, "%x\n", header->userdata_offset); + printf(GREEN, " Pad: "); + printf(YELLOW, "%x\n", header->pad2); + + struct sb1_version_t product_ver = header->product_ver; + fix_version(&product_ver); + struct sb1_version_t component_ver = header->component_ver; + fix_version(&component_ver); + + memcpy(&sb1_file->product_ver, &product_ver, sizeof(product_ver)); + memcpy(&sb1_file->component_ver, &component_ver, sizeof(component_ver)); + + printf(GREEN, " Product version: "); + printf(YELLOW, "%X.%X.%X\n", product_ver.major, product_ver.minor, product_ver.revision); + printf(GREEN, " Component version: "); + printf(YELLOW, "%X.%X.%X\n", component_ver.major, component_ver.minor, component_ver.revision); + + printf(GREEN, " Drive tag: "); + printf(YELLOW, "%x\n", header->drive_tag); + + /* reduce size w.r.t to userdata part */ + uint32_t userdata_size = 0; + if(header->userdata_offset != 0) + { + userdata_size = header->image_size - header->userdata_offset; + header->image_size -= userdata_size; + } + + if(header->image_size % SECTOR_SIZE) + { + if(g_force) + printf(GREY, "Image size is not a multiple of sector size\n"); + else + fatal(SB1_FORMAT_ERROR, "Image size is not a multiple of sector size\n"); + } + + /* find key */ + union xorcrypt_key_t key[2]; + bool valid_key = false; + uint8_t sector[SECTOR_SIZE]; + + for(int i = 0; i < g_nr_keys; i++) + { + if(!g_key_array[i].method == CRYPTO_XOR_KEY) + continue; + /* copy key and data because it's modified by the crypto code */ + memcpy(key, g_key_array[i].u.xor_key, sizeof(key)); + memcpy(sector, header + 1, SECTOR_SIZE - header->header_size); + /* try to decrypt the first sector */ + uint32_t mark = xor_decrypt(key, sector, SECTOR_SIZE - 4 - header->header_size); + if(mark != *(uint32_t *)§or[SECTOR_SIZE - 4 - header->header_size]) + continue; + /* found ! */ + valid_key = true; + /* copy key again it's modified by the crypto code */ + memcpy(key, g_key_array[i].u.xor_key, sizeof(key)); + break; + } + + printf(BLUE, "Crypto\n"); + for(int i = 0; i < 2; i++) + { + printf(RED, " Key %d\n", i); + printf(OFF, " "); + for(int j = 0; j < 64; j++) + { + printf(YELLOW, "%02x ", key[i].key[j]); + if((j + 1) % 16 == 0) + { + printf(OFF, "\n"); + if(j + 1 != 64) + printf(OFF, " "); + } + } + } + + if(!valid_key) + fatal(SB1_NO_VALID_KEY, "No valid key found\n"); + + /* decrypt image in-place (and removing crypto markers) */ + void *ptr = header + 1; + void *copy_ptr = header + 1; + int offset = header->header_size; + for(unsigned i = 0; i < header->image_size / SECTOR_SIZE; i++) + { + int size = SECTOR_SIZE - 4 - offset; + uint32_t mark = xor_decrypt(key, ptr, size); + if(mark != *(uint32_t *)(ptr + size)) + fatal(SB1_CHECKSUM_ERROR, "Crypto mark mismatch\n"); + memmove(copy_ptr, ptr, size); + ptr += size + 4; + copy_ptr += size; + offset = 0; + } + + /* reduce image size given the removed marks */ + header->image_size -= header->image_size / SECTOR_SIZE; + + printf(BLUE, "Commands\n"); + struct sb1_cmd_header_t *cmd = (void *)(header + 1); + while((void *)cmd < (void *)header + header->image_size) + { + printf(GREEN, " Command"); + printf(YELLOW, " %#x\n", cmd->cmd); + printf(YELLOW, " Size:"); + printf(RED, " %#x\n", SB1_CMD_SIZE(cmd->cmd)); + printf(YELLOW, " Critical:"); + printf(RED, " %d\n", SB1_CMD_CRITICAL(cmd->cmd)); + printf(YELLOW, " Data Type:"); + printf(RED, " %#x\n", SB1_CMD_DATATYPE(cmd->cmd)); + printf(YELLOW, " Bytes:"); + printf(RED, " %#x\n", SB1_CMD_BYTES(cmd->cmd)); + printf(YELLOW, " Boot:"); + printf(RED, " %#x (%s)\n", SB1_CMD_BOOT(cmd->cmd), sb1_cmd_name(SB1_CMD_BOOT(cmd->cmd))); + printf(YELLOW, " Addr:"); + printf(RED, " %#x\n", cmd->addr); + + /* last instruction ? */ + if(SB1_CMD_BOOT(cmd->cmd) == SB1_INST_JUMP || + SB1_CMD_BOOT(cmd->cmd) == SB1_INST_MODE) + break; + + cmd = (void *)cmd + 4 + 4 * SB1_CMD_SIZE(cmd->cmd); + } + + sb1_file->data_size = header->image_size - header->header_size; + sb1_file->data = malloc(sb1_file->data_size); + memcpy(sb1_file->data, header + 1, sb1_file->data_size); + + return sb1_file; + #undef printf + #undef fatal + #undef print_hex +} + +void sb1_free(struct sb1_file_t *file) +{ + if(!file) return; + + free(file->data); + free(file); +} + +void sb1_dump(struct sb1_file_t *file, void *u, sb1_color_printf cprintf) +{ + #define printf(c, ...) cprintf(u, false, c, __VA_ARGS__) + #define print_hex(c, p, len, nl) \ + do { printf(c, ""); print_hex(p, len, nl); } while(0) + + #define TREE RED + #define HEADER GREEN + #define TEXT YELLOW + #define TEXT2 BLUE + #define SEP OFF + + #undef printf + #undef print_hex +} + diff --git a/utils/imxtools/sbtools/sb1.h b/utils/imxtools/sbtools/sb1.h new file mode 100644 index 0000000000..f0a7a4ebc8 --- /dev/null +++ b/utils/imxtools/sbtools/sb1.h @@ -0,0 +1,111 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2012 Amaury Pouly + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef __SB1_H__ +#define __SB1_H__ + +#include +#include + +#include "misc.h" + +#define SECTOR_SIZE 512 + +/* All fields are in big-endian BCD */ +struct sb1_version_t +{ + uint16_t major; + uint16_t pad0; + uint16_t minor; + uint16_t pad1; + uint16_t revision; + uint16_t pad2; +}; + +struct sb1_header_t +{ + uint32_t rom_version; + uint32_t image_size; + uint32_t header_size; + uint32_t userdata_offset; + uint32_t pad2; + uint8_t signature[4]; /* Signature "STMP" */ + struct sb1_version_t product_ver; + struct sb1_version_t component_ver; + uint32_t drive_tag; +} __attribute__((packed)); + +struct sb1_cmd_header_t +{ + uint32_t cmd; // 31:21=cmd size, 20=critical, 19:6=size 5:4=datatype, 3:0=boot cmd + uint32_t addr; +} __attribute__((packed)); + +#define SB1_CMD_SIZE(cmd) ((cmd) >> 21) +#define SB1_CMD_CRITICAL(cmd) !!(cmd & (1 << 20)) +#define SB1_CMD_BYTES(cmd) (((cmd) >> 6) & 0x3fff) +#define SB1_CMD_DATATYPE(cmd) (((cmd) >> 4) & 0x3) +#define SB1_CMD_BOOT(cmd) ((cmd) & 0xf) + +#define SB1_INST_LOAD 0x1 +#define SB1_INST_FILL 0x2 +#define SB1_INST_JUMP 0x3 +#define SB1_INST_CALL 0x4 +#define SB1_INST_MODE 0x5 +#define SB1_INST_SDRAM 0x6 + +struct sb1_file_t +{ + struct sb1_version_t product_ver; + struct sb1_version_t component_ver; + void *data; + int data_size; +}; + +enum sb1_error_t +{ + SB1_SUCCESS = 0, + SB1_ERROR = -1, + SB1_OPEN_ERROR = -2, + SB1_READ_ERROR = -3, + SB1_WRITE_ERROR = -4, + SB1_FORMAT_ERROR = -5, + SB1_CHECKSUM_ERROR = -6, + SB1_NO_VALID_KEY = -7, + SB1_FIRST_CRYPTO_ERROR = -8, + SB1_LAST_CRYPTO_ERROR = SB1_FIRST_CRYPTO_ERROR - CRYPTO_NUM_ERRORS, +}; + +enum sb1_error_t sb1_write_file(struct sb1_file_t *sb, const char *filename); + +typedef void (*sb1_color_printf)(void *u, bool err, color_t c, const char *f, ...); +struct sb1_file_t *sb1_read_file(const char *filename, void *u, + sb1_color_printf printf, enum sb1_error_t *err); +/* use size_t(-1) to use maximum size */ +struct sb1_file_t *sb1_read_file_ex(const char *filename, size_t offset, size_t size, + void *u, sb1_color_printf printf, enum sb1_error_t *err); +struct sb1_file_t *sb1_read_memory(void *buffer, size_t size, void *u, + sb1_color_printf printf, enum sb1_error_t *err); + +void sb1_dump(struct sb1_file_t *file, void *u, sb1_color_printf printf); +void sb1_free(struct sb1_file_t *file); + +#endif /* __SB1_H__ */ + diff --git a/utils/imxtools/sbtools/sbtoelf.c b/utils/imxtools/sbtools/sbtoelf.c index 0170ea1836..0445c4a366 100644 --- a/utils/imxtools/sbtools/sbtoelf.c +++ b/utils/imxtools/sbtools/sbtoelf.c @@ -40,6 +40,7 @@ #include "crypto.h" #include "elf.h" #include "sb.h" +#include "sb1.h" #include "misc.h" /* all blocks are sized as a multiple of 0x1ff */ @@ -156,6 +157,15 @@ static void extract_sb_file(struct sb_file_t *file) extract_sb_section(&file->sections[i]); } +static void extract_sb1_file(struct sb1_file_t *file) +{ + FILE *f = fopen(g_out_prefix, "wb"); + if(f == NULL) + bugp("Cannot open %s for writing\n", g_out_prefix); + fwrite(file->data, file->data_size, 1, f); + fclose(f); +} + static void usage(void) { printf("Usage: sbtoelf [options] sb-file\n"); @@ -170,6 +180,8 @@ static void usage(void) printf(" -n/--no-color\tDisable output colors\n"); printf(" -l/--loopback \tProduce sb file out of extracted description*\n"); printf(" -f/--force\tForce reading even without a key*\n"); + printf(" -1/--v1\tForce to read file as a version 1 file\n"); + printf(" -2/--v2\tForce to read file as a version 2 file\n"); printf("Options marked with a * are for debug purpose only\n"); exit(1); } @@ -191,11 +203,72 @@ static struct crypto_key_t g_zero_key = .u.key = {0} }; +static struct crypto_key_t g_default_xor_key = +{ + .method = CRYPTO_XOR_KEY, + .u.xor_key = + { + {.k = {0x67ECAEF6, 0xB31FB961, 0x118A9F4C, 0xA32A97DA, + 0x6CC39617, 0x5BC00314, 0x9D430685, 0x4D7DB502, + 0xA347685E, 0x3C87E86C, 0x8987AAA0, 0x24B78EF1, + 0x893B9605, 0x9BB8C2BE, 0x6D9544E2, 0x375B525C}}, + {.k = {0x3F424704, 0x53B5A331, 0x6AD345A5, 0x20DCEC51, + 0x743C8D3B, 0x444B3792, 0x0AF429569, 0xB7EE1111, + 0x583BF768, 0x9683BF9A, 0x0B032D799, 0xFE4E78ED, + 0xF20D08C2, 0xFA0BE4A2, 0x4D89C317, 0x887B2D6F}} + } +}; + +enum sb_version_guess_t +{ + SB_VERSION_1, + SB_VERSION_2, + SB_VERSION_UNK, +}; + +enum sb_version_guess_t guess_sb_version(const char *filename) +{ + FILE *f = fopen(filename, "rb"); + if(f == NULL) + bugp("Cannot open file for reading\n"); + // check signature + uint8_t sig[4]; + if(fseek(f, 20, SEEK_SET)) + return SB_VERSION_UNK; + if(fread(sig, 4, 1, f) != 1) + return SB_VERSION_UNK; + if(memcmp(sig, "STMP", 4) != 0) + return SB_VERSION_UNK; + // check header size (v1) + uint32_t hdr_size; + if(fseek(f, 8, SEEK_SET)) + return SB_VERSION_UNK; + if(fread(&hdr_size, 4, 1, f) != 1) + return SB_VERSION_UNK; + if(hdr_size == 0x34) + return SB_VERSION_1; + // check header size (v2) + if(fseek(f, 32, SEEK_SET)) + return SB_VERSION_UNK; + if(fread(&hdr_size, 4, 1, f) != 1) + return SB_VERSION_UNK; + if(hdr_size == 0xc) + return SB_VERSION_2; + return SB_VERSION_UNK; +} + int main(int argc, char **argv) { bool raw_mode = false; const char *loopback = NULL; - + bool force_sb1 = false; + bool force_sb2 = false; + + /* decrypt the xor key which is xor'ed */ + for(int i = 0; i < 2; i++) + for(int j = 0; j < 16; j++) + g_default_xor_key.u.xor_key[i].k[j] ^= 0xaa55aa55; + while(1) { static struct option long_options[] = @@ -205,11 +278,13 @@ int main(int argc, char **argv) {"add-key", required_argument, 0, 'a'}, {"no-color", no_argument, 0, 'n'}, {"loopback", required_argument, 0, 'l'}, - {"force", no_argument, 0, 'f' }, + {"force", no_argument, 0, 'f'}, + {"v1", no_argument, 0, '1'}, + {"v2", no_argument, 0, '2'}, {0, 0, 0, 0} }; - int c = getopt_long(argc, argv, "?do:k:zra:nl:f", long_options, NULL); + int c = getopt_long(argc, argv, "?do:k:zra:nl:f12x", long_options, NULL); if(c == -1) break; switch(c) @@ -243,10 +318,11 @@ int main(int argc, char **argv) break; } case 'z': - { add_keys(&g_zero_key, 1); break; - } + case 'x': + add_keys(&g_default_xor_key, 1); + break; case 'r': raw_mode = true; break; @@ -261,11 +337,20 @@ int main(int argc, char **argv) add_keys(&key, 1); break; } + case '1': + force_sb1 = true; + break; + case '2': + force_sb2 = true; + break; default: abort(); } } + if(force_sb1 && force_sb2) + bug("You cannot force both version 1 and 2\n"); + if(argc - optind != 1) { usage(); @@ -274,34 +359,70 @@ int main(int argc, char **argv) const char *sb_filename = argv[optind]; - enum sb_error_t err; - struct sb_file_t *file = sb_read_file(sb_filename, raw_mode, NULL, sb_printf, &err); - if(file == NULL) + enum sb_version_guess_t ver = guess_sb_version(sb_filename); + + if(force_sb2 || ver == SB_VERSION_2) + { + enum sb_error_t err; + struct sb_file_t *file = sb_read_file(sb_filename, raw_mode, NULL, sb_printf, &err); + if(file == NULL) + { + color(OFF); + printf("SB read failed: %d\n", err); + return 1; + } + + color(OFF); + if(g_out_prefix) + extract_sb_file(file); + if(g_debug) + { + color(GREY); + printf("[Debug output]\n"); + sb_dump(file, NULL, sb_printf); + } + if(loopback) + { + /* sb_read_file will fill real key and IV but we don't want to override + * them when looping back otherwise the output will be inconsistent and + * garbage */ + file->override_real_key = false; + file->override_crypto_iv = false; + sb_write_file(file, loopback); + } + sb_free(file); + } + else if(force_sb1 || ver == SB_VERSION_1) + { + enum sb1_error_t err; + struct sb1_file_t *file = sb1_read_file(sb_filename, NULL, sb_printf, &err); + if(file == NULL) + { + color(OFF); + printf("SB read failed: %d\n", err); + return 1; + } + + color(OFF); + if(g_out_prefix) + extract_sb1_file(file); + if(g_debug) + { + color(GREY); + printf("[Debug output]\n"); + sb1_dump(file, NULL, sb_printf); + } + if(loopback) + sb1_write_file(file, loopback); + + sb1_free(file); + } + else { color(OFF); - printf("SB read failed: %d\n", err); + printf("Cannot guess file type, are you sure it's a valid image ?\n"); return 1; } - - color(OFF); - if(g_out_prefix) - extract_sb_file(file); - if(g_debug) - { - color(GREY); - printf("[Debug output]\n"); - sb_dump(file, NULL, sb_printf); - } - if(loopback) - { - /* sb_read_file will fill real key and IV but we don't want to override - * them when looping back otherwise the output will be inconsistent and - * garbage */ - file->override_real_key = false; - file->override_crypto_iv = false; - sb_write_file(file, loopback); - } - sb_free(file); clear_keys(); return 0;