From a87a9ef37372b4380808ec2efa7c762e137668f1 Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Wed, 23 May 2012 11:03:35 +0200 Subject: imxtools: move tools to a new sbtools/ subdirectory Change-Id: I0d8d6831b35037725486f61fc363de87bc8ba92e --- utils/imxtools/sbtools/Makefile | 26 + utils/imxtools/sbtools/README | 32 + utils/imxtools/sbtools/aes128.c | 284 +++++++ utils/imxtools/sbtools/crc.c | 83 ++ utils/imxtools/sbtools/crypto.c | 188 +++++ utils/imxtools/sbtools/crypto.h | 115 +++ utils/imxtools/sbtools/dbparser.c | 857 ++++++++++++++++++++ utils/imxtools/sbtools/dbparser.h | 118 +++ utils/imxtools/sbtools/elf.c | 575 ++++++++++++++ utils/imxtools/sbtools/elf.h | 94 +++ utils/imxtools/sbtools/elftosb.c | 462 +++++++++++ utils/imxtools/sbtools/fuze+_key_file.txt | 1 + utils/imxtools/sbtools/misc.c | 265 +++++++ utils/imxtools/sbtools/misc.h | 59 ++ utils/imxtools/sbtools/sb.c | 1224 +++++++++++++++++++++++++++++ utils/imxtools/sbtools/sb.h | 243 ++++++ utils/imxtools/sbtools/sbloader.c | 193 +++++ utils/imxtools/sbtools/sbtoelf.c | 303 +++++++ utils/imxtools/sbtools/sha1.c | 150 ++++ 19 files changed, 5272 insertions(+) create mode 100644 utils/imxtools/sbtools/Makefile create mode 100644 utils/imxtools/sbtools/README create mode 100644 utils/imxtools/sbtools/aes128.c create mode 100644 utils/imxtools/sbtools/crc.c create mode 100644 utils/imxtools/sbtools/crypto.c create mode 100644 utils/imxtools/sbtools/crypto.h create mode 100644 utils/imxtools/sbtools/dbparser.c create mode 100644 utils/imxtools/sbtools/dbparser.h create mode 100644 utils/imxtools/sbtools/elf.c create mode 100644 utils/imxtools/sbtools/elf.h create mode 100644 utils/imxtools/sbtools/elftosb.c create mode 100644 utils/imxtools/sbtools/fuze+_key_file.txt create mode 100644 utils/imxtools/sbtools/misc.c create mode 100644 utils/imxtools/sbtools/misc.h create mode 100644 utils/imxtools/sbtools/sb.c create mode 100644 utils/imxtools/sbtools/sb.h create mode 100644 utils/imxtools/sbtools/sbloader.c create mode 100644 utils/imxtools/sbtools/sbtoelf.c create mode 100644 utils/imxtools/sbtools/sha1.c (limited to 'utils/imxtools/sbtools') diff --git a/utils/imxtools/sbtools/Makefile b/utils/imxtools/sbtools/Makefile new file mode 100644 index 0000000000..7a09d86d24 --- /dev/null +++ b/utils/imxtools/sbtools/Makefile @@ -0,0 +1,26 @@ +DEFINES=-DCRYPTO_LIBUSB +CC=gcc +LD=gcc +CFLAGS=-g -std=c99 -W -Wall `pkg-config --cflags libusb-1.0` $(DEFINES) +LDFLAGS=`pkg-config --libs libusb-1.0` +BINS=elftosb sbtoelf sbloader + +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 + $(LD) -o $@ $^ $(LDFLAGS) + +elftosb: elftosb.o crc.o crypto.o aes128.o sha1.o elf.o dbparser.o misc.o sb.o + $(LD) -o $@ $^ $(LDFLAGS) + +sbloader: sbloader.o + $(LD) -o $@ $^ $(LDFLAGS) + +clean: + rm -fr *.o + +veryclean: + rm -rf $(BINS) diff --git a/utils/imxtools/sbtools/README b/utils/imxtools/sbtools/README new file mode 100644 index 0000000000..8bf6fd5f8e --- /dev/null +++ b/utils/imxtools/sbtools/README @@ -0,0 +1,32 @@ +This file document the format of the command file used by the elftosb tool. +By no way our tools tries to be compatible with Freescale's elftosb2. +However, our format is more subset of the general one. + +The parse supports a limited form of comments: comments starting with // and ending at the end of the line. + +A file first contains the list of sources: + +sources +{ + hw_init = "sdram_init.elf"; + rockbox = "rockbox.elf"; +} + +It can then contain an arbitrary number of section. A section is identified by a number. +Within a section, three commands are supported: "load", "jump" and "call": + +section(0x626f6f74) // hex for 'boot' +{ + load hw_init; + call hw_init; + load rockbox; + jump rockbox; +} + +Finally, both elftosb and sbtoelf tools use key files. A key file is a list of keys. +Each key consist is 128-bit long and is written in hexadecimal: + +00000000000000000000000000000000 + +The parser does not handle blank line and only allows a final newline at the end of the file. +A file is allowed to contain zero (0) keys. diff --git a/utils/imxtools/sbtools/aes128.c b/utils/imxtools/sbtools/aes128.c new file mode 100644 index 0000000000..5870813db8 --- /dev/null +++ b/utils/imxtools/sbtools/aes128.c @@ -0,0 +1,284 @@ +// Simple, thoroughly commented implementation of 128-bit AES / Rijndael using C +// Chris Hulbert - chris.hulbert@gmail.com - http://splinter.com.au/blog +// References: +// http://en.wikipedia.org/wiki/Advanced_Encryption_Standard +// http://en.wikipedia.org/wiki/Rijndael_key_schedule +// http://en.wikipeia.org/wiki/Rijndael_mix_columns +// http://en.wikipedia.org/wiki/Rijndael_S-box +// This code is public domain, or any OSI-approved license, your choice. No warranty. +#include "crypto.h" + +// Here are all the lookup tables for the row shifts, rcon, s-boxes, and galois field multiplications +byte shift_rows_table[] = {0,5,10,15,4,9,14,3,8,13,2,7,12,1,6,11}; +byte shift_rows_table_inv[] = {0,13,10,7,4,1,14,11,8,5,2,15,12,9,6,3}; +byte lookup_rcon[]={0x8d,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a}; +byte lookup_sbox[]={0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16}; +byte lookup_sbox_inv[]={0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38,0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb,0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87,0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb,0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d,0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e,0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2,0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25,0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16,0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92,0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda,0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84,0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a,0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06,0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02,0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b,0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea,0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73,0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85,0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e,0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89,0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b,0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20,0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4,0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31,0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f,0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d,0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef,0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0,0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61,0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26,0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d}; +byte lookup_g2 []={0x00,0x02,0x04,0x06,0x08,0x0a,0x0c,0x0e,0x10,0x12,0x14,0x16,0x18,0x1a,0x1c,0x1e,0x20,0x22,0x24,0x26,0x28,0x2a,0x2c,0x2e,0x30,0x32,0x34,0x36,0x38,0x3a,0x3c,0x3e,0x40,0x42,0x44,0x46,0x48,0x4a,0x4c,0x4e,0x50,0x52,0x54,0x56,0x58,0x5a,0x5c,0x5e,0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6e,0x70,0x72,0x74,0x76,0x78,0x7a,0x7c,0x7e,0x80,0x82,0x84,0x86,0x88,0x8a,0x8c,0x8e,0x90,0x92,0x94,0x96,0x98,0x9a,0x9c,0x9e,0xa0,0xa2,0xa4,0xa6,0xa8,0xaa,0xac,0xae,0xb0,0xb2,0xb4,0xb6,0xb8,0xba,0xbc,0xbe,0xc0,0xc2,0xc4,0xc6,0xc8,0xca,0xcc,0xce,0xd0,0xd2,0xd4,0xd6,0xd8,0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xee,0xf0,0xf2,0xf4,0xf6,0xf8,0xfa,0xfc,0xfe,0x1b,0x19,0x1f,0x1d,0x13,0x11,0x17,0x15,0x0b,0x09,0x0f,0x0d,0x03,0x01,0x07,0x05,0x3b,0x39,0x3f,0x3d,0x33,0x31,0x37,0x35,0x2b,0x29,0x2f,0x2d,0x23,0x21,0x27,0x25,0x5b,0x59,0x5f,0x5d,0x53,0x51,0x57,0x55,0x4b,0x49,0x4f,0x4d,0x43,0x41,0x47,0x45,0x7b,0x79,0x7f,0x7d,0x73,0x71,0x77,0x75,0x6b,0x69,0x6f,0x6d,0x63,0x61,0x67,0x65,0x9b,0x99,0x9f,0x9d,0x93,0x91,0x97,0x95,0x8b,0x89,0x8f,0x8d,0x83,0x81,0x87,0x85,0xbb,0xb9,0xbf,0xbd,0xb3,0xb1,0xb7,0xb5,0xab,0xa9,0xaf,0xad,0xa3,0xa1,0xa7,0xa5,0xdb,0xd9,0xdf,0xdd,0xd3,0xd1,0xd7,0xd5,0xcb,0xc9,0xcf,0xcd,0xc3,0xc1,0xc7,0xc5,0xfb,0xf9,0xff,0xfd,0xf3,0xf1,0xf7,0xf5,0xeb,0xe9,0xef,0xed,0xe3,0xe1,0xe7,0xe5}; +byte lookup_g3 []={0x00,0x03,0x06,0x05,0x0c,0x0f,0x0a,0x09,0x18,0x1b,0x1e,0x1d,0x14,0x17,0x12,0x11,0x30,0x33,0x36,0x35,0x3c,0x3f,0x3a,0x39,0x28,0x2b,0x2e,0x2d,0x24,0x27,0x22,0x21,0x60,0x63,0x66,0x65,0x6c,0x6f,0x6a,0x69,0x78,0x7b,0x7e,0x7d,0x74,0x77,0x72,0x71,0x50,0x53,0x56,0x55,0x5c,0x5f,0x5a,0x59,0x48,0x4b,0x4e,0x4d,0x44,0x47,0x42,0x41,0xc0,0xc3,0xc6,0xc5,0xcc,0xcf,0xca,0xc9,0xd8,0xdb,0xde,0xdd,0xd4,0xd7,0xd2,0xd1,0xf0,0xf3,0xf6,0xf5,0xfc,0xff,0xfa,0xf9,0xe8,0xeb,0xee,0xed,0xe4,0xe7,0xe2,0xe1,0xa0,0xa3,0xa6,0xa5,0xac,0xaf,0xaa,0xa9,0xb8,0xbb,0xbe,0xbd,0xb4,0xb7,0xb2,0xb1,0x90,0x93,0x96,0x95,0x9c,0x9f,0x9a,0x99,0x88,0x8b,0x8e,0x8d,0x84,0x87,0x82,0x81,0x9b,0x98,0x9d,0x9e,0x97,0x94,0x91,0x92,0x83,0x80,0x85,0x86,0x8f,0x8c,0x89,0x8a,0xab,0xa8,0xad,0xae,0xa7,0xa4,0xa1,0xa2,0xb3,0xb0,0xb5,0xb6,0xbf,0xbc,0xb9,0xba,0xfb,0xf8,0xfd,0xfe,0xf7,0xf4,0xf1,0xf2,0xe3,0xe0,0xe5,0xe6,0xef,0xec,0xe9,0xea,0xcb,0xc8,0xcd,0xce,0xc7,0xc4,0xc1,0xc2,0xd3,0xd0,0xd5,0xd6,0xdf,0xdc,0xd9,0xda,0x5b,0x58,0x5d,0x5e,0x57,0x54,0x51,0x52,0x43,0x40,0x45,0x46,0x4f,0x4c,0x49,0x4a,0x6b,0x68,0x6d,0x6e,0x67,0x64,0x61,0x62,0x73,0x70,0x75,0x76,0x7f,0x7c,0x79,0x7a,0x3b,0x38,0x3d,0x3e,0x37,0x34,0x31,0x32,0x23,0x20,0x25,0x26,0x2f,0x2c,0x29,0x2a,0x0b,0x08,0x0d,0x0e,0x07,0x04,0x01,0x02,0x13,0x10,0x15,0x16,0x1f,0x1c,0x19,0x1a}; +byte lookup_g9 []={0x00,0x09,0x12,0x1b,0x24,0x2d,0x36,0x3f,0x48,0x41,0x5a,0x53,0x6c,0x65,0x7e,0x77,0x90,0x99,0x82,0x8b,0xb4,0xbd,0xa6,0xaf,0xd8,0xd1,0xca,0xc3,0xfc,0xf5,0xee,0xe7,0x3b,0x32,0x29,0x20,0x1f,0x16,0x0d,0x04,0x73,0x7a,0x61,0x68,0x57,0x5e,0x45,0x4c,0xab,0xa2,0xb9,0xb0,0x8f,0x86,0x9d,0x94,0xe3,0xea,0xf1,0xf8,0xc7,0xce,0xd5,0xdc,0x76,0x7f,0x64,0x6d,0x52,0x5b,0x40,0x49,0x3e,0x37,0x2c,0x25,0x1a,0x13,0x08,0x01,0xe6,0xef,0xf4,0xfd,0xc2,0xcb,0xd0,0xd9,0xae,0xa7,0xbc,0xb5,0x8a,0x83,0x98,0x91,0x4d,0x44,0x5f,0x56,0x69,0x60,0x7b,0x72,0x05,0x0c,0x17,0x1e,0x21,0x28,0x33,0x3a,0xdd,0xd4,0xcf,0xc6,0xf9,0xf0,0xeb,0xe2,0x95,0x9c,0x87,0x8e,0xb1,0xb8,0xa3,0xaa,0xec,0xe5,0xfe,0xf7,0xc8,0xc1,0xda,0xd3,0xa4,0xad,0xb6,0xbf,0x80,0x89,0x92,0x9b,0x7c,0x75,0x6e,0x67,0x58,0x51,0x4a,0x43,0x34,0x3d,0x26,0x2f,0x10,0x19,0x02,0x0b,0xd7,0xde,0xc5,0xcc,0xf3,0xfa,0xe1,0xe8,0x9f,0x96,0x8d,0x84,0xbb,0xb2,0xa9,0xa0,0x47,0x4e,0x55,0x5c,0x63,0x6a,0x71,0x78,0x0f,0x06,0x1d,0x14,0x2b,0x22,0x39,0x30,0x9a,0x93,0x88,0x81,0xbe,0xb7,0xac,0xa5,0xd2,0xdb,0xc0,0xc9,0xf6,0xff,0xe4,0xed,0x0a,0x03,0x18,0x11,0x2e,0x27,0x3c,0x35,0x42,0x4b,0x50,0x59,0x66,0x6f,0x74,0x7d,0xa1,0xa8,0xb3,0xba,0x85,0x8c,0x97,0x9e,0xe9,0xe0,0xfb,0xf2,0xcd,0xc4,0xdf,0xd6,0x31,0x38,0x23,0x2a,0x15,0x1c,0x07,0x0e,0x79,0x70,0x6b,0x62,0x5d,0x54,0x4f,0x46}; +byte lookup_g11 []={0x00,0x0b,0x16,0x1d,0x2c,0x27,0x3a,0x31,0x58,0x53,0x4e,0x45,0x74,0x7f,0x62,0x69,0xb0,0xbb,0xa6,0xad,0x9c,0x97,0x8a,0x81,0xe8,0xe3,0xfe,0xf5,0xc4,0xcf,0xd2,0xd9,0x7b,0x70,0x6d,0x66,0x57,0x5c,0x41,0x4a,0x23,0x28,0x35,0x3e,0x0f,0x04,0x19,0x12,0xcb,0xc0,0xdd,0xd6,0xe7,0xec,0xf1,0xfa,0x93,0x98,0x85,0x8e,0xbf,0xb4,0xa9,0xa2,0xf6,0xfd,0xe0,0xeb,0xda,0xd1,0xcc,0xc7,0xae,0xa5,0xb8,0xb3,0x82,0x89,0x94,0x9f,0x46,0x4d,0x50,0x5b,0x6a,0x61,0x7c,0x77,0x1e,0x15,0x08,0x03,0x32,0x39,0x24,0x2f,0x8d,0x86,0x9b,0x90,0xa1,0xaa,0xb7,0xbc,0xd5,0xde,0xc3,0xc8,0xf9,0xf2,0xef,0xe4,0x3d,0x36,0x2b,0x20,0x11,0x1a,0x07,0x0c,0x65,0x6e,0x73,0x78,0x49,0x42,0x5f,0x54,0xf7,0xfc,0xe1,0xea,0xdb,0xd0,0xcd,0xc6,0xaf,0xa4,0xb9,0xb2,0x83,0x88,0x95,0x9e,0x47,0x4c,0x51,0x5a,0x6b,0x60,0x7d,0x76,0x1f,0x14,0x09,0x02,0x33,0x38,0x25,0x2e,0x8c,0x87,0x9a,0x91,0xa0,0xab,0xb6,0xbd,0xd4,0xdf,0xc2,0xc9,0xf8,0xf3,0xee,0xe5,0x3c,0x37,0x2a,0x21,0x10,0x1b,0x06,0x0d,0x64,0x6f,0x72,0x79,0x48,0x43,0x5e,0x55,0x01,0x0a,0x17,0x1c,0x2d,0x26,0x3b,0x30,0x59,0x52,0x4f,0x44,0x75,0x7e,0x63,0x68,0xb1,0xba,0xa7,0xac,0x9d,0x96,0x8b,0x80,0xe9,0xe2,0xff,0xf4,0xc5,0xce,0xd3,0xd8,0x7a,0x71,0x6c,0x67,0x56,0x5d,0x40,0x4b,0x22,0x29,0x34,0x3f,0x0e,0x05,0x18,0x13,0xca,0xc1,0xdc,0xd7,0xe6,0xed,0xf0,0xfb,0x92,0x99,0x84,0x8f,0xbe,0xb5,0xa8,0xa3}; +byte lookup_g13 []={0x00,0x0d,0x1a,0x17,0x34,0x39,0x2e,0x23,0x68,0x65,0x72,0x7f,0x5c,0x51,0x46,0x4b,0xd0,0xdd,0xca,0xc7,0xe4,0xe9,0xfe,0xf3,0xb8,0xb5,0xa2,0xaf,0x8c,0x81,0x96,0x9b,0xbb,0xb6,0xa1,0xac,0x8f,0x82,0x95,0x98,0xd3,0xde,0xc9,0xc4,0xe7,0xea,0xfd,0xf0,0x6b,0x66,0x71,0x7c,0x5f,0x52,0x45,0x48,0x03,0x0e,0x19,0x14,0x37,0x3a,0x2d,0x20,0x6d,0x60,0x77,0x7a,0x59,0x54,0x43,0x4e,0x05,0x08,0x1f,0x12,0x31,0x3c,0x2b,0x26,0xbd,0xb0,0xa7,0xaa,0x89,0x84,0x93,0x9e,0xd5,0xd8,0xcf,0xc2,0xe1,0xec,0xfb,0xf6,0xd6,0xdb,0xcc,0xc1,0xe2,0xef,0xf8,0xf5,0xbe,0xb3,0xa4,0xa9,0x8a,0x87,0x90,0x9d,0x06,0x0b,0x1c,0x11,0x32,0x3f,0x28,0x25,0x6e,0x63,0x74,0x79,0x5a,0x57,0x40,0x4d,0xda,0xd7,0xc0,0xcd,0xee,0xe3,0xf4,0xf9,0xb2,0xbf,0xa8,0xa5,0x86,0x8b,0x9c,0x91,0x0a,0x07,0x10,0x1d,0x3e,0x33,0x24,0x29,0x62,0x6f,0x78,0x75,0x56,0x5b,0x4c,0x41,0x61,0x6c,0x7b,0x76,0x55,0x58,0x4f,0x42,0x09,0x04,0x13,0x1e,0x3d,0x30,0x27,0x2a,0xb1,0xbc,0xab,0xa6,0x85,0x88,0x9f,0x92,0xd9,0xd4,0xc3,0xce,0xed,0xe0,0xf7,0xfa,0xb7,0xba,0xad,0xa0,0x83,0x8e,0x99,0x94,0xdf,0xd2,0xc5,0xc8,0xeb,0xe6,0xf1,0xfc,0x67,0x6a,0x7d,0x70,0x53,0x5e,0x49,0x44,0x0f,0x02,0x15,0x18,0x3b,0x36,0x21,0x2c,0x0c,0x01,0x16,0x1b,0x38,0x35,0x22,0x2f,0x64,0x69,0x7e,0x73,0x50,0x5d,0x4a,0x47,0xdc,0xd1,0xc6,0xcb,0xe8,0xe5,0xf2,0xff,0xb4,0xb9,0xae,0xa3,0x80,0x8d,0x9a,0x97}; +byte lookup_g14 []={0x00,0x0e,0x1c,0x12,0x38,0x36,0x24,0x2a,0x70,0x7e,0x6c,0x62,0x48,0x46,0x54,0x5a,0xe0,0xee,0xfc,0xf2,0xd8,0xd6,0xc4,0xca,0x90,0x9e,0x8c,0x82,0xa8,0xa6,0xb4,0xba,0xdb,0xd5,0xc7,0xc9,0xe3,0xed,0xff,0xf1,0xab,0xa5,0xb7,0xb9,0x93,0x9d,0x8f,0x81,0x3b,0x35,0x27,0x29,0x03,0x0d,0x1f,0x11,0x4b,0x45,0x57,0x59,0x73,0x7d,0x6f,0x61,0xad,0xa3,0xb1,0xbf,0x95,0x9b,0x89,0x87,0xdd,0xd3,0xc1,0xcf,0xe5,0xeb,0xf9,0xf7,0x4d,0x43,0x51,0x5f,0x75,0x7b,0x69,0x67,0x3d,0x33,0x21,0x2f,0x05,0x0b,0x19,0x17,0x76,0x78,0x6a,0x64,0x4e,0x40,0x52,0x5c,0x06,0x08,0x1a,0x14,0x3e,0x30,0x22,0x2c,0x96,0x98,0x8a,0x84,0xae,0xa0,0xb2,0xbc,0xe6,0xe8,0xfa,0xf4,0xde,0xd0,0xc2,0xcc,0x41,0x4f,0x5d,0x53,0x79,0x77,0x65,0x6b,0x31,0x3f,0x2d,0x23,0x09,0x07,0x15,0x1b,0xa1,0xaf,0xbd,0xb3,0x99,0x97,0x85,0x8b,0xd1,0xdf,0xcd,0xc3,0xe9,0xe7,0xf5,0xfb,0x9a,0x94,0x86,0x88,0xa2,0xac,0xbe,0xb0,0xea,0xe4,0xf6,0xf8,0xd2,0xdc,0xce,0xc0,0x7a,0x74,0x66,0x68,0x42,0x4c,0x5e,0x50,0x0a,0x04,0x16,0x18,0x32,0x3c,0x2e,0x20,0xec,0xe2,0xf0,0xfe,0xd4,0xda,0xc8,0xc6,0x9c,0x92,0x80,0x8e,0xa4,0xaa,0xb8,0xb6,0x0c,0x02,0x10,0x1e,0x34,0x3a,0x28,0x26,0x7c,0x72,0x60,0x6e,0x44,0x4a,0x58,0x56,0x37,0x39,0x2b,0x25,0x0f,0x01,0x13,0x1d,0x47,0x49,0x5b,0x55,0x7f,0x71,0x63,0x6d,0xd7,0xd9,0xcb,0xc5,0xef,0xe1,0xf3,0xfd,0xa7,0xa9,0xbb,0xb5,0x9f,0x91,0x83,0x8d}; + +// Xor's all elements in a n byte array a by b +void xor_(byte *a, byte *b, int n) { + int i; + for (i=0;i ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 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 "crypto.h" + +/* Table extracted from firmware, don't know if this is regular CRC32 */ + +static uint32_t crc_table[256] = { + 0x0, 0x4C11DB7, 0x9823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B, 0x1A864DB2, + 0x1E475005, 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61, 0x350C9B64, + 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD, 0x4C11DB70, 0x48D0C6C7, 0x4593E01E, + 0x4152FDA9, 0x5F15ADAC, 0x5BD4B01B, 0x569796C2, 0x52568B75, 0x6A1936C8, + 0x6ED82B7F, 0x639B0DA6, 0x675A1011, 0x791D4014, 0x7DDC5DA3, 0x709F7B7A, + 0x745E66CD, 0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039, 0x8B27C03C, + 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, 0x0BE2B5B58, 0x0BAEA46EF, 0x0B7A96036, + 0x0B3687D81, 0x0AD2F2D84, 0x0A9EE3033, 0x0A4AD16EA, 0x0A06C0B5D, 0x0D4326D90, + 0x0D0F37027, 0x0DDB056FE, 0x0D9714B49, 0x0C7361B4C, 0x0C3F706FB, 0x0CEB42022, + 0x0CA753D95, 0x0F23A8028, 0x0F6FB9D9F, 0x0FBB8BB46, 0x0FF79A6F1, 0x0E13EF6F4, + 0x0E5FFEB43, 0x0E8BCCD9A, 0x0EC7DD02D, 0x34867077, 0x30476DC0, 0x3D044B19, + 0x39C556AE, 0x278206AB, 0x23431B1C, 0x2E003DC5, 0x2AC12072, 0x128E9DCF, + 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16, 0x18AEB13, 0x54BF6A4, 0x808D07D, + 0x0CC9CDCA, 0x7897AB07, 0x7C56B6B0, 0x71159069, 0x75D48DDE, 0x6B93DDDB, + 0x6F52C06C, 0x6211E6B5, 0x66D0FB02, 0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1, + 0x53DC6066, 0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA, 0x0ACA5C697, + 0x0A864DB20, 0x0A527FDF9, 0x0A1E6E04E, 0x0BFA1B04B, 0x0BB60ADFC, 0x0B6238B25, + 0x0B2E29692, 0x8AAD2B2F, 0x8E6C3698, 0x832F1041, 0x87EE0DF6, 0x99A95DF3, + 0x9D684044, 0x902B669D, 0x94EA7B2A, 0x0E0B41DE7, 0x0E4750050, 0x0E9362689, + 0x0EDF73B3E, 0x0F3B06B3B, 0x0F771768C, 0x0FA325055, 0x0FEF34DE2, 0x0C6BCF05F, + 0x0C27DEDE8, 0x0CF3ECB31, 0x0CBFFD686, 0x0D5B88683, 0x0D1799B34, 0x0DC3ABDED, + 0x0D8FBA05A, 0x690CE0EE, 0x6DCDFD59, 0x608EDB80, 0x644FC637, 0x7A089632, + 0x7EC98B85, 0x738AAD5C, 0x774BB0EB, 0x4F040D56, 0x4BC510E1, 0x46863638, + 0x42472B8F, 0x5C007B8A, 0x58C1663D, 0x558240E4, 0x51435D53, 0x251D3B9E, + 0x21DC2629, 0x2C9F00F0, 0x285E1D47, 0x36194D42, 0x32D850F5, 0x3F9B762C, + 0x3B5A6B9B, 0x315D626, 0x7D4CB91, 0x0A97ED48, 0x0E56F0FF, 0x1011A0FA, + 0x14D0BD4D, 0x19939B94, 0x1D528623, 0x0F12F560E, 0x0F5EE4BB9, 0x0F8AD6D60, + 0x0FC6C70D7, 0x0E22B20D2, 0x0E6EA3D65, 0x0EBA91BBC, 0x0EF68060B, 0x0D727BBB6, + 0x0D3E6A601, 0x0DEA580D8, 0x0DA649D6F, 0x0C423CD6A, 0x0C0E2D0DD, 0x0CDA1F604, + 0x0C960EBB3, 0x0BD3E8D7E, 0x0B9FF90C9, 0x0B4BCB610, 0x0B07DABA7, 0x0AE3AFBA2, + 0x0AAFBE615, 0x0A7B8C0CC, 0x0A379DD7B, 0x9B3660C6, 0x9FF77D71, 0x92B45BA8, + 0x9675461F, 0x8832161A, 0x8CF30BAD, 0x81B02D74, 0x857130C3, 0x5D8A9099, + 0x594B8D2E, 0x5408ABF7, 0x50C9B640, 0x4E8EE645, 0x4A4FFBF2, 0x470CDD2B, + 0x43CDC09C, 0x7B827D21, 0x7F436096, 0x7200464F, 0x76C15BF8, 0x68860BFD, + 0x6C47164A, 0x61043093, 0x65C52D24, 0x119B4BE9, 0x155A565E, 0x18197087, + 0x1CD86D30, 0x29F3D35, 0x65E2082, 0x0B1D065B, 0x0FDC1BEC, 0x3793A651, + 0x3352BBE6, 0x3E119D3F, 0x3AD08088, 0x2497D08D, 0x2056CD3A, 0x2D15EBE3, + 0x29D4F654, 0x0C5A92679, 0x0C1683BCE, 0x0CC2B1D17, 0x0C8EA00A0, 0x0D6AD50A5, + 0x0D26C4D12, 0x0DF2F6BCB, 0x0DBEE767C, 0x0E3A1CBC1, 0x0E760D676, 0x0EA23F0AF, + 0x0EEE2ED18, 0x0F0A5BD1D, 0x0F464A0AA, 0x0F9278673, 0x0FDE69BC4, 0x89B8FD09, + 0x8D79E0BE, 0x803AC667, 0x84FBDBD0, 0x9ABC8BD5, 0x9E7D9662, 0x933EB0BB, + 0x97FFAD0C, 0x0AFB010B1, 0x0AB710D06, 0x0A6322BDF, 0x0A2F33668, 0x0BCB4666D, + 0x0B8757BDA, 0x0B5365D03, 0x0B1F740B4 +}; + +uint32_t crc(byte *data, int size) +{ + return crc_continue(0xffffffff, data, size); +} + +uint32_t crc_continue(uint32_t previous_crc, byte *data, int size) +{ + uint32_t c = previous_crc; + /* normal CRC */ + for(int i = 0; i < size; i++) + c = crc_table[data[i] ^ (c >> 24)] ^ (c << 8); + return c; +} diff --git a/utils/imxtools/sbtools/crypto.c b/utils/imxtools/sbtools/crypto.c new file mode 100644 index 0000000000..d4afc6c816 --- /dev/null +++ b/utils/imxtools/sbtools/crypto.c @@ -0,0 +1,188 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 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 "crypto.h" +#include +#include +#ifdef CRYPTO_LIBUSB +#include "libusb.h" +#endif +#include "misc.h" + +static enum crypto_method_t cur_method = CRYPTO_NONE; +static byte key[16]; +static uint16_t usb_vid, usb_pid; + +void crypto_setup(enum crypto_method_t method, void *param) +{ + cur_method = method; + switch(method) + { + case CRYPTO_KEY: + memcpy(key, param, sizeof(key)); + break; + case CRYPTO_USBOTP: + { + uint32_t value = *(uint32_t *)param; + usb_vid = value >> 16; + usb_pid = value & 0xffff; + break; + } + default: + break; + } +} + +int crypto_apply( + byte *in_data, /* Input data */ + byte *out_data, /* Output data (or NULL) */ + int nr_blocks, /* Number of blocks (one block=16 bytes) */ + byte iv[16], /* Key */ + byte (*out_cbc_mac)[16], /* CBC-MAC of the result (or NULL) */ + int encrypt) +{ + if(cur_method == CRYPTO_KEY) + { + cbc_mac(in_data, out_data, nr_blocks, key, iv, out_cbc_mac, encrypt); + return CRYPTO_ERROR_SUCCESS; + } + #ifdef CRYPTO_LIBUSB + else if(cur_method == CRYPTO_USBOTP) + { + if(out_cbc_mac && !encrypt) + memcpy(*out_cbc_mac, in_data + 16 * (nr_blocks - 1), 16); + + libusb_device_handle *handle = NULL; + libusb_context *ctx; + /* init library */ + libusb_init(&ctx); + libusb_set_debug(NULL,3); + /* open device */ + handle = libusb_open_device_with_vid_pid(ctx, usb_vid, usb_pid); + if(handle == NULL) + { + printf("usbotp: cannot open device %04x:%04x\n", usb_vid, usb_pid); + return CRYPTO_ERROR_NODEVICE; + } + /* get device pointer */ + libusb_device *mydev = libusb_get_device(handle); + if(g_debug) + printf("usbotp: device found at %d:%d\n", libusb_get_bus_number(mydev), + libusb_get_device_address(mydev)); + int config_id; + /* explore configuration */ + libusb_get_configuration(handle, &config_id); + struct libusb_config_descriptor *config; + libusb_get_active_config_descriptor(mydev, &config); + + if(g_debug) + { + printf("usbotp: configuration: %d\n", config_id); + printf("usbotp: interfaces: %d\n", config->bNumInterfaces); + } + + const struct libusb_endpoint_descriptor *endp = NULL; + int intf, intf_alt; + for(intf = 0; intf < config->bNumInterfaces; intf++) + for(intf_alt = 0; intf_alt < config->interface[intf].num_altsetting; intf_alt++) + for(int ep = 0; ep < config->interface[intf].altsetting[intf_alt].bNumEndpoints; ep++) + { + endp = &config->interface[intf].altsetting[intf_alt].endpoint[ep]; + if((endp->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_INTERRUPT && + (endp->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) + goto Lfound; + } + libusb_close(handle); + printf("usbotp: No suitable endpoint found\n"); + return CRYPTO_ERROR_BADENDP; + + if(g_debug) + { + printf("usbotp: use interface %d, alt %d\n", intf, intf_alt); + printf("usbotp: use endpoint %d\n", endp->bEndpointAddress); + } + Lfound: + if(libusb_claim_interface(handle, intf) != 0) + { + if(g_debug) + printf("usbotp: claim error\n"); + return CRYPTO_ERROR_CLAIMFAIL; + } + + int buffer_size = 16 + 16 * nr_blocks; + unsigned char *buffer = xmalloc(buffer_size); + memcpy(buffer, iv, 16); + memcpy(buffer + 16, in_data, 16 * nr_blocks); + int ret = libusb_control_transfer(handle, + LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE, + 0xaa, encrypt ? 0xeeee : 0xdddd, 0, buffer, buffer_size, 1000); + if(ret < 0) + { + if(g_debug) + printf("usbotp: control transfer failed: %d\n", ret); + libusb_release_interface(handle, intf); + libusb_close(handle); + return CRYPTO_ERROR_DEVREJECT; + } + + int recv_size; + ret = libusb_interrupt_transfer(handle, endp->bEndpointAddress, buffer, + buffer_size, &recv_size, 1000); + libusb_release_interface(handle, intf); + libusb_close(handle); + + if(ret < 0) + { + if(g_debug) + printf("usbotp: interrupt transfer failed: %d\n", ret); + return CRYPTO_ERROR_DEVSILENT; + } + if(recv_size != buffer_size) + { + if(g_debug) + printf("usbotp: device returned %d bytes, expected %d\n", recv_size, + buffer_size); + return CRYPTO_ERROR_DEVERR; + } + + if(out_data) + memcpy(out_data, buffer + 16, 16 * nr_blocks); + if(out_cbc_mac && encrypt) + memcpy(*out_cbc_mac, buffer + buffer_size - 16, 16); + + return CRYPTO_ERROR_SUCCESS; + } + #endif + else + return CRYPTO_ERROR_BADSETUP; +} + +int crypto_cbc( + byte *in_data, /* Input data */ + byte *out_data, /* Output data (or NULL) */ + int nr_blocks, /* Number of blocks (one block=16 bytes) */ + struct crypto_key_t *key, /* Key */ + byte iv[16], /* IV */ + byte (*out_cbc_mac)[16], /* CBC-MAC of the result (or NULL) */ + int encrypt) +{ + crypto_setup(key->method, (void *)key->u.param); + return crypto_apply(in_data, out_data, nr_blocks, iv, out_cbc_mac, encrypt); +} diff --git a/utils/imxtools/sbtools/crypto.h b/utils/imxtools/sbtools/crypto.h new file mode 100644 index 0000000000..452db6a28d --- /dev/null +++ b/utils/imxtools/sbtools/crypto.h @@ -0,0 +1,115 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 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 __CRYPTO_H__ +#define __CRYPTO_H__ + +#include +#include +#include + +typedef uint8_t byte; + +/* aes128.c */ +void xor_(byte *a, byte *b, int n); +void EncryptAES(byte *msg, byte *key, byte *c); +void DecryptAES(byte *c, byte *key, byte *m); +void Pretty(byte* b,int len,const char* label); +void cbc_mac( + byte *in_data, /* Input data */ + byte *out_data, /* Output data (or NULL) */ + int nr_blocks, /* Number of blocks to encrypt/decrypt (one block=16 bytes) */ + byte key[16], /* Key */ + byte iv[16], /* Initialisation Vector */ + byte (*out_cbc_mac)[16], /* CBC-MAC of the result (or NULL) */ + int encrypt /* 1 to encrypt, 0 to decrypt */ + ); + +/* crypto.c */ +enum crypto_method_t +{ + CRYPTO_NONE, /* disable */ + CRYPTO_KEY, /* key */ + CRYPTO_USBOTP, /* use usbotp device */ +}; + +/* parameter can be: + * - CRYPTO_KEY: array of 16-bytes (the key) + * - CRYPTO_USBOTP: 32-bit integer: vid << 16 | pid */ +void crypto_setup(enum crypto_method_t method, void *param); + +#define CRYPTO_ERROR_SUCCESS 0 +#define CRYPTO_ERROR_BADSETUP -1 /* bad crypto setup */ +#define CRYPTO_ERROR_NODEVICE -2 /* no device with vid:pid */ +#define CRYPTO_ERROR_BADENDP -3 /* device doesn't have the required endpoints */ +#define CRYPTO_ERROR_CLAIMFAIL -4 /* device interface claim error */ +#define CRYPTO_ERROR_DEVREJECT -5 /* device rejected cypto operation */ +#define CRYPTO_ERROR_DEVSILENT -6 /* device did not notify completion */ +#define CRYPTO_ERROR_DEVERR -7 /* device did something wrong (like return too small buffer) */ +#define CRYPTO_NUM_ERRORS 8 +/* return 0 on success, <0 on error */ +int crypto_apply( + byte *in_data, /* Input data */ + byte *out_data, /* Output data (or NULL) */ + int nr_blocks, /* Number of blocks (one block=16 bytes) */ + byte iv[16], /* IV */ + byte (*out_cbc_mac)[16], /* CBC-MAC of the result (or NULL) */ + int encrypt); + +/* all-in-one function */ +struct crypto_key_t +{ + enum crypto_method_t method; + union + { + byte key[16]; + uint32_t vid_pid; + byte param[0]; + }u; +}; + +int crypto_cbc( + byte *in_data, /* Input data */ + byte *out_data, /* Output data (or NULL) */ + int nr_blocks, /* Number of blocks (one block=16 bytes) */ + struct crypto_key_t *key, /* Key */ + byte iv[16], /* IV */ + byte (*out_cbc_mac)[16], /* CBC-MAC of the result (or NULL) */ + int encrypt); + +/* crc.c */ +uint32_t crc(byte *data, int size); +uint32_t crc_continue(uint32_t previous_crc, byte *data, int size); + +/* sha1.c */ +struct sha_1_params_t +{ + uint32_t hash[5]; + uint64_t buffer_nr_bits; + uint32_t w[80]; +}; + +void sha_1_init(struct sha_1_params_t *params); +void sha_1_block(struct sha_1_params_t *params, uint32_t cur_hash[5], byte *data); +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); + +#endif /* __CRYPTO_H__ */ diff --git a/utils/imxtools/sbtools/dbparser.c b/utils/imxtools/sbtools/dbparser.c new file mode 100644 index 0000000000..89a63b3767 --- /dev/null +++ b/utils/imxtools/sbtools/dbparser.c @@ -0,0 +1,857 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2011 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. + * + ****************************************************************************/ + +#define _POSIX_C_SOURCE 200809L /* for strdup */ +#include +#include +#include +#include +#include "dbparser.h" +#include "misc.h" + +enum lexem_type_t +{ + LEX_IDENTIFIER, + LEX_LPAREN, + LEX_RPAREN, + LEX_NUMBER, + LEX_STRING, /* double-quoted string */ + LEX_EQUAL, + LEX_SEMICOLON, + LEX_LBRACE, + LEX_RBRACE, + LEX_RANGLE, + LEX_OR, + LEX_LSHIFT, + LEX_COLON, + LEX_LE, + LEX_EOF +}; + +struct lexem_t +{ + enum lexem_type_t type; + /* if str is not NULL, it must be a malloc'd pointer */ + char *str; + uint32_t num; + int line; + const char *file; +}; + +struct context_t +{ + const char *file; + char *begin; + char *end; + char *ptr; + int line; +}; + +#define parse_error(ctx, ...) \ + do { fprintf(stderr, "%s:%d: ", ctx->file, ctx->line); \ + fprintf(stderr, __VA_ARGS__); exit(2); } while(0) + +static void advance(struct context_t *ctx, int nr_chars) +{ + while(nr_chars--) + { + if(*(ctx->ptr++) == '\n') + ctx->line++; + } +} + +static inline bool eof(struct context_t *ctx) +{ + return ctx->ptr == ctx->end; +} + +static inline bool next_valid(struct context_t *ctx, int nr) +{ + return ctx->ptr + nr < ctx->end; +} + +static inline char cur_char(struct context_t *ctx) +{ + return *ctx->ptr; +} + +static inline char next_char(struct context_t *ctx, int nr) +{ + return ctx->ptr[nr]; +} + +static inline void locate_lexem(struct lexem_t *lex, struct context_t *ctx) +{ + lex->file = ctx->file; + lex->line = ctx->line; +} + +static void __parse_string(struct context_t *ctx, void *user, void (*emit_fn)(void *user, char c)) +{ + while(!eof(ctx)) + { + if(cur_char(ctx) == '"') + break; + else if(cur_char(ctx) == '\\') + { + advance(ctx, 1); + if(eof(ctx)) + parse_error(ctx, "Unfinished string\n"); + if(cur_char(ctx) == '\\') emit_fn(user, '\\'); + else if(cur_char(ctx) == '\'') emit_fn(user, '\''); + else if(cur_char(ctx) == '\"') emit_fn(user, '\"'); + else parse_error(ctx, "Unknown escape sequence \\%c\n", cur_char(ctx)); + advance(ctx, 1); + } + else + { + emit_fn(user, cur_char(ctx)); + advance(ctx, 1); + } + } + if(eof(ctx) || cur_char(ctx) != '"') + parse_error(ctx, "Unfinished string\n"); + advance(ctx, 1); +} + +static void __parse_string_emit(void *user, char c) +{ + char **pstr = (char **)user; + *(*pstr)++ = c; +} + +static void __parse_string_count(void *user, char c) +{ + (void) c; + (*(int *)user)++; +} + +static void parse_string(struct context_t *ctx, struct lexem_t *lexem) +{ + locate_lexem(lexem, ctx); + /* skip " */ + advance(ctx, 1); + /* compute length */ + struct context_t cpy_ctx = *ctx; + int length = 0; + __parse_string(&cpy_ctx, (void *)&length, __parse_string_count); + /* parse again */ + lexem->type = LEX_STRING; + lexem->str = xmalloc(length + 1); + lexem->str[length] = 0; + char *pstr = lexem->str; + __parse_string(ctx, (void *)&pstr, __parse_string_emit); +} + +static void parse_ascii_number(struct context_t *ctx, struct lexem_t *lexem) +{ + locate_lexem(lexem, ctx); + /* skip ' */ + advance(ctx, 1); + /* we expect n<=4 character and then ' */ + int len = 0; + uint32_t value = 0; + while(!eof(ctx)) + { + if(cur_char(ctx) != '\'') + { + value = value << 8 | cur_char(ctx); + len++; + advance(ctx, 1); + } + else + break; + } + if(eof(ctx) || cur_char(ctx) != '\'') + parse_error(ctx, "Unterminated ascii number literal\n"); + if(len == 0 || len > 4) + parse_error(ctx, "Invalid ascii number literal length: only 1 to 4 characters allowed\n"); + /* skip ' */ + advance(ctx, 1); + lexem->type = LEX_NUMBER; + lexem->num = value; +} + +static void parse_number(struct context_t *ctx, struct lexem_t *lexem) +{ + locate_lexem(lexem, ctx); + /* check base */ + int base = 10; + if(cur_char(ctx) == '0' && next_valid(ctx, 1) && next_char(ctx, 1) == 'x') + { + advance(ctx, 2); + base = 16; + } + + lexem->type = LEX_NUMBER; + lexem->num = 0; + while(!eof(ctx) && isxdigit(cur_char(ctx))) + { + if(base == 10 && !isdigit(cur_char(ctx))) + break; + byte v; + if(convxdigit(cur_char(ctx), &v)) + break; + lexem->num = base * lexem->num + v; + advance(ctx, 1); + } +} + +static void parse_identifier(struct context_t *ctx, struct lexem_t *lexem) +{ + locate_lexem(lexem, ctx); + /* remember position */ + char *old = ctx->ptr; + while(!eof(ctx) && (isalnum(cur_char(ctx)) || cur_char(ctx) == '_')) + advance(ctx, 1); + lexem->type = LEX_IDENTIFIER; + int len = ctx->ptr - old; + lexem->str = xmalloc(len + 1); + lexem->str[len] = 0; + memcpy(lexem->str, old, len); +} + +static void next_lexem(struct context_t *ctx, struct lexem_t *lexem) +{ + #define ret_simple(t, adv) \ + do {locate_lexem(lexem, ctx); \ + lexem->type = t; \ + advance(ctx, adv); \ + return;} while(0) + while(!eof(ctx)) + { + char c = cur_char(ctx); + /* skip whitespace */ + if(c == ' ' || c == '\t' || c == '\n' || c == '\r') + { + advance(ctx, 1); + continue; + } + /* skip C++ style comments */ + if(c == '/' && next_valid(ctx, 1) && next_char(ctx, 1) == '/') + { + while(!eof(ctx) && cur_char(ctx) != '\n') + advance(ctx, 1); + continue; + } + /* skip C-style comments */ + if(c == '/' && next_valid(ctx, 1) && next_char(ctx, 1) == '*') + { + advance(ctx, 2); + while(true) + { + if(!next_valid(ctx, 1)) + parse_error(ctx, "Unterminated comment"); + if(cur_char(ctx) == '*' && next_char(ctx, 1) == '/') + { + advance(ctx, 2); + break; + } + advance(ctx, 1); + } + continue; + } + break; + } + if(eof(ctx)) ret_simple(LEX_EOF, 0); + char c = cur_char(ctx); + bool nv = next_valid(ctx, 1); + char nc = nv ? next_char(ctx, 1) : 0; + if(c == '(') ret_simple(LEX_LPAREN, 1); + if(c == ')') ret_simple(LEX_RPAREN, 1); + if(c == '{') ret_simple(LEX_LBRACE, 1); + if(c == '}') ret_simple(LEX_RBRACE, 1); + if(c == '>') ret_simple(LEX_RANGLE, 1); + if(c == '=') ret_simple(LEX_EQUAL, 1); + if(c == ';') ret_simple(LEX_SEMICOLON, 1); + if(c == ',') ret_simple(LEX_COLON, 1); + if(c == '|') ret_simple(LEX_OR, 1); + if(c == '<' && nv && nc == '<') ret_simple(LEX_LSHIFT, 2); + if(c == '<' && nv && nc == '=') ret_simple(LEX_LE, 2); + if(c == '"') return parse_string(ctx, lexem); + if(c == '\'') return parse_ascii_number(ctx, lexem); + if(isdigit(c)) return parse_number(ctx, lexem); + if(isalpha(c) || c == '_') return parse_identifier(ctx, lexem); + parse_error(ctx, "Unexpected character '%c'\n", c); + #undef ret_simple +} + +#if 0 +static void log_lexem(struct lexem_t *lexem) +{ + switch(lexem->type) + { + case LEX_EOF: printf(""); break; + case LEX_EQUAL: printf("="); break; + case LEX_IDENTIFIER: printf("id(%s)", lexem->str); break; + case LEX_LPAREN: printf("("); break; + case LEX_RPAREN: printf(")"); break; + case LEX_LBRACE: printf("{"); break; + case LEX_RBRACE: printf("}"); break; + case LEX_SEMICOLON: printf(";"); break; + case LEX_NUMBER: printf("num(%d)", lexem->num); break; + case LEX_STRING: printf("str(%s)", lexem->str); break; + case LEX_OR: printf("|"); break; + case LEX_LSHIFT: printf("<<"); break; + default: printf(""); + } +} +#endif + +struct cmd_source_t *db_find_source_by_id(struct cmd_file_t *cmd_file, const char *id) +{ + struct cmd_source_t *src = cmd_file->source_list; + while(src) + { + if(strcmp(src->identifier, id) == 0) + return src; + src = src->next; + } + return NULL; +} + +struct cmd_option_t *db_find_option_by_id(struct cmd_option_t *opt, const char *name) +{ + while(opt) + { + if(strcmp(opt->name, name) == 0) + return opt; + opt = opt->next; + } + return NULL; +} + +#define INVALID_SB_SUBVERSION 0xffff + +static uint16_t parse_sb_subversion(char *str) +{ + int len = strlen(str); + uint16_t n = 0; + if(len == 0 || len > 4) + return INVALID_SB_SUBVERSION; + for(int i = 0; i < len; i++) + { + if(!isdigit(str[i])) + return INVALID_SB_SUBVERSION; + n = n << 4 | (str[i] - '0'); + } + return n; +} + +bool db_parse_sb_version(struct sb_version_t *ver, char *str) +{ + int len = strlen(str); + int cnt = 0; + int pos[2]; + + for(int i = 0; i < len; i++) + { + if(str[i] != '.') + continue; + if(cnt == 2) + return false; + pos[cnt++] = i + 1; + str[i] = 0; + } + if(cnt != 2) + return false; + ver->major = parse_sb_subversion(str); + ver->minor = parse_sb_subversion(str + pos[0]); + ver->revision = parse_sb_subversion(str + pos[1]); + return ver->major != INVALID_SB_SUBVERSION && + ver->minor != INVALID_SB_SUBVERSION && + ver->revision != INVALID_SB_SUBVERSION; +} + +#undef parse_error +#define parse_error(lexem, ...) \ + do { fprintf(stderr, "%s:%d: ", lexem.file, lexem.line); \ + fprintf(stderr, __VA_ARGS__); exit(2); } while(0) + +struct lex_ctx_t +{ + struct context_t ctx; + struct lexem_t lexem; +}; + +/* When lexems hold strings (like identifier), it might be useful to steal + * the pointer and don't clean the lexem but in other case, one don't want + * to keep the pointer to the string and just want to release the memory. + * Thus clean_lexem should be true except when one keeps a pointer */ +static inline void next(struct lex_ctx_t *ctx, bool clean_lexem) +{ + if(clean_lexem) + free(ctx->lexem.str); + memset(&ctx->lexem, 0, sizeof(struct lexem_t)); + next_lexem(&ctx->ctx, &ctx->lexem); +} + +static uint32_t parse_term_expr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list) +{ + uint32_t ret = 0; + if(ctx->lexem.type == LEX_NUMBER) + ret = ctx->lexem.num; + else if(ctx->lexem.type == LEX_IDENTIFIER) + { + struct cmd_option_t *c = db_find_option_by_id(const_list, ctx->lexem.str); + if(c == NULL) + parse_error(ctx->lexem, "Undefined reference to constant '%s'\n", ctx->lexem.str); + if(c->is_string) + parse_error(ctx->lexem, "Internal error: constant '%s' is not an integer\n", ctx->lexem.str); + ret = c->val; + } + else + parse_error(ctx->lexem, "Number or constant identifier expected\n"); + next(ctx, true); + return ret; +} + +static uint32_t parse_shift_expr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list) +{ + uint32_t v = parse_term_expr(ctx, const_list); + while(ctx->lexem.type == LEX_LSHIFT) + { + next(ctx, true); + v <<= parse_term_expr(ctx, const_list); + } + return v; +} + +static uint32_t parse_or_expr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list) +{ + uint32_t v = parse_shift_expr(ctx, const_list); + while(ctx->lexem.type == LEX_OR) + { + next(ctx, true); + v |= parse_shift_expr(ctx, const_list); + } + return v; +} + +static uint32_t parse_intexpr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list) +{ + return parse_or_expr(ctx, const_list); +} + +#define NR_INITIAL_CONSTANTS 4 +static char *init_const_name[NR_INITIAL_CONSTANTS] = {"true", "false", "yes", "no"}; +static uint32_t init_const_value[NR_INITIAL_CONSTANTS] = {1, 0, 1, 0}; + +struct cmd_file_t *db_parse_file(const char *file) +{ + size_t size; + FILE *f = fopen(file, "r"); + if(f == NULL) + { + if(g_debug) + perror("Cannot open db file"); + return NULL; + } + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, 0, SEEK_SET); + char *buf = xmalloc(size); + if(fread(buf, size, 1, f) != 1) + { + if(g_debug) + perror("Cannot read db file"); + return NULL; + } + fclose(f); + + if(g_debug) + printf("Parsing db file '%s'\n", file); + struct cmd_file_t *cmd_file = xmalloc(sizeof(struct cmd_file_t)); + memset(cmd_file, 0, sizeof(struct cmd_file_t)); + + /* add initial constants */ + for(int i = 0; i < NR_INITIAL_CONSTANTS; i++) + { + struct cmd_option_t *opt = xmalloc(sizeof(struct cmd_option_t)); + memset(opt, 0, sizeof(struct cmd_option_t)); + opt->name = strdup(init_const_name[i]); + opt->is_string = false; + opt->val = init_const_value[i]; + opt->next = cmd_file->constant_list; + cmd_file->constant_list = opt; + } + + struct lex_ctx_t lctx; + lctx.ctx.file = file; + lctx.ctx.line = 1; + lctx.ctx.begin = buf; + lctx.ctx.ptr = buf; + lctx.ctx.end = buf + size; + #define next(clean_lexem) next(&lctx, clean_lexem) + #define lexem lctx.lexem + /* init lexer */ + next(false); /* don't clean init lexem because it doesn't exist */ + /* constants ? */ + if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "constants")) + { + next(true); + if(lexem.type != LEX_LBRACE) + parse_error(lexem, "'{' expected after 'constants'\n"); + + while(true) + { + struct cmd_option_t *opt = xmalloc(sizeof(struct cmd_option_t)); + memset(opt, 0, sizeof(struct cmd_option_t)); + next(true); + if(lexem.type == LEX_RBRACE) + break; + if(lexem.type != LEX_IDENTIFIER) + parse_error(lexem, "Identifier expected in constants\n"); + opt->name = lexem.str; + next(false); /* lexem string is kept as option name */ + if(lexem.type != LEX_EQUAL) + parse_error(lexem, "'=' expected after identifier\n"); + next(true); + opt->is_string = false; + opt->val = parse_intexpr(&lctx, cmd_file->constant_list); + opt->next = cmd_file->constant_list; + cmd_file->constant_list = opt; + if(lexem.type != LEX_SEMICOLON) + parse_error(lexem, "';' expected after string\n"); + } + next(true); + } + /* options ? */ + if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "options")) + { + next(true); + if(lexem.type != LEX_LBRACE) + parse_error(lexem, "'{' expected after 'options'\n"); + + while(true) + { + next(true); + if(lexem.type == LEX_RBRACE) + break; + struct cmd_option_t *opt = xmalloc(sizeof(struct cmd_option_t)); + memset(opt, 0, sizeof(struct cmd_option_t)); + if(lexem.type != LEX_IDENTIFIER) + parse_error(lexem, "Identifier expected in options\n"); + opt->name = lexem.str; + next(false); /* lexem string is kept as option name */ + if(lexem.type != LEX_EQUAL) + parse_error(lexem, "'=' expected after identifier\n"); + next(true); + if(lexem.type == LEX_STRING) + { + opt->is_string = true; + opt->str = lexem.str; + next(false); /* lexem string is kept as option name */ + } + else + { + opt->is_string = false; + opt->val = parse_intexpr(&lctx, cmd_file->constant_list); + } + opt->next = cmd_file->opt_list; + cmd_file->opt_list = opt; + if(lexem.type != LEX_SEMICOLON) + parse_error(lexem, "';' expected after string\n"); + } + next(true); + } + /* sources */ + if(lexem.type != LEX_IDENTIFIER || strcmp(lexem.str, "sources")) + parse_error(lexem, "'sources' expected\n"); + next(true); + if(lexem.type != LEX_LBRACE) + parse_error(lexem, "'{' expected after 'sources'\n"); + + while(true) + { + next(true); + if(lexem.type == LEX_RBRACE) + break; + struct cmd_source_t *src = xmalloc(sizeof(struct cmd_source_t)); + memset(src, 0, sizeof(struct cmd_source_t)); + if(lexem.type != LEX_IDENTIFIER) + parse_error(lexem, "identifier expected in sources\n"); + src->identifier = lexem.str; + next(false); /* lexem string is kept as source name */ + if(lexem.type != LEX_EQUAL) + parse_error(lexem, "'=' expected after identifier\n"); + next(true); + if(lexem.type == LEX_STRING) + { + src->is_extern = false; + src->filename = lexem.str; + next(false); /* lexem string is kept as file name */ + } + else if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "extern")) + { + src->is_extern = true; + src->filename = strdup(""); /* duplicate because it will be free'd */ + next(true); + if(lexem.type != LEX_LPAREN) + parse_error(lexem, "'(' expected after 'extern'\n"); + next(true); + src->extern_nr = parse_intexpr(&lctx, cmd_file->constant_list); + if(lexem.type != LEX_RPAREN) + parse_error(lexem, "')' expected\n"); + next(true); + } + else + parse_error(lexem, "String or 'extern' expected after '='\n"); + if(lexem.type != LEX_SEMICOLON) + parse_error(lexem, "';' expected\n"); + if(db_find_source_by_id(cmd_file, src->identifier) != NULL) + parse_error(lexem, "Duplicate source identifier\n"); + /* type filled later */ + src->type = CMD_SRC_UNK; + src->next = cmd_file->source_list; + cmd_file->source_list = src; + } + + /* sections */ + struct cmd_section_t *end_sec = NULL; + while(true) + { + next(true); + if(lexem.type == LEX_EOF) + break; + struct cmd_section_t *sec = xmalloc(sizeof(struct cmd_section_t)); + struct cmd_inst_t *end_list = NULL; + memset(sec, 0, sizeof(struct cmd_section_t)); + if(lexem.type != LEX_IDENTIFIER || strcmp(lexem.str, "section") != 0) + parse_error(lexem, "'section' expected\n"); + next(true); + if(lexem.type != LEX_LPAREN) + parse_error(lexem, "'(' expected after 'section'\n"); + next(true); + /* can be any number */ + sec->identifier = parse_intexpr(&lctx, cmd_file->constant_list); + /* options ? */ + if(lexem.type == LEX_SEMICOLON) + { + do + { + next(true); + struct cmd_option_t *opt = xmalloc(sizeof(struct cmd_option_t)); + memset(opt, 0, sizeof(struct cmd_option_t)); + if(lexem.type != LEX_IDENTIFIER) + parse_error(lexem, "Identifier expected for section option\n"); + opt->name = lexem.str; + next(false); /* lexem string is kept as option name */ + if(lexem.type != LEX_EQUAL) + parse_error(lexem, "'=' expected after option identifier\n"); + next(true); + if(lexem.type == LEX_STRING) + { + opt->is_string = true; + opt->str = lexem.str; + next(false); /* lexem string is kept as option string */ + } + else + { + opt->is_string = false; + opt->val = parse_intexpr(&lctx, cmd_file->constant_list); + } + opt->next = sec->opt_list; + sec->opt_list = opt; + }while(lexem.type == LEX_COLON); + } + if(lexem.type != LEX_RPAREN) + parse_error(lexem, "')' expected after section identifier\n"); + next(true); + if(lexem.type == LEX_LBRACE) + { + sec->is_data = false; + /* commands */ + while(true) + { + next(true); + if(lexem.type == LEX_RBRACE) + break; + struct cmd_inst_t *inst = xmalloc(sizeof(struct cmd_inst_t)); + memset(inst, 0, sizeof(struct cmd_inst_t)); + if(lexem.type != LEX_IDENTIFIER) + parse_error(lexem, "Instruction expected in section\n"); + if(strcmp(lexem.str, "load") == 0) + inst->type = CMD_LOAD; + else if(strcmp(lexem.str, "call") == 0) + inst->type = CMD_CALL; + else if(strcmp(lexem.str, "jump") == 0) + inst->type = CMD_JUMP; + else if(strcmp(lexem.str, "mode") == 0) + inst->type = CMD_MODE; + else + parse_error(lexem, "Instruction expected in section\n"); + next(true); + + if(inst->type == CMD_LOAD) + { + if(lexem.type != LEX_IDENTIFIER) + parse_error(lexem, "Identifier expected after instruction\n"); + inst->identifier = lexem.str; + if(db_find_source_by_id(cmd_file, inst->identifier) == NULL) + parse_error(lexem, "Undefined reference to source '%s'\n", inst->identifier); + next(false); /* lexem string kept as identifier */ + if(lexem.type == LEX_RANGLE) + { + // load at + inst->type = CMD_LOAD_AT; + next(true); + inst->addr = parse_intexpr(&lctx, cmd_file->constant_list); + } + if(lexem.type != LEX_SEMICOLON) + parse_error(lexem, "';' expected after command\n"); + } + else if(inst->type == CMD_CALL || inst->type == CMD_JUMP) + { + if(lexem.type == LEX_IDENTIFIER) + { + inst->identifier = lexem.str; + if(db_find_source_by_id(cmd_file, inst->identifier) == NULL) + parse_error(lexem, "Undefined reference to source '%s'\n", inst->identifier); + next(false); /* lexem string kept as identifier */ + } + else + { + inst->type = (inst->type == CMD_CALL) ? CMD_CALL_AT : CMD_JUMP_AT; + inst->addr = parse_intexpr(&lctx, cmd_file->constant_list); + } + + if(lexem.type == LEX_LPAREN) + { + next(true); + inst->argument = parse_intexpr(&lctx, cmd_file->constant_list); + if(lexem.type != LEX_RPAREN) + parse_error(lexem, "Expected closing brace\n"); + next(true); + } + if(lexem.type != LEX_SEMICOLON) + parse_error(lexem, "';' expected after command\n"); + } + else if(inst->type == CMD_MODE) + { + inst->argument = parse_intexpr(&lctx, cmd_file->constant_list); + if(lexem.type != LEX_SEMICOLON) + parse_error(lexem, "Expected ';' after command\n"); + } + else + parse_error(lexem, "Internal error"); + if(end_list == NULL) + { + sec->inst_list = inst; + end_list = inst; + } + else + { + end_list->next = inst; + end_list = inst; + } + } + } + else if(lexem.type == LEX_LE) + { + sec->is_data = true; + next(true); + if(lexem.type != LEX_IDENTIFIER) + parse_error(lexem, "Identifier expected after '<='\n"); + sec->source_id = lexem.str; + next(false); /* lexem string is kept as source id */ + if(lexem.type != LEX_SEMICOLON) + parse_error(lexem, "';' expected after identifier\n"); + } + else + parse_error(lexem, "'{' or '<=' expected after section directive\n"); + + if(end_sec == NULL) + { + cmd_file->section_list = sec; + end_sec = sec; + } + else + { + end_sec->next = sec; + end_sec = sec; + } + } + #undef lexem + #undef next + + free(buf); + return cmd_file; +} + +void db_generate_default_sb_version(struct sb_version_t *ver) +{ + ver->major = ver->minor = ver->revision = 0x999; +} + +void db_free_option_list(struct cmd_option_t *opt_list) +{ + while(opt_list) + { + struct cmd_option_t *next = opt_list->next; + fflush(stdout); + free(opt_list->name); + free(opt_list->str); + free(opt_list); + opt_list = next; + } +} + +void db_free(struct cmd_file_t *file) +{ + db_free_option_list(file->opt_list); + db_free_option_list(file->constant_list); + struct cmd_source_t *src = file->source_list; + while(src) + { + struct cmd_source_t *next = src->next; + free(src->identifier); + fflush(stdout); + free(src->filename); + if(src->loaded) + { + if(src->type == CMD_SRC_BIN) + free(src->bin.data); + if(src->type == CMD_SRC_ELF) + elf_release(&src->elf); + } + free(src); + src = next; + } + struct cmd_section_t *sec = file->section_list; + while(sec) + { + struct cmd_section_t *next = sec->next; + db_free_option_list(sec->opt_list); + free(sec->source_id); + struct cmd_inst_t *inst = sec->inst_list; + while(inst) + { + struct cmd_inst_t *next = inst->next; + free(inst->identifier); + free(inst); + inst = next; + } + free(sec); + sec = next; + } + free(file); +} diff --git a/utils/imxtools/sbtools/dbparser.h b/utils/imxtools/sbtools/dbparser.h new file mode 100644 index 0000000000..4a36861583 --- /dev/null +++ b/utils/imxtools/sbtools/dbparser.h @@ -0,0 +1,118 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2011 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 __DBPARSER__ +#define __DBPARSER__ + +/** + * Command file parsing + */ +#include "sb.h" +#include "elf.h" + +enum cmd_source_type_t +{ + CMD_SRC_UNK, + CMD_SRC_ELF, + CMD_SRC_BIN +}; + +struct bin_param_t +{ + uint32_t size; + void *data; +}; + +struct cmd_source_t +{ + char *identifier; + bool is_extern; + // + int extern_nr; + char *filename; + // + struct cmd_source_t *next; + /* for later use */ + enum cmd_source_type_t type; + bool loaded; + struct elf_params_t elf; + struct bin_param_t bin; +}; + +enum cmd_inst_type_t +{ + CMD_LOAD, /* load image */ + CMD_JUMP, /* jump at image */ + CMD_CALL, /* call image */ + CMD_LOAD_AT, /* load binary at */ + CMD_CALL_AT, /* call at address */ + CMD_JUMP_AT, /* jump at address */ + CMD_MODE, /* change boot mode */ +}; + +struct cmd_inst_t +{ + enum cmd_inst_type_t type; + char *identifier; + uint32_t argument; // for jump, call, mode + uint32_t addr; // for 'at' + struct cmd_inst_t *next; +}; + +struct cmd_option_t +{ + char *name; + bool is_string; + /* */ + uint32_t val; + char *str; + /* */ + struct cmd_option_t *next; +}; + +struct cmd_section_t +{ + uint32_t identifier; + bool is_data; + // + struct cmd_inst_t *inst_list; + char *source_id; + // + struct cmd_section_t *next; + struct cmd_option_t *opt_list; +}; + +struct cmd_file_t +{ + struct cmd_option_t *opt_list; + struct cmd_option_t *constant_list; /* constant are always integers */ + struct cmd_source_t *source_list; + struct cmd_section_t *section_list; +}; + +struct cmd_source_t *db_find_source_by_id(struct cmd_file_t *cmd_file, const char *id); +struct cmd_option_t *db_find_option_by_id(struct cmd_option_t *opt, const char *name); +bool db_parse_sb_version(struct sb_version_t *ver, char *str); +void db_generate_default_sb_version(struct sb_version_t *ver); +struct cmd_file_t *db_parse_file(const char *file); +void db_free_option_list(struct cmd_option_t *opt_list); +void db_free(struct cmd_file_t *file); + +#endif /* __DBPARSER__ */ diff --git a/utils/imxtools/sbtools/elf.c b/utils/imxtools/sbtools/elf.c new file mode 100644 index 0000000000..481ab98dd6 --- /dev/null +++ b/utils/imxtools/sbtools/elf.c @@ -0,0 +1,575 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2011 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 "elf.h" +#include "misc.h" + +/** + * Definitions + * taken from elf.h linux header + * based on ELF specification + * based on ARM ELF specification + */ +typedef uint16_t Elf32_Half; + +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf32_Addr; +typedef uint32_t Elf32_Off; +typedef uint16_t Elf32_Section; + +#define EI_NIDENT 16 + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf32_Half e_type; /* Object file type */ + Elf32_Half e_machine; /* Architecture */ + Elf32_Word e_version; /* Object file version */ + Elf32_Addr e_entry; /* Entry point virtual address */ + Elf32_Off e_phoff; /* Program header table file offset */ + Elf32_Off e_shoff; /* Section header table file offset */ + Elf32_Word e_flags; /* Processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size in bytes */ + Elf32_Half e_phentsize; /* Program header table entry size */ + Elf32_Half e_phnum; /* Program header table entry count */ + Elf32_Half e_shentsize; /* Section header table entry size */ + Elf32_Half e_shnum; /* Section header table entry count */ + Elf32_Half e_shstrndx; /* Section header string table index */ +}Elf32_Ehdr; + +#define EI_MAG0 0 /* File identification byte 0 index */ +#define ELFMAG0 0x7f /* Magic number byte 0 */ + +#define EI_MAG1 1 /* File identification byte 1 index */ +#define ELFMAG1 'E' /* Magic number byte 1 */ + +#define EI_MAG2 2 /* File identification byte 2 index */ +#define ELFMAG2 'L' /* Magic number byte 2 */ + +#define EI_MAG3 3 /* File identification byte 3 index */ +#define ELFMAG3 'F' /* Magic number byte 3 */ + +#define EI_CLASS 4 /* File class byte index */ +#define ELFCLASS32 1 /* 32-bit objects */ + +#define EI_DATA 5 /* Data encoding byte index */ +#define ELFDATA2LSB 1 /* 2's complement, little endian */ + +#define EI_VERSION 6 /* File version byte index, Value must be EV_CURRENT */ + +#define EI_OSABI 7 /* OS ABI identification */ +#define ELFOSABI_NONE 0 /* UNIX System V ABI */ +#define ELFOSABI_ARM_AEABI 64 /* ARM EABI */ +#define ELFOSABI_ARM 97 /* ARM */ + +#define EI_ABIVERSION 8 /* ABI version */ + +#define EI_PAD 9 /* Byte index of padding bytes */ + +#define ET_EXEC 2 /* Executable file */ + +#define EM_ARM 40 /* ARM */ + +#define EV_CURRENT 1 /* Current version */ + +#define EF_ARM_HASENTRY 0x00000002 + +#define SHN_UNDEF 0 /* Undefined section */ + +typedef struct +{ + Elf32_Word sh_name; /* Section name (string tbl index) */ + Elf32_Word sh_type; /* Section type */ + Elf32_Word sh_flags; /* Section flags */ + Elf32_Addr sh_addr; /* Section virtual addr at execution */ + Elf32_Off sh_offset; /* Section file offset */ + Elf32_Word sh_size; /* Section size in bytes */ + Elf32_Word sh_link; /* Link to another section */ + Elf32_Word sh_info; /* Additional section information */ + Elf32_Word sh_addralign; /* Section alignment */ + Elf32_Word sh_entsize; /* Entry size if section holds table */ +}Elf32_Shdr; + +#define SHT_NULL 0 /* Section header table entry unused */ +#define SHT_PROGBITS 1 /* Program data */ +#define SHT_SYMTAB 2 /* Symbol table */ +#define SHT_STRTAB 3 /* String table */ +#define SHT_RELA 4 /* Relocation entries with addends */ +#define SHT_HASH 5 /* Symbol hash table */ +#define SHT_DYNAMIC 6 /* Dynamic linking information */ +#define SHT_NOTE 7 /* Notes */ +#define SHT_NOBITS 8 /* Program space with no data (bss) */ +#define SHT_REL 9 /* Relocation entries, no addends */ +#define SHT_SHLIB 10 /* Reserved */ +#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ +#define SHT_INIT_ARRAY 14 /* Array of constructors */ +#define SHT_FINI_ARRAY 15 /* Array of destructors */ +#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ +#define SHT_GROUP 17 /* Section group */ +#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ +#define SHT_NUM 19 /* Number of defined types. */ + +#define SHF_WRITE (1 << 0) /* Writable */ +#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ +#define SHF_EXECINSTR (1 << 2) /* Executable */ +#define SHF_MERGE (1 << 4) /* Might be merged */ +#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ + +typedef struct +{ + Elf32_Word p_type; /* Segment type */ + Elf32_Off p_offset; /* Segment file offset */ + Elf32_Addr p_vaddr; /* Segment virtual address */ + Elf32_Addr p_paddr; /* Segment physical address */ + Elf32_Word p_filesz; /* Segment size in file */ + Elf32_Word p_memsz; /* Segment size in memory */ + Elf32_Word p_flags; /* Segment flags */ + Elf32_Word p_align; /* Segment alignment */ +}Elf32_Phdr; + +#define PT_LOAD 1 /* Loadable program segment */ + +#define PF_X (1 << 0) /* Segment is executable */ +#define PF_W (1 << 1) /* Segment is writable */ +#define PF_R (1 << 2) /* Segment is readable */ + +void elf_init(struct elf_params_t *params) +{ + memset(params, 0, sizeof(struct elf_params_t)); +} + +extern void *xmalloc(size_t s); + +static struct elf_section_t *elf_add_section(struct elf_params_t *params) +{ + struct elf_section_t *sec = xmalloc(sizeof(struct elf_section_t)); + if(params->first_section == NULL) + params->first_section = params->last_section = sec; + else + { + params->last_section->next = sec; + params->last_section = sec; + } + sec->next = NULL; + + return sec; +} + +static struct elf_segment_t *elf_add_segment(struct elf_params_t *params) +{ + struct elf_segment_t *seg = xmalloc(sizeof(struct elf_section_t)); + if(params->first_segment == NULL) + params->first_segment = params->last_segment = seg; + else + { + params->last_segment->next = seg; + params->last_segment = seg; + } + seg->next = NULL; + + return seg; +} + +void elf_add_load_section(struct elf_params_t *params, + uint32_t load_addr, uint32_t size, const void *section) +{ + struct elf_section_t *sec = elf_add_section(params); + + sec->type = EST_LOAD; + sec->addr = load_addr; + sec->size = size; + sec->section = xmalloc(size); + memcpy(sec->section, section, size); +} + +void elf_add_fill_section(struct elf_params_t *params, + uint32_t fill_addr, uint32_t size, uint32_t pattern) +{ + if(pattern != 0x00) + { + printf("oops, non-zero filling, ignore fill section\n"); + return; + } + + struct elf_section_t *sec = elf_add_section(params); + + sec->type = EST_FILL; + sec->addr = fill_addr; + sec->size = size; + sec->pattern = pattern; +} + +void elf_write_file(struct elf_params_t *params, elf_write_fn_t write, + elf_printf_fn_t printf, void *user) +{ + (void) printf; + + Elf32_Ehdr ehdr; + uint32_t phnum = 0; + struct elf_section_t *sec = params->first_section; + uint32_t offset = 0; + Elf32_Phdr phdr; + Elf32_Shdr shdr; + memset(&ehdr, 0, EI_NIDENT); + + while(sec) + { + if(sec->type == EST_LOAD) + { + sec->offset = offset; + offset += sec->size; + } + else + { + sec->offset = 0; + } + + phnum++; + sec = sec->next; + } + + uint32_t strtbl_offset = offset; + + ehdr.e_ident[EI_MAG0] = ELFMAG0; + ehdr.e_ident[EI_MAG1] = ELFMAG1; + ehdr.e_ident[EI_MAG2] = ELFMAG2; + ehdr.e_ident[EI_MAG3] = ELFMAG3; + ehdr.e_ident[EI_CLASS] = ELFCLASS32; + ehdr.e_ident[EI_DATA] = ELFDATA2LSB; + ehdr.e_ident[EI_VERSION] = EV_CURRENT; + ehdr.e_ident[EI_OSABI] = ELFOSABI_NONE; + ehdr.e_ident[EI_ABIVERSION] = 0; + ehdr.e_type = ET_EXEC; + ehdr.e_machine = EM_ARM; + ehdr.e_version = EV_CURRENT; + ehdr.e_entry = params->start_addr; + ehdr.e_flags = 0; + if(params->has_start_addr) + ehdr.e_flags |= EF_ARM_HASENTRY; + ehdr.e_ehsize = sizeof ehdr; + ehdr.e_phentsize = sizeof phdr; + ehdr.e_phnum = phnum; + ehdr.e_shentsize = sizeof shdr; + ehdr.e_shnum = phnum + 2; /* one for section 0 and one for string table */ + ehdr.e_shstrndx = ehdr.e_shnum - 1; + ehdr.e_phoff = ehdr.e_ehsize; + ehdr.e_shoff = ehdr.e_ehsize + ehdr.e_phnum * ehdr.e_phentsize; + + write(user, 0, &ehdr, sizeof ehdr); + + /* allocate enough size to hold any combinaison of .text/.bss in the string table: + * - one empty name ("\0") + * - at most N names of the form ".textXXXX\0" or ".bssXXXX\0" + * - one name ".shstrtab\0" */ + char *strtbl_content = malloc(1 + strlen(".shstrtab") + 1 + + phnum * (strlen(".textXXXX") + 1)); + + strtbl_content[0] = '\0'; + strcpy(&strtbl_content[1], ".shstrtab"); + uint32_t strtbl_index = 1 + strlen(".shstrtab") + 1; + + uint32_t data_offset = ehdr.e_ehsize + ehdr.e_phnum * ehdr.e_phentsize + + ehdr.e_shnum * ehdr.e_shentsize; + + sec = params->first_section; + offset = ehdr.e_phoff; + while(sec) + { + sec->offset += data_offset; + + phdr.p_type = PT_LOAD; + if(sec->type == EST_LOAD) + phdr.p_offset = sec->offset; + else + phdr.p_offset = 0; + phdr.p_paddr = sec->addr; + phdr.p_vaddr = phdr.p_paddr; /* assume identity map ? */ + phdr.p_memsz = sec->size; + if(sec->type == EST_LOAD) + phdr.p_filesz = phdr.p_memsz; + else + phdr.p_filesz = 0; + phdr.p_flags = PF_X | PF_W | PF_R; + phdr.p_align = 0; + + write(user, offset, &phdr, sizeof phdr); + + offset += sizeof(Elf32_Phdr); + sec = sec->next; + } + + sec = params->first_section; + offset = ehdr.e_shoff; + + { + shdr.sh_name = 0; + shdr.sh_type = SHT_NULL; + shdr.sh_flags = 0; + shdr.sh_addr = 0; + shdr.sh_offset = 0; + shdr.sh_size = 0; + shdr.sh_link = SHN_UNDEF; + shdr.sh_info = 0; + shdr.sh_addralign = 0; + shdr.sh_entsize = 0; + + write(user, offset, &shdr, sizeof shdr); + + offset += sizeof(Elf32_Shdr); + } + + uint32_t text_idx = 0; + uint32_t bss_idx = 0; + while(sec) + { + shdr.sh_name = strtbl_index; + if(sec->type == EST_LOAD) + { + strtbl_index += 1 + sprintf(&strtbl_content[strtbl_index], ".text%d", text_idx++); + shdr.sh_type = SHT_PROGBITS; + } + else + { + strtbl_index += 1 + sprintf(&strtbl_content[strtbl_index], ".bss%d", bss_idx++); + shdr.sh_type = SHT_NOBITS; + } + shdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR; + shdr.sh_addr = sec->addr; + shdr.sh_offset = sec->offset; + shdr.sh_size = sec->size; + shdr.sh_link = SHN_UNDEF; + shdr.sh_info = 0; + shdr.sh_addralign = 1; + shdr.sh_entsize = 0; + + write(user, offset, &shdr, sizeof shdr); + + offset += sizeof(Elf32_Shdr); + sec = sec->next; + } + + { + shdr.sh_name = 1; + shdr.sh_type = SHT_STRTAB; + shdr.sh_flags = 0; + shdr.sh_addr = 0; + shdr.sh_offset = strtbl_offset + data_offset; + shdr.sh_size = strtbl_index; + shdr.sh_link = SHN_UNDEF; + shdr.sh_info = 0; + shdr.sh_addralign = 1; + shdr.sh_entsize = 0; + + write(user, offset, &shdr, sizeof shdr); + + offset += sizeof(Elf32_Shdr); + } + + sec = params->first_section; + while(sec) + { + if(sec->type == EST_LOAD) + write(user, sec->offset, sec->section, sec->size); + sec = sec->next; + } + + write(user, strtbl_offset + data_offset, strtbl_content, strtbl_index); + free(strtbl_content); +} + +bool elf_read_file(struct elf_params_t *params, elf_read_fn_t read, + elf_printf_fn_t printf, void *user) +{ + #define error_printf(...) ({printf(user, true, __VA_ARGS__); return false;}) + + /* read header */ + Elf32_Ehdr ehdr; + if(!read(user, 0, &ehdr, sizeof(ehdr))) + { + printf(user, true, "error reading elf header\n"); + return false; + } + /* basic checks */ + if(ehdr.e_ident[EI_MAG0] != ELFMAG0 || ehdr.e_ident[EI_MAG1] != ELFMAG1 || + ehdr.e_ident[EI_MAG2] != ELFMAG2 || ehdr.e_ident[EI_MAG3] != ELFMAG3) + error_printf("invalid elf header\n"); + if(ehdr.e_ident[EI_CLASS] != ELFCLASS32) + error_printf("invalid elf class: must be a 32-bit object\n"); + if(ehdr.e_ident[EI_DATA] != ELFDATA2LSB) + error_printf("invalid elf data encoding: must be 32-bit lsb\n"); + if(ehdr.e_ident[EI_VERSION] != EV_CURRENT) + error_printf("invalid elf version\n"); + if(ehdr.e_type != ET_EXEC) + error_printf("invalid elf file: must be an executable file\n"); + if(ehdr.e_machine != EM_ARM) + error_printf("invalid elf file: must target an arm machine\n"); + if(ehdr.e_ehsize != sizeof(ehdr)) + error_printf("invalid elf file: size header mismatch\n"); + if(ehdr.e_phnum > 0 && ehdr.e_phentsize != sizeof(Elf32_Phdr)) + error_printf("invalid elf file: program header size mismatch\n"); + if(ehdr.e_shnum > 0 && ehdr.e_shentsize != sizeof(Elf32_Shdr)) + error_printf("invalid elf file: section header size mismatch\n"); + elf_set_start_addr(params, ehdr.e_entry); + + char *strtab = NULL; + if(ehdr.e_shstrndx != SHN_UNDEF) + { + Elf32_Shdr shstrtab; + if(read(user, ehdr.e_shoff + ehdr.e_shstrndx * ehdr.e_shentsize, + &shstrtab, sizeof(shstrtab))) + { + strtab = xmalloc(shstrtab.sh_size); + if(!read(user, shstrtab.sh_offset, strtab, shstrtab.sh_size)) + { + free(strtab); + strtab = NULL; + } + } + } + /* run through sections */ + printf(user, false, "ELF file:\n"); + for(int i = 1; i < ehdr.e_shnum; i++) + { + uint32_t off = ehdr.e_shoff + i * ehdr.e_shentsize; + Elf32_Shdr shdr; + memset(&shdr, 0, sizeof(shdr)); + if(!read(user, off, &shdr, sizeof(shdr))) + error_printf("error reading elf section header"); + + if(shdr.sh_type == SHT_PROGBITS && shdr.sh_flags & SHF_ALLOC) + { + void *data = xmalloc(shdr.sh_size); + if(!read(user, shdr.sh_offset, data, shdr.sh_size)) + error_printf("error read self section data\n"); + elf_add_load_section(params, shdr.sh_addr, shdr.sh_size, data); + free(data); + + if(strtab) + printf(user, false, "create load segment for %s\n", &strtab[shdr.sh_name]); + } + else if(shdr.sh_type == SHT_NOBITS && shdr.sh_flags & SHF_ALLOC) + { + elf_add_fill_section(params, shdr.sh_addr, shdr.sh_size, 0); + if(strtab) + printf(user, false, "create fill segment for %s\n", &strtab[shdr.sh_name]); + } + else + { + if(strtab) + printf(user, false, "filter out %s\n", &strtab[shdr.sh_name], shdr.sh_type); + } + + } + free(strtab); + /* run through segments */ + for(int i = 1; i < ehdr.e_phnum; i++) + { + uint32_t off = ehdr.e_phoff + i * ehdr.e_phentsize; + Elf32_Phdr phdr; + memset(&phdr, 0, sizeof(phdr)); + if(!read(user, off, &phdr, sizeof(phdr))) + error_printf("error reading elf segment header"); + if(phdr.p_type != PT_LOAD) + continue; + struct elf_segment_t *seg = elf_add_segment(params); + seg->vaddr = phdr.p_vaddr; + seg->paddr = phdr.p_paddr; + seg->vsize = phdr.p_memsz; + seg->psize = phdr.p_filesz; + printf(user, false, "create segment [%#x,+%#x[ -> [%#x,+%#x[\n", + seg->vaddr, seg->vsize, seg->paddr, seg->psize); + } + + return true; +} + +uint32_t elf_translate_virtual_address(struct elf_params_t *params, uint32_t addr) +{ + struct elf_segment_t *seg = params->first_segment; + while(seg) + { + if(seg->vaddr <= addr && addr < seg->vaddr + seg->vsize) + return addr - seg->vaddr + seg->paddr; + seg = seg->next; + } + return addr; +} + +void elf_translate_addresses(struct elf_params_t *params) +{ + struct elf_section_t *sec = params->first_section; + while(sec) + { + sec->addr = elf_translate_virtual_address(params, sec->addr); + sec = sec->next; + } + params->start_addr = elf_translate_virtual_address(params, params->start_addr); +} + +bool elf_is_empty(struct elf_params_t *params) +{ + return params->first_section == NULL; +} + +void elf_set_start_addr(struct elf_params_t *params, uint32_t addr) +{ + params->has_start_addr = true; + params->start_addr = addr; +} + +bool elf_get_start_addr(struct elf_params_t *params, uint32_t *addr) +{ + if(params->has_start_addr && addr != NULL) + *addr = params->start_addr; + return params->has_start_addr; +} + +int elf_get_nr_sections(struct elf_params_t *params) +{ + int nr = 0; + struct elf_section_t *sec = params->first_section; + while(sec) + { + nr++; + sec = sec->next; + } + return nr; +} + +void elf_release(struct elf_params_t *params) +{ + struct elf_section_t *sec = params->first_section; + while(sec) + { + struct elf_section_t *next_sec = sec->next; + if(sec->type == EST_LOAD) + free(sec->section); + free(sec); + sec = next_sec; + } + struct elf_segment_t *seg = params->first_segment; + while(seg) + { + struct elf_segment_t *next_seg = seg->next; + free(seg); + seg = next_seg; + } +} diff --git a/utils/imxtools/sbtools/elf.h b/utils/imxtools/sbtools/elf.h new file mode 100644 index 0000000000..2166833276 --- /dev/null +++ b/utils/imxtools/sbtools/elf.h @@ -0,0 +1,94 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2011 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 __ELF_H__ +#define __ELF_H__ + +#include +#include +#include +#include +#include +#include + +/** + * API + */ +enum elf_section_type_t +{ + EST_LOAD, + EST_FILL +}; + +struct elf_section_t +{ + uint32_t addr; /* virtual address */ + uint32_t size; /* virtual size */ + enum elf_section_type_t type; + /* */ + void *section; /* data */ + uint32_t pattern; /* fill pattern */ + /* */ + struct elf_section_t *next; + /* Internal to elf_write_file */ + uint32_t offset; +}; + +struct elf_segment_t +{ + uint32_t vaddr; /* virtual address */ + uint32_t paddr; /* physical address */ + uint32_t vsize; /* virtual size */ + uint32_t psize; /* physical size */ + struct elf_segment_t *next; +}; + +struct elf_params_t +{ + bool has_start_addr; + uint32_t start_addr; + struct elf_section_t *first_section; + struct elf_section_t *last_section; + struct elf_segment_t *first_segment; + struct elf_segment_t *last_segment; +}; + +typedef bool (*elf_read_fn_t)(void *user, uint32_t addr, void *buf, size_t count); +/* write function manages it's own error state */ +typedef void (*elf_write_fn_t)(void *user, uint32_t addr, const void *buf, size_t count); +typedef void (*elf_printf_fn_t)(void *user, bool error, const char *fmt, ...); + +void elf_init(struct elf_params_t *params); +void elf_add_load_section(struct elf_params_t *params, + uint32_t load_addr, uint32_t size, const void *section); +void elf_add_fill_section(struct elf_params_t *params, + uint32_t fill_addr, uint32_t size, uint32_t pattern); +uint32_t elf_translate_virtual_address(struct elf_params_t *params, uint32_t addr); +void elf_translate_addresses(struct elf_params_t *params); +void elf_write_file(struct elf_params_t *params, elf_write_fn_t write, elf_printf_fn_t printf, void *user); +bool elf_read_file(struct elf_params_t *params, elf_read_fn_t read, elf_printf_fn_t printf, + void *user); +bool elf_is_empty(struct elf_params_t *params); +void elf_set_start_addr(struct elf_params_t *params, uint32_t addr); +bool elf_get_start_addr(struct elf_params_t *params, uint32_t *addr); +int elf_get_nr_sections(struct elf_params_t *params); +void elf_release(struct elf_params_t *params); + +#endif /* __ELF_H__ */ diff --git a/utils/imxtools/sbtools/elftosb.c b/utils/imxtools/sbtools/elftosb.c new file mode 100644 index 0000000000..43bcf34849 --- /dev/null +++ b/utils/imxtools/sbtools/elftosb.c @@ -0,0 +1,462 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2011 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. + * + ****************************************************************************/ + +#define _ISOC99_SOURCE +#define _POSIX_C_SOURCE 200809L /* for strdup */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "crypto.h" +#include "elf.h" +#include "sb.h" +#include "dbparser.h" +#include "misc.h" +#include "sb.h" + +char **g_extern; +int g_extern_count; + +#define ROUND_UP(val, round) ((((val) + (round) - 1) / (round)) * (round)) + +#define crypto_cbc(...) \ + do { int ret = crypto_cbc(__VA_ARGS__); \ + if(ret != CRYPTO_ERROR_SUCCESS) \ + bug("crypto_cbc error: %d\n", ret); \ + }while(0) + +/** + * command file to sb conversion + */ + +static bool elf_read(void *user, uint32_t addr, void *buf, size_t count) +{ + if(fseek((FILE *)user, addr, SEEK_SET) == -1) + return false; + return fread(buf, 1, count, (FILE *)user) == count; +} + +static void elf_printf(void *user, bool error, const char *fmt, ...) +{ + if(!g_debug && !error) + return; + (void) user; + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} + +static void resolve_extern(struct cmd_source_t *src) +{ + if(!src->is_extern) + return; + src->is_extern = false; + if(src->extern_nr < 0 || src->extern_nr >= g_extern_count) + bug("There aren't enough file on command file to resolve extern(%d)\n", src->extern_nr); + /* first free the old src->filename content */ + free(src->filename); + src->filename = strdup(g_extern[src->extern_nr]); +} + +static void load_elf_by_id(struct cmd_file_t *cmd_file, const char *id) +{ + struct cmd_source_t *src = db_find_source_by_id(cmd_file, id); + if(src == NULL) + bug("undefined reference to source '%s'\n", id); + /* avoid reloading */ + if(src->type == CMD_SRC_ELF && src->loaded) + return; + if(src->type != CMD_SRC_UNK) + bug("source '%s' seen both as elf and binary file\n", id); + /* resolve potential extern file */ + resolve_extern(src); + /* load it */ + src->type = CMD_SRC_ELF; + FILE *fd = fopen(src->filename, "rb"); + if(fd == NULL) + bug("cannot open '%s' (id '%s')\n", src->filename, id); + if(g_debug) + printf("Loading ELF file '%s'...\n", src->filename); + elf_init(&src->elf); + src->loaded = elf_read_file(&src->elf, elf_read, elf_printf, fd); + fclose(fd); + if(!src->loaded) + bug("error loading elf file '%s' (id '%s')\n", src->filename, id); + elf_translate_addresses(&src->elf); +} + +static void load_bin_by_id(struct cmd_file_t *cmd_file, const char *id) +{ + struct cmd_source_t *src = db_find_source_by_id(cmd_file, id); + if(src == NULL) + bug("undefined reference to source '%s'\n", id); + /* avoid reloading */ + if(src->type == CMD_SRC_BIN && src->loaded) + return; + if(src->type != CMD_SRC_UNK) + bug("source '%s' seen both as elf and binary file\n", id); + /* resolve potential extern file */ + resolve_extern(src); + /* load it */ + src->type = CMD_SRC_BIN; + FILE *fd = fopen(src->filename, "rb"); + if(fd == NULL) + bug("cannot open '%s' (id '%s')\n", src->filename, id); + if(g_debug) + printf("Loading BIN file '%s'...\n", src->filename); + fseek(fd, 0, SEEK_END); + src->bin.size = ftell(fd); + fseek(fd, 0, SEEK_SET); + src->bin.data = xmalloc(src->bin.size); + fread(src->bin.data, 1, src->bin.size, fd); + fclose(fd); + src->loaded = true; +} + +static struct sb_file_t *apply_cmd_file(struct cmd_file_t *cmd_file) +{ + struct sb_file_t *sb = xmalloc(sizeof(struct sb_file_t)); + memset(sb, 0, sizeof(struct sb_file_t)); + + db_generate_default_sb_version(&sb->product_ver); + db_generate_default_sb_version(&sb->component_ver); + + if(g_debug) + printf("Applying command file...\n"); + /* count sections */ + struct cmd_section_t *csec = cmd_file->section_list; + while(csec) + { + sb->nr_sections++; + csec = csec->next; + } + + sb->sections = xmalloc(sb->nr_sections * sizeof(struct sb_section_t)); + memset(sb->sections, 0, sb->nr_sections * sizeof(struct sb_section_t)); + /* flatten sections */ + csec = cmd_file->section_list; + for(int i = 0; i < sb->nr_sections; i++, csec = csec->next) + { + struct sb_section_t *sec = &sb->sections[i]; + sec->identifier = csec->identifier; + + /* options */ + do + { + /* cleartext */ + struct cmd_option_t *opt = db_find_option_by_id(csec->opt_list, "cleartext"); + if(opt != NULL) + { + if(opt->is_string) + bug("Cleartext section attribute must be an integer\n"); + if(opt->val != 0 && opt->val != 1) + bug("Cleartext section attribute must be 0 or 1\n"); + sec->is_cleartext = opt->val; + } + /* alignment */ + opt = db_find_option_by_id(csec->opt_list, "alignment"); + if(opt != NULL) + { + if(opt->is_string) + bug("Cleartext section attribute must be an integer\n"); + // n is a power of 2 iff n & (n - 1) = 0 + // alignement cannot be lower than block size + if((opt->val & (opt->val - 1)) != 0) + bug("Cleartext section attribute must be a power of two\n"); + if(opt->val < BLOCK_SIZE) + sec->alignment = BLOCK_SIZE; + else + sec->alignment = opt->val; + } + else + sec->alignment = BLOCK_SIZE; + }while(0); + + if(csec->is_data) + { + sec->is_data = true; + sec->nr_insts = 1; + sec->insts = xmalloc(sec->nr_insts * sizeof(struct sb_inst_t)); + memset(sec->insts, 0, sec->nr_insts * sizeof(struct sb_inst_t)); + + load_bin_by_id(cmd_file, csec->source_id); + struct bin_param_t *bin = &db_find_source_by_id(cmd_file, csec->source_id)->bin; + + sec->insts[0].inst = SB_INST_DATA; + sec->insts[0].size = bin->size; + sec->insts[0].data = memdup(bin->data, bin->size); + } + else + { + sec->is_data = false; + /* count instructions and loads things */ + struct cmd_inst_t *cinst = csec->inst_list; + while(cinst) + { + if(cinst->type == CMD_LOAD) + { + load_elf_by_id(cmd_file, cinst->identifier); + struct elf_params_t *elf = &db_find_source_by_id(cmd_file, cinst->identifier)->elf; + sec->nr_insts += elf_get_nr_sections(elf); + } + else if(cinst->type == CMD_JUMP || cinst->type == CMD_CALL) + { + load_elf_by_id(cmd_file, cinst->identifier); + struct elf_params_t *elf = &db_find_source_by_id(cmd_file, cinst->identifier)->elf; + if(!elf_get_start_addr(elf, NULL)) + bug("cannot jump/call '%s' because it has no starting point !\n", cinst->identifier); + sec->nr_insts++; + } + else if(cinst->type == CMD_CALL_AT || cinst->type == CMD_JUMP_AT) + { + sec->nr_insts++; + } + else if(cinst->type == CMD_LOAD_AT) + { + load_bin_by_id(cmd_file, cinst->identifier); + sec->nr_insts++; + } + else if(cinst->type == CMD_MODE) + { + sec->nr_insts++; + } + else + bug("die\n"); + + cinst = cinst->next; + } + + sec->insts = xmalloc(sec->nr_insts * sizeof(struct sb_inst_t)); + memset(sec->insts, 0, sec->nr_insts * sizeof(struct sb_inst_t)); + /* flatten */ + int idx = 0; + cinst = csec->inst_list; + while(cinst) + { + if(cinst->type == CMD_LOAD) + { + struct elf_params_t *elf = &db_find_source_by_id(cmd_file, cinst->identifier)->elf; + struct elf_section_t *esec = elf->first_section; + while(esec) + { + if(esec->type == EST_LOAD) + { + sec->insts[idx].inst = SB_INST_LOAD; + sec->insts[idx].addr = esec->addr; + sec->insts[idx].size = esec->size; + sec->insts[idx++].data = memdup(esec->section, esec->size); + } + else if(esec->type == EST_FILL) + { + sec->insts[idx].inst = SB_INST_FILL; + sec->insts[idx].addr = esec->addr; + sec->insts[idx].size = esec->size; + sec->insts[idx++].pattern = esec->pattern; + } + esec = esec->next; + } + } + else if(cinst->type == CMD_JUMP || cinst->type == CMD_CALL) + { + struct elf_params_t *elf = &db_find_source_by_id(cmd_file, cinst->identifier)->elf; + sec->insts[idx].argument = cinst->argument; + sec->insts[idx].inst = (cinst->type == CMD_JUMP) ? SB_INST_JUMP : SB_INST_CALL; + sec->insts[idx++].addr = elf->start_addr; + } + else if(cinst->type == CMD_JUMP_AT || cinst->type == CMD_CALL_AT) + { + sec->insts[idx].argument = cinst->argument; + sec->insts[idx].inst = (cinst->type == CMD_JUMP_AT) ? SB_INST_JUMP : SB_INST_CALL; + sec->insts[idx++].addr = cinst->addr; + } + else if(cinst->type == CMD_LOAD_AT) + { + struct bin_param_t *bin = &db_find_source_by_id(cmd_file, cinst->identifier)->bin; + sec->insts[idx].inst = SB_INST_LOAD; + sec->insts[idx].addr = cinst->addr; + sec->insts[idx].data = memdup(bin->data, bin->size); + sec->insts[idx++].size = bin->size; + } + else if(cinst->type == CMD_MODE) + { + sec->insts[idx].inst = SB_INST_MODE; + sec->insts[idx++].addr = cinst->argument; + } + else + bug("die\n"); + + cinst = cinst->next; + } + } + } + + return sb; +} + +static void usage(void) +{ + printf("Usage: elftosb [options | file]...\n"); + printf("Options:\n"); + printf(" -?/--help\tDisplay this message\n"); + printf(" -o \tSet output file\n"); + printf(" -c \tSet command file\n"); + printf(" -d/--debug\tEnable debug output\n"); + printf(" -k \tAdd key file\n"); + printf(" -z\t\tAdd zero key\n"); + printf(" --add-key \tAdd single key (hex or usbotp)\n"); + printf(" --real-key \tOverride real key\n"); + printf(" --crypto-iv \tOverride crypto IV\n"); + exit(1); +} + +static struct crypto_key_t g_zero_key = +{ + .method = CRYPTO_KEY, + .u.key = {0} +}; + +int main(int argc, char **argv) +{ + char *cmd_filename = NULL; + char *output_filename = NULL; + struct crypto_key_t real_key; + struct crypto_key_t crypto_iv; + real_key.method = CRYPTO_NONE; + crypto_iv.method = CRYPTO_NONE; + + while(1) + { + static struct option long_options[] = + { + {"help", no_argument, 0, '?'}, + {"debug", no_argument, 0, 'd'}, + {"add-key", required_argument, 0, 'a'}, + {"real-key", required_argument, 0, 'r'}, + {"crypto-iv", required_argument, 0, 'i'}, + {0, 0, 0, 0} + }; + + int c = getopt_long(argc, argv, "?do:c:k:za:", long_options, NULL); + if(c == -1) + break; + switch(c) + { + case 'd': + g_debug = true; + break; + case '?': + usage(); + break; + case 'o': + output_filename = optarg; + break; + case 'c': + cmd_filename = optarg; + break; + case 'k': + { + if(!add_keys_from_file(optarg)) + bug("Cannot keys from %s\n", optarg); + break; + } + case 'z': + { + add_keys(&g_zero_key, 1); + break; + } + case 'a': + case 'r': + case 'i': + { + struct crypto_key_t key; + char *s = optarg; + if(!parse_key(&s, &key)) + bug("Invalid key/iv specified as argument"); + if(*s != 0) + bug("Trailing characters after key/iv specified as argument"); + if(c == 'r') + memcpy(&real_key, &key, sizeof(key)); + else if(c == 'i') + memcpy(&crypto_iv, &key, sizeof(key)); + else + add_keys(&key, 1); + break; + } + default: + abort(); + } + } + + if(!cmd_filename) + bug("You must specify a command file\n"); + if(!output_filename) + bug("You must specify an output file\n"); + + g_extern = &argv[optind]; + g_extern_count = argc - optind; + + if(g_debug) + { + printf("key: %d\n", g_nr_keys); + for(int i = 0; i < g_nr_keys; i++) + { + printf(" "); + print_key(&g_key_array[i], true); + } + + for(int i = 0; i < g_extern_count; i++) + printf("extern(%d)=%s\n", i, g_extern[i]); + } + + struct cmd_file_t *cmd_file = db_parse_file(cmd_filename); + struct sb_file_t *sb_file = apply_cmd_file(cmd_file); + db_free(cmd_file); + + if(real_key.method == CRYPTO_KEY) + { + sb_file->override_real_key = true; + memcpy(sb_file->real_key, real_key.u.key, 16); + } + if(crypto_iv.method == CRYPTO_KEY) + { + sb_file->override_crypto_iv = true; + memcpy(sb_file->crypto_iv, crypto_iv.u.key, 16); + } + + /* fill with default parameters since there is no command file support for them */ + sb_file->drive_tag = 0; + sb_file->first_boot_sec_id = sb_file->sections[0].identifier; + sb_file->flags = 0; + sb_file->minor_version = 1; + + sb_write_file(sb_file, output_filename); + sb_free(sb_file); + clear_keys(); + + return 0; +} diff --git a/utils/imxtools/sbtools/fuze+_key_file.txt b/utils/imxtools/sbtools/fuze+_key_file.txt new file mode 100644 index 0000000000..a965e715f7 --- /dev/null +++ b/utils/imxtools/sbtools/fuze+_key_file.txt @@ -0,0 +1 @@ +00000000000000000000000000000000 diff --git a/utils/imxtools/sbtools/misc.c b/utils/imxtools/sbtools/misc.c new file mode 100644 index 0000000000..fce71ae0cd --- /dev/null +++ b/utils/imxtools/sbtools/misc.c @@ -0,0 +1,265 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 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" + +bool g_debug = false; + +/** + * Misc + */ + +void *memdup(const void *p, size_t len) +{ + void *cpy = xmalloc(len); + memcpy(cpy, p, len); + return cpy; +} + +void generate_random_data(void *buf, size_t sz) +{ + size_t i = 0; + unsigned char* p = (unsigned char*)buf; + while(i++ < sz) + *p++ = rand(); +} + +void *xmalloc(size_t s) +{ + void * r = malloc(s); + if(!r) bugp("malloc"); + return r; +} + +int convxdigit(char digit, byte *val) +{ + if(digit >= '0' && digit <= '9') + { + *val = digit - '0'; + return 0; + } + else if(digit >= 'A' && digit <= 'F') + { + *val = digit - 'A' + 10; + return 0; + } + else if(digit >= 'a' && digit <= 'f') + { + *val = digit - 'a' + 10; + return 0; + } + else + return 1; +} + +/* helper function to augment an array, free old array */ +void *augment_array(void *arr, size_t elem_sz, size_t cnt, void *aug, size_t aug_cnt) +{ + void *p = xmalloc(elem_sz * (cnt + aug_cnt)); + memcpy(p, arr, elem_sz * cnt); + memcpy(p + elem_sz * cnt, aug, elem_sz * aug_cnt); + free(arr); + return p; +} + +/** + * Key file parsing + */ +int g_nr_keys; +key_array_t g_key_array; + +bool parse_key(char **pstr, struct crypto_key_t *key) +{ + char *str = *pstr; + /* ignore spaces */ + while(isspace(*str)) + str++; + /* CRYPTO_KEY: 32 hex characters + * CRYPTO_USBOTP: usbotp(vid:pid) where vid and pid are hex numbers */ + if(isxdigit(str[0])) + { + if(strlen(str) < 32) + return false; + for(int j = 0; j < 16; j++) + { + byte a, b; + if(convxdigit(str[2 * j], &a) || convxdigit(str[2 * j + 1], &b)) + return false; + key->u.key[j] = (a << 4) | b; + } + /* skip key */ + *pstr = str + 32; + key->method = CRYPTO_KEY; + return true; + } + else + { + const char *prefix = "usbotp("; + if(strlen(str) < strlen(prefix)) + return false; + if(strncmp(str, prefix, strlen(prefix)) != 0) + return false; + str += strlen(prefix); + /* vid */ + long vid = strtol(str, &str, 16); + if(vid < 0 || vid > 0xffff) + return false; + if(*str++ != ':') + return false; + /* pid */ + long pid = strtol(str, &str, 16); + if(pid < 0 || pid > 0xffff) + return false; + if(*str++ != ')') + return false; + *pstr = str; + key->method = CRYPTO_USBOTP; + key->u.vid_pid = vid << 16 | pid; + return true; + } +} + +void add_keys(key_array_t ka, int kac) +{ + key_array_t new_ka = xmalloc((g_nr_keys + kac) * sizeof(struct crypto_key_t)); + memcpy(new_ka, g_key_array, g_nr_keys * sizeof(struct crypto_key_t)); + memcpy(new_ka + g_nr_keys, ka, kac * sizeof(struct crypto_key_t)); + free(g_key_array); + g_key_array = new_ka; + g_nr_keys += kac; +} + +void clear_keys() +{ + free(g_key_array); + g_nr_keys = 0; + g_key_array = NULL; +} + +bool add_keys_from_file(const char *key_file) +{ + int size; + FILE *fd = fopen(key_file, "r"); + if(fd == NULL) + { + if(g_debug) + perror("cannot open key file"); + return false; + } + fseek(fd, 0, SEEK_END); + size = ftell(fd); + fseek(fd, 0, SEEK_SET); + char *buf = xmalloc(size + 1); + if(fread(buf, 1, size, fd) != (size_t)size) + { + if(g_debug) + perror("Cannot read key file"); + fclose(fd); + return false; + } + buf[size] = 0; + fclose(fd); + + if(g_debug) + printf("Parsing key file '%s'...\n", key_file); + char *p = buf; + while(1) + { + struct crypto_key_t k; + /* parse key */ + if(!parse_key(&p, &k)) + { + if(g_debug) + printf("invalid key file\n"); + return false; + } + if(g_debug) + { + printf("Add key: "); + print_key(&k, true); + } + add_keys(&k, 1); + /* request at least one space character before next key, or end of file */ + if(*p != 0 && !isspace(*p)) + { + if(g_debug) + printf("invalid key file\n"); + return false; + } + /* skip whitespace */ + while(isspace(*p)) + p++; + if(*p == 0) + break; + } + free(buf); + return true; +} + +void print_hex(byte *data, int len, bool newline) +{ + for(int i = 0; i < len; i++) + printf("%02X ", data[i]); + if(newline) + printf("\n"); +} + +void print_key(struct crypto_key_t *key, bool newline) +{ + switch(key->method) + { + case CRYPTO_KEY: + print_hex(key->u.key, 16, false); + break; + case CRYPTO_USBOTP: + printf("USB-OTP(%04x:%04x)", key->u.vid_pid >> 16, key->u.vid_pid & 0xffff); + break; + case CRYPTO_NONE: + printf("none"); + break; + } + if(newline) + printf("\n"); +} + +char OFF[] = { 0x1b, 0x5b, 0x31, 0x3b, '0', '0', 0x6d, '\0' }; + +char GREY[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '0', 0x6d, '\0' }; +char RED[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '1', 0x6d, '\0' }; +char GREEN[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '2', 0x6d, '\0' }; +char YELLOW[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '3', 0x6d, '\0' }; +char BLUE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '4', 0x6d, '\0' }; + +static bool g_color_enable = true; + +void enable_color(bool enable) +{ + g_color_enable = enable; +} + +void color(color_t c) +{ + if(g_color_enable) + printf("%s", (char *)c); +} diff --git a/utils/imxtools/sbtools/misc.h b/utils/imxtools/sbtools/misc.h new file mode 100644 index 0000000000..4df9bbe957 --- /dev/null +++ b/utils/imxtools/sbtools/misc.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 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 __MISC_H__ +#define __MISC_H__ + +#include +#include "crypto.h" + +#define _STR(a) #a +#define STR(a) _STR(a) + +#define bug(...) do { fprintf(stderr,"["__FILE__":"STR(__LINE__)"]ERROR: "__VA_ARGS__); exit(1); } while(0) +#define bugp(...) do { fprintf(stderr, __VA_ARGS__); perror(" "); exit(1); } while(0) + +#define ROUND_UP(val, round) ((((val) + (round) - 1) / (round)) * (round)) + +extern bool g_debug; + +typedef struct crypto_key_t *key_array_t; +int g_nr_keys; +key_array_t g_key_array; + +void *memdup(const void *p, size_t len); +void *augment_array(void *arr, size_t elem_sz, size_t cnt, void *aug, size_t aug_cnt); +void generate_random_data(void *buf, size_t sz); +void *xmalloc(size_t s); +int convxdigit(char digit, byte *val); +void print_hex(byte *data, int len, bool newline); +void add_keys(key_array_t ka, int kac); +bool parse_key(char **str, struct crypto_key_t *key); +bool add_keys_from_file(const char *key_file); +void print_key(struct crypto_key_t *key, bool newline); +void clear_keys(); + +typedef char color_t[]; + +extern color_t OFF, GREY, RED, GREEN, YELLOW, BLUE; +void color(color_t c); +void enable_color(bool enable); + +#endif /* __MISC_H__ */ diff --git a/utils/imxtools/sbtools/sb.c b/utils/imxtools/sbtools/sb.c new file mode 100644 index 0000000000..9b97509491 --- /dev/null +++ b/utils/imxtools/sbtools/sb.c @@ -0,0 +1,1224 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2011 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 "sb.h" + +static void fill_gaps(struct sb_file_t *sb) +{ + for(int i = 0; i < sb->nr_sections; i++) + { + struct sb_section_t *sec = &sb->sections[i]; + for(int j = 0; j < sec->nr_insts; j++) + { + struct sb_inst_t *inst = &sec->insts[j]; + if(inst->inst != SB_INST_LOAD) + continue; + inst->padding_size = ROUND_UP(inst->size, BLOCK_SIZE) - inst->size; + /* emulate elftosb2 behaviour: generate 15 bytes (that's a safe maximum) */ + inst->padding = xmalloc(15); + generate_random_data(inst->padding, 15); + } + } +} + +static void compute_sb_offsets(struct sb_file_t *sb) +{ + sb->image_size = 0; + /* sb header */ + sb->image_size += sizeof(struct sb_header_t) / BLOCK_SIZE; + /* sections headers */ + sb->image_size += sb->nr_sections * sizeof(struct sb_section_header_t) / BLOCK_SIZE; + /* key dictionary */ + sb->image_size += g_nr_keys * sizeof(struct sb_key_dictionary_entry_t) / BLOCK_SIZE; + /* sections */ + for(int i = 0; i < sb->nr_sections; i++) + { + /* each section has a preliminary TAG command */ + sb->image_size += sizeof(struct sb_instruction_tag_t) / BLOCK_SIZE; + /* we might need to pad the section so compute next alignment */ + uint32_t alignment = BLOCK_SIZE; + if((i + 1) < sb->nr_sections) + alignment = sb->sections[i + 1].alignment; + alignment /= BLOCK_SIZE; /* alignment in block sizes */ + + struct sb_section_t *sec = &sb->sections[i]; + sec->sec_size = 0; + + if(g_debug) + { + printf("%s section 0x%08x", sec->is_data ? "Data" : "Boot", + sec->identifier); + if(sec->is_cleartext) + printf(" (cleartext)"); + printf("\n"); + } + + sec->file_offset = sb->image_size; + for(int j = 0; j < sec->nr_insts; j++) + { + struct sb_inst_t *inst = &sec->insts[j]; + if(inst->inst == SB_INST_CALL || inst->inst == SB_INST_JUMP) + { + if(g_debug) + printf(" %s | addr=0x%08x | arg=0x%08x\n", + inst->inst == SB_INST_CALL ? "CALL" : "JUMP", inst->addr, inst->argument); + sb->image_size += sizeof(struct sb_instruction_call_t) / BLOCK_SIZE; + sec->sec_size += sizeof(struct sb_instruction_call_t) / BLOCK_SIZE; + } + else if(inst->inst == SB_INST_FILL) + { + if(g_debug) + printf(" FILL | addr=0x%08x | len=0x%08x | pattern=0x%08x\n", + inst->addr, inst->size, inst->pattern); + sb->image_size += sizeof(struct sb_instruction_fill_t) / BLOCK_SIZE; + sec->sec_size += sizeof(struct sb_instruction_fill_t) / BLOCK_SIZE; + } + else if(inst->inst == SB_INST_LOAD) + { + if(g_debug) + printf(" LOAD | addr=0x%08x | len=0x%08x\n", inst->addr, inst->size); + /* load header */ + sb->image_size += sizeof(struct sb_instruction_load_t) / BLOCK_SIZE; + sec->sec_size += sizeof(struct sb_instruction_load_t) / BLOCK_SIZE; + /* data + alignment */ + sb->image_size += (inst->size + inst->padding_size) / BLOCK_SIZE; + sec->sec_size += (inst->size + inst->padding_size) / BLOCK_SIZE; + } + else if(inst->inst == SB_INST_MODE) + { + if(g_debug) + printf(" MODE | mod=0x%08x\n", inst->addr); + sb->image_size += sizeof(struct sb_instruction_mode_t) / BLOCK_SIZE; + sec->sec_size += sizeof(struct sb_instruction_mode_t) / BLOCK_SIZE; + } + else if(inst->inst == SB_INST_DATA) + { + if(g_debug) + printf(" DATA | size=0x%08x\n", inst->size); + sb->image_size += ROUND_UP(inst->size, BLOCK_SIZE) / BLOCK_SIZE; + sec->sec_size += ROUND_UP(inst->size, BLOCK_SIZE) / BLOCK_SIZE; + } + else + { + if(g_debug) + printf("die on inst %d\n", inst->inst); + } + } + /* we need to make sure next section starts on the right alignment. + * Since each section starts with a boot tag, we thus need to ensure + * that this sections ends at adress X such that X+BLOCK_SIZE is + * a multiple of the alignment. + * For data sections, we just add random data, otherwise we add nops */ + uint32_t missing_sz = alignment - ((sb->image_size + 1) % alignment); + if(missing_sz != alignment) + { + struct sb_inst_t *aug_insts; + int nr_aug_insts = 0; + + if(sb->sections[i].is_data) + { + nr_aug_insts = 1; + aug_insts = xmalloc(sizeof(struct sb_inst_t)); + memset(aug_insts, 0, sizeof(struct sb_inst_t)); + aug_insts[0].inst = SB_INST_DATA; + aug_insts[0].size = missing_sz * BLOCK_SIZE; + aug_insts[0].data = xmalloc(missing_sz * BLOCK_SIZE); + generate_random_data(aug_insts[0].data, missing_sz * BLOCK_SIZE); + if(g_debug) + printf(" DATA | size=0x%08x\n", aug_insts[0].size); + } + else + { + nr_aug_insts = missing_sz; + aug_insts = xmalloc(sizeof(struct sb_inst_t) * nr_aug_insts); + memset(aug_insts, 0, sizeof(struct sb_inst_t) * nr_aug_insts); + for(int j = 0; j < nr_aug_insts; j++) + { + aug_insts[j].inst = SB_INST_NOP; + if(g_debug) + printf(" NOOP\n"); + } + } + + sb->sections[i].insts = augment_array(sb->sections[i].insts, sizeof(struct sb_inst_t), + sb->sections[i].nr_insts, aug_insts, nr_aug_insts); + sb->sections[i].nr_insts += nr_aug_insts; + free(aug_insts); + + /* augment image and section size */ + sb->image_size += missing_sz; + sec->sec_size += missing_sz; + } + } + /* final signature */ + sb->image_size += 2; +} + +static uint64_t generate_timestamp() +{ + struct tm tm_base; + memset(&tm_base, 0, sizeof(tm_base)); + /* 2000/1/1 0:00:00 */ + tm_base.tm_mday = 1; + tm_base.tm_year = 100; + time_t t = time(NULL) - mktime(&tm_base); + return (uint64_t)t * 1000000L; +} + +static uint16_t swap16(uint16_t t) +{ + return (t << 8) | (t >> 8); +} + +static void fix_version(struct sb_version_t *ver) +{ + ver->major = swap16(ver->major); + ver->minor = swap16(ver->minor); + ver->revision = swap16(ver->revision); +} + +static void produce_sb_header(struct sb_file_t *sb, struct sb_header_t *sb_hdr) +{ + struct sha_1_params_t sha_1_params; + + sb_hdr->signature[0] = 'S'; + sb_hdr->signature[1] = 'T'; + sb_hdr->signature[2] = 'M'; + sb_hdr->signature[3] = 'P'; + sb_hdr->major_ver = IMAGE_MAJOR_VERSION; + sb_hdr->minor_ver = IMAGE_MINOR_VERSION; + sb_hdr->flags = 0; + sb_hdr->image_size = sb->image_size; + sb_hdr->header_size = sizeof(struct sb_header_t) / BLOCK_SIZE; + sb_hdr->first_boot_sec_id = sb->first_boot_sec_id; + sb_hdr->nr_keys = g_nr_keys; + sb_hdr->nr_sections = sb->nr_sections; + sb_hdr->sec_hdr_size = sizeof(struct sb_section_header_t) / BLOCK_SIZE; + sb_hdr->key_dict_off = sb_hdr->header_size + + sb_hdr->sec_hdr_size * sb_hdr->nr_sections; + sb_hdr->first_boot_tag_off = sb_hdr->key_dict_off + + sizeof(struct sb_key_dictionary_entry_t) * sb_hdr->nr_keys / BLOCK_SIZE; + generate_random_data(sb_hdr->rand_pad0, sizeof(sb_hdr->rand_pad0)); + generate_random_data(sb_hdr->rand_pad1, sizeof(sb_hdr->rand_pad1)); + /* Version 1.0 has 6 bytes of random padding, + * Version 1.1 requires the last 4 bytes to be 'sgtl' */ + if(sb->minor_version >= 1) + memcpy(&sb_hdr->rand_pad0[2], "sgtl", 4); + + sb_hdr->timestamp = generate_timestamp(); + sb_hdr->product_ver = sb->product_ver; + fix_version(&sb_hdr->product_ver); + sb_hdr->component_ver = sb->component_ver; + fix_version(&sb_hdr->component_ver); + sb_hdr->drive_tag = sb->drive_tag; + + sha_1_init(&sha_1_params); + sha_1_update(&sha_1_params, &sb_hdr->signature[0], + sizeof(struct sb_header_t) - sizeof(sb_hdr->sha1_header)); + sha_1_finish(&sha_1_params); + sha_1_output(&sha_1_params, sb_hdr->sha1_header); +} + +static void produce_sb_section_header(struct sb_section_t *sec, + struct sb_section_header_t *sec_hdr) +{ + sec_hdr->identifier = sec->identifier; + sec_hdr->offset = sec->file_offset; + sec_hdr->size = sec->sec_size; + sec_hdr->flags = (sec->is_data ? 0 : SECTION_BOOTABLE) + | (sec->is_cleartext ? SECTION_CLEARTEXT : 0); +} + +static uint8_t instruction_checksum(struct sb_instruction_header_t *hdr) +{ + uint8_t sum = 90; + byte *ptr = (byte *)hdr; + for(int i = 1; i < 16; i++) + sum += ptr[i]; + return sum; +} + +static void produce_section_tag_cmd(struct sb_section_t *sec, + struct sb_instruction_tag_t *tag, bool is_last) +{ + tag->hdr.opcode = SB_INST_TAG; + tag->hdr.flags = is_last ? SB_INST_LAST_TAG : 0; + tag->identifier = sec->identifier; + tag->len = sec->sec_size; + tag->flags = (sec->is_data ? 0 : SECTION_BOOTABLE) + | (sec->is_cleartext ? SECTION_CLEARTEXT : 0); + tag->hdr.checksum = instruction_checksum(&tag->hdr); +} + +void produce_sb_instruction(struct sb_inst_t *inst, + struct sb_instruction_common_t *cmd) +{ + memset(cmd, 0, sizeof(struct sb_instruction_common_t)); + cmd->hdr.opcode = inst->inst; + switch(inst->inst) + { + case SB_INST_CALL: + case SB_INST_JUMP: + cmd->addr = inst->addr; + cmd->data = inst->argument; + break; + case SB_INST_FILL: + cmd->addr = inst->addr; + cmd->len = inst->size; + cmd->data = inst->pattern; + break; + case SB_INST_LOAD: + cmd->addr = inst->addr; + cmd->len = inst->size; + cmd->data = crc_continue(crc(inst->data, inst->size), + inst->padding, inst->padding_size); + break; + case SB_INST_MODE: + cmd->data = inst->addr; + break; + case SB_INST_NOP: + break; + default: + if(g_debug) + printf("die on invalid inst %d\n", inst->inst); + } + cmd->hdr.checksum = instruction_checksum(&cmd->hdr); +} + +enum sb_error_t sb_write_file(struct sb_file_t *sb, const char *filename) +{ + struct crypto_key_t real_key; + real_key.method = CRYPTO_KEY; + byte crypto_iv[16]; + byte (*cbc_macs)[16] = xmalloc(16 * g_nr_keys); + /* init CBC-MACs */ + for(int i = 0; i < g_nr_keys; i++) + memset(cbc_macs[i], 0, 16); + + fill_gaps(sb); + compute_sb_offsets(sb); + + generate_random_data(real_key.u.key, 16); + + /* global SHA-1 */ + struct sha_1_params_t file_sha1; + sha_1_init(&file_sha1); + /* produce and write header */ + struct sb_header_t sb_hdr; + produce_sb_header(sb, &sb_hdr); + /* allocate image */ + byte *buf = xmalloc(sb_hdr.image_size * BLOCK_SIZE); + byte *buf_p = buf; + #define write(p, sz) do { memcpy(buf_p, p, sz); buf_p += sz; } while(0) + + sha_1_update(&file_sha1, (byte *)&sb_hdr, sizeof(sb_hdr)); + write(&sb_hdr, sizeof(sb_hdr)); + + memcpy(crypto_iv, &sb_hdr, 16); + + /* update CBC-MACs */ + for(int i = 0; i < g_nr_keys; i++) + crypto_cbc((byte *)&sb_hdr, NULL, sizeof(sb_hdr) / BLOCK_SIZE, &g_key_array[i], + cbc_macs[i], &cbc_macs[i], 1); + + /* produce and write section headers */ + for(int i = 0; i < sb_hdr.nr_sections; i++) + { + struct sb_section_header_t sb_sec_hdr; + produce_sb_section_header(&sb->sections[i], &sb_sec_hdr); + sha_1_update(&file_sha1, (byte *)&sb_sec_hdr, sizeof(sb_sec_hdr)); + write(&sb_sec_hdr, sizeof(sb_sec_hdr)); + /* update CBC-MACs */ + for(int j = 0; j < g_nr_keys; j++) + crypto_cbc((byte *)&sb_sec_hdr, NULL, sizeof(sb_sec_hdr) / BLOCK_SIZE, + &g_key_array[j], cbc_macs[j], &cbc_macs[j], 1); + } + /* produce key dictionary */ + for(int i = 0; i < g_nr_keys; i++) + { + struct sb_key_dictionary_entry_t entry; + memcpy(entry.hdr_cbc_mac, cbc_macs[i], 16); + crypto_cbc(real_key.u.key, entry.key, 1, &g_key_array[i], + crypto_iv, NULL, 1); + + write(&entry, sizeof(entry)); + sha_1_update(&file_sha1, (byte *)&entry, sizeof(entry)); + } + + free(cbc_macs); + + /* HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK */ + /* Image crafting, don't use it unless you understand what you do */ + if(sb->override_real_key) + memcpy(real_key.u.key, sb->real_key, 16); + if(sb->override_crypto_iv) + memcpy(crypto_iv, sb->crypto_iv, 16); + /* KCAH KCAH KCAH KCAH KCAH KCAH KCAH KCAH KCAH KCAH KCAH KCAH KCAH KCAH */ + if(g_debug) + { + printf("Real key: "); + for(int j = 0; j < 16; j++) + printf("%02x", real_key.u.key[j]); + printf("\n"); + printf("IV : "); + for(int j = 0; j < 16; j++) + printf("%02x", crypto_iv[j]); + printf("\n"); + } + /* produce sections data */ + for(int i = 0; i< sb_hdr.nr_sections; i++) + { + /* produce tag command */ + struct sb_instruction_tag_t tag_cmd; + produce_section_tag_cmd(&sb->sections[i], &tag_cmd, (i + 1) == sb_hdr.nr_sections); + if(g_nr_keys > 0) + crypto_cbc((byte *)&tag_cmd, (byte *)&tag_cmd, sizeof(tag_cmd) / BLOCK_SIZE, + &real_key, crypto_iv, NULL, 1); + sha_1_update(&file_sha1, (byte *)&tag_cmd, sizeof(tag_cmd)); + write(&tag_cmd, sizeof(tag_cmd)); + /* produce other commands */ + byte cur_cbc_mac[16]; + memcpy(cur_cbc_mac, crypto_iv, 16); + for(int j = 0; j < sb->sections[i].nr_insts; j++) + { + struct sb_inst_t *inst = &sb->sections[i].insts[j]; + /* command */ + if(inst->inst != SB_INST_DATA) + { + struct sb_instruction_common_t cmd; + produce_sb_instruction(inst, &cmd); + if(g_nr_keys > 0 && !sb->sections[i].is_cleartext) + crypto_cbc((byte *)&cmd, (byte *)&cmd, sizeof(cmd) / BLOCK_SIZE, + &real_key, cur_cbc_mac, &cur_cbc_mac, 1); + sha_1_update(&file_sha1, (byte *)&cmd, sizeof(cmd)); + write(&cmd, sizeof(cmd)); + } + /* data */ + if(inst->inst == SB_INST_LOAD || inst->inst == SB_INST_DATA) + { + uint32_t sz = inst->size + inst->padding_size; + byte *data = xmalloc(sz); + memcpy(data, inst->data, inst->size); + memcpy(data + inst->size, inst->padding, inst->padding_size); + if(g_nr_keys > 0 && !sb->sections[i].is_cleartext) + crypto_cbc(data, data, sz / BLOCK_SIZE, + &real_key, cur_cbc_mac, &cur_cbc_mac, 1); + sha_1_update(&file_sha1, data, sz); + write(data, sz); + free(data); + } + } + } + /* write file SHA-1 */ + byte final_sig[32]; + sha_1_finish(&file_sha1); + sha_1_output(&file_sha1, final_sig); + generate_random_data(final_sig + 20, 12); + if(g_nr_keys > 0) + crypto_cbc(final_sig, final_sig, 2, &real_key, crypto_iv, NULL, 1); + write(final_sig, 32); + + if(buf_p - buf != sb_hdr.image_size * BLOCK_SIZE) + { + if(g_debug) + printf("SB image buffer was not entirely filled !"); + return SB_ERROR; + } + + FILE *fd = fopen(filename, "wb"); + if(fd == NULL) + return SB_OPEN_ERROR; + if(fwrite(buf, sb_hdr.image_size * BLOCK_SIZE, 1, fd) != 1) + { + free(buf); + return SB_WRITE_ERROR; + } + fclose(fd); + free(buf); + + return SB_SUCCESS; +} + +static struct sb_section_t *read_section(bool data_sec, uint32_t id, byte *buf, + int size, const char *indent, void *u, sb_color_printf cprintf, enum sb_error_t *err) +{ + #define printf(c, ...) cprintf(u, false, c, __VA_ARGS__) + #define fatal(e, ...) \ + do { if(err) *err = e; \ + cprintf(u, true, GREY, __VA_ARGS__); \ + sb_free_section(*sec); \ + free(sec); \ + return NULL; } while(0) + + struct sb_section_t *sec = xmalloc(sizeof(struct sb_section_t)); + memset(sec, 0, sizeof(struct sb_section_t)); + sec->identifier = id; + sec->is_data = data_sec; + sec->sec_size = ROUND_UP(size, BLOCK_SIZE) / BLOCK_SIZE; + + if(data_sec) + { + sec->nr_insts = 1; + sec->insts = xmalloc(sizeof(struct sb_inst_t)); + memset(sec->insts, 0, sizeof(struct sb_inst_t)); + sec->insts->inst = SB_INST_DATA; + sec->insts->size = size; + sec->insts->data = memdup(buf, size); + return sec; + } + + /* Pretty print the content */ + int pos = 0; + while(pos < size) + { + struct sb_inst_t inst; + memset(&inst, 0, sizeof(inst)); + + struct sb_instruction_header_t *hdr = (struct sb_instruction_header_t *)&buf[pos]; + inst.inst = hdr->opcode; + + printf(OFF, "%s", indent); + uint8_t checksum = instruction_checksum(hdr); + if(checksum != hdr->checksum) + fatal(SB_CHECKSUM_ERROR, "Bad instruction checksum"); + if(hdr->flags != 0) + { + printf(GREY, "["); + printf(BLUE, "f=%x", hdr->flags); + printf(GREY, "] "); + } + if(hdr->opcode == SB_INST_LOAD) + { + struct sb_instruction_load_t *load = (struct sb_instruction_load_t *)&buf[pos]; + inst.size = load->len; + inst.addr = load->addr; + inst.data = memdup(load + 1, load->len); + + printf(RED, "LOAD"); + printf(OFF, " | "); + printf(BLUE, "addr=0x%08x", load->addr); + printf(OFF, " | "); + printf(GREEN, "len=0x%08x", load->len); + printf(OFF, " | "); + printf(YELLOW, "crc=0x%08x", load->crc); + /* data is padded to 16-byte boundary with random data and crc'ed with it */ + uint32_t computed_crc = crc(&buf[pos + sizeof(struct sb_instruction_load_t)], + ROUND_UP(load->len, 16)); + if(load->crc == computed_crc) + printf(RED, " Ok\n"); + else + { + printf(RED, " Failed (crc=0x%08x)\n", computed_crc); + fatal(SB_CHECKSUM_ERROR, "Instruction data crc error\n"); + } + + pos += load->len + sizeof(struct sb_instruction_load_t); + } + else if(hdr->opcode == SB_INST_FILL) + { + struct sb_instruction_fill_t *fill = (struct sb_instruction_fill_t *)&buf[pos]; + inst.pattern = fill->pattern; + inst.size = fill->len; + inst.addr = fill->addr; + + printf(RED, "FILL"); + printf(OFF, " | "); + printf(BLUE, "addr=0x%08x", fill->addr); + printf(OFF, " | "); + printf(GREEN, "len=0x%08x", fill->len); + printf(OFF, " | "); + printf(YELLOW, "pattern=0x%08x\n", fill->pattern); + + pos += sizeof(struct sb_instruction_fill_t); + } + else if(hdr->opcode == SB_INST_CALL || + hdr->opcode == SB_INST_JUMP) + { + int is_call = (hdr->opcode == SB_INST_CALL); + struct sb_instruction_call_t *call = (struct sb_instruction_call_t *)&buf[pos]; + inst.addr = call->addr; + inst.argument = call->arg; + + if(is_call) + printf(RED, "CALL"); + else + printf(RED, "JUMP"); + printf(OFF, " | "); + printf(BLUE, "addr=0x%08x", call->addr); + printf(OFF, " | "); + printf(GREEN, "arg=0x%08x\n", call->arg); + + pos += sizeof(struct sb_instruction_call_t); + } + else if(hdr->opcode == SB_INST_MODE) + { + struct sb_instruction_mode_t *mode = (struct sb_instruction_mode_t *)hdr; + inst.argument = mode->mode; + + printf(RED, "MODE"); + printf(OFF, " | "); + printf(BLUE, "mod=0x%08x\n", mode->mode); + + pos += sizeof(struct sb_instruction_mode_t); + } + else if(hdr->opcode == SB_INST_NOP) + { + printf(RED, "NOOP\n"); + pos += sizeof(struct sb_instruction_mode_t); + } + else + { + fatal(SB_FORMAT_ERROR, "Unknown instruction %d at address 0x%08lx\n", hdr->opcode, (unsigned long)pos); + break; + } + + sec->insts = augment_array(sec->insts, sizeof(struct sb_inst_t), sec->nr_insts++, &inst, 1); + pos = ROUND_UP(pos, BLOCK_SIZE); + } + + return sec; + #undef printf + #undef fatal +} + +void sb_fill_section_name(char name[5], uint32_t identifier) +{ + name[0] = (identifier >> 24) & 0xff; + name[1] = (identifier >> 16) & 0xff; + name[2] = (identifier >> 8) & 0xff; + name[3] = identifier & 0xff; + for(int i = 0; i < 4; i++) + if(!isprint(name[i])) + name[i] = '_'; + name[4] = 0; +} + +static uint32_t guess_alignment(uint32_t off) +{ + /* find greatest power of two which divides the offset */ + if(off == 0) + return 1; + uint32_t a = 1; + while(off % (2 * a) == 0) + a *= 2; + return a; +} + +struct sb_file_t *sb_read_file(const char *filename, bool raw_mode, void *u, + sb_color_printf cprintf, enum sb_error_t *err) +{ + return sb_read_file_ex(filename, 0, -1, raw_mode, u, cprintf, err); +} + +struct sb_file_t *sb_read_file_ex(const char *filename, size_t offset, size_t size, bool raw_mode, void *u, + sb_color_printf cprintf, enum sb_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(SB_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(SB_READ_ERROR, "Cannot read file\n"); + } + fclose(f); + + struct sb_file_t *ret = sb_read_memory(buf, read_size, raw_mode, u, cprintf, err); + free(buf); + return ret; + + #undef fatal +} + +struct sb_file_t *sb_read_memory(void *_buf, size_t filesize, bool raw_mode, void *u, + sb_color_printf cprintf, enum sb_error_t *err) +{ + struct sb_file_t *sb_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__); \ + sb_free(sb_file); \ + return NULL; } while(0) + #define print_hex(c, p, len, nl) \ + do { printf(c, ""); print_hex(p, len, nl); } while(0) + + struct sha_1_params_t sha_1_params; + sb_file = xmalloc(sizeof(struct sb_file_t)); + memset(sb_file, 0, sizeof(struct sb_file_t)); + struct sb_header_t *sb_header = (struct sb_header_t *)buf; + + sb_file->image_size = sb_header->image_size; + sb_file->minor_version = sb_header->minor_ver; + sb_file->flags = sb_header->flags; + sb_file->drive_tag = sb_header->drive_tag; + sb_file->first_boot_sec_id = sb_header->first_boot_sec_id; + + if(memcmp(sb_header->signature, "STMP", 4) != 0) + fatal(SB_FORMAT_ERROR, "Bad signature\n"); + if(sb_header->image_size * BLOCK_SIZE > filesize) + fatal(SB_FORMAT_ERROR, "File too small (should be at least %d bytes)\n", + sb_header->image_size * BLOCK_SIZE); + if(sb_header->header_size * BLOCK_SIZE != sizeof(struct sb_header_t)) + fatal(SB_FORMAT_ERROR, "Bad header size\n"); + if(sb_header->sec_hdr_size * BLOCK_SIZE != sizeof(struct sb_section_header_t)) + fatal(SB_FORMAT_ERROR, "Bad section header size\n"); + + if(filesize > sb_header->image_size * BLOCK_SIZE) + { + printf(GREY, "[Restrict file size from %lu to %d bytes]\n", filesize, + sb_header->image_size * BLOCK_SIZE); + filesize = sb_header->image_size * BLOCK_SIZE; + } + + printf(BLUE, "Basic info:\n"); + printf(GREEN, " SB version: "); + printf(YELLOW, "%d.%d\n", sb_header->major_ver, sb_header->minor_ver); + printf(GREEN, " Header SHA-1: "); + byte *hdr_sha1 = sb_header->sha1_header; + print_hex(YELLOW, hdr_sha1, 20, false); + /* Check SHA1 sum */ + byte computed_sha1[20]; + sha_1_init(&sha_1_params); + sha_1_update(&sha_1_params, &sb_header->signature[0], + sizeof(struct sb_header_t) - sizeof(sb_header->sha1_header)); + sha_1_finish(&sha_1_params); + sha_1_output(&sha_1_params, computed_sha1); + if(memcmp(hdr_sha1, computed_sha1, 20) == 0) + printf(RED, " Ok\n"); + else + printf(RED, " Failed\n"); + printf(GREEN, " Flags: "); + printf(YELLOW, "%x\n", sb_header->flags); + printf(GREEN, " Total file size : "); + printf(YELLOW, "%ld\n", filesize); + + /* Sizes and offsets */ + printf(BLUE, "Sizes and offsets:\n"); + printf(GREEN, " # of encryption keys = "); + printf(YELLOW, "%d\n", sb_header->nr_keys); + printf(GREEN, " # of sections = "); + printf(YELLOW, "%d\n", sb_header->nr_sections); + + /* Versions */ + printf(BLUE, "Versions\n"); + + printf(GREEN, " Random 1: "); + print_hex(YELLOW, sb_header->rand_pad0, sizeof(sb_header->rand_pad0), true); + printf(GREEN, " Random 2: "); + print_hex(YELLOW, sb_header->rand_pad1, sizeof(sb_header->rand_pad1), true); + + uint64_t micros = sb_header->timestamp; + time_t seconds = (micros / (uint64_t)1000000L); + struct tm tm_base; + memset(&tm_base, 0, sizeof(tm_base)); + /* 2000/1/1 0:00:00 */ + tm_base.tm_mday = 1; + tm_base.tm_year = 100; + seconds += mktime(&tm_base); + struct tm *time = gmtime(&seconds); + printf(GREEN, " Creation date/time = "); + printf(YELLOW, "%s", asctime(time)); + + struct sb_version_t product_ver = sb_header->product_ver; + fix_version(&product_ver); + struct sb_version_t component_ver = sb_header->component_ver; + fix_version(&component_ver); + + memcpy(&sb_file->product_ver, &product_ver, sizeof(product_ver)); + memcpy(&sb_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", sb_header->drive_tag); + printf(GREEN, " First boot tag offset = "); + printf(YELLOW, "%x\n", sb_header->first_boot_tag_off); + printf(GREEN, " First boot section ID = "); + printf(YELLOW, "0x%08x\n", sb_header->first_boot_sec_id); + + /* encryption cbc-mac */ + byte real_key[16]; + bool valid_key = false; /* false until a matching key was found */ + + if(sb_header->nr_keys > 0) + { + byte (*cbcmacs)[16] = xmalloc(16 * g_nr_keys); + printf(BLUE, "Encryption keys\n"); + for(int i = 0; i < g_nr_keys; i++) + { + printf(RED, " Key %d: ", i); + printf(YELLOW, ""); + print_key(&g_key_array[i], true); + printf(GREEN, " CBC-MAC: "); + /* check it */ + byte zero[16]; + memset(zero, 0, 16); + int ret = crypto_cbc(buf, NULL, sb_header->header_size + sb_header->nr_sections, + &g_key_array[i], zero, &cbcmacs[i], 1); + if(ret != CRYPTO_ERROR_SUCCESS) + { + free(cbcmacs); + fatal(SB_FIRST_CRYPTO_ERROR + ret, "Crypto error: %d", ret); + } + print_hex(YELLOW, cbcmacs[i], 16, true); + } + + printf(BLUE, "DEK\n"); + for(int i = 0; i < sb_header->nr_keys; i++) + { + printf(RED, " Entry %d\n", i); + uint32_t ofs = sizeof(struct sb_header_t) + + sizeof(struct sb_section_header_t) * sb_header->nr_sections + + sizeof(struct sb_key_dictionary_entry_t) * i; + struct sb_key_dictionary_entry_t *dict_entry = + (struct sb_key_dictionary_entry_t *)&buf[ofs]; + /* cbc mac */ + printf(GREEN, " Encrypted key: "); + print_hex(YELLOW, dict_entry->key, 16, true); + printf(GREEN, " CBC-MAC : "); + print_hex(YELLOW, dict_entry->hdr_cbc_mac, 16, false); + /* check it */ + int idx = 0; + while(idx < g_nr_keys && memcmp(dict_entry->hdr_cbc_mac, cbcmacs[idx], 16) != 0) + idx++; + if(idx != g_nr_keys) + { + printf(RED, " Match\n"); + /* decrypt */ + byte decrypted_key[16]; + byte iv[16]; + memcpy(iv, buf, 16); /* uses the first 16-bytes of SHA-1 sig as IV */ + int ret = crypto_cbc(dict_entry->key, decrypted_key, 1, &g_key_array[idx], iv, NULL, 0); + if(ret != CRYPTO_ERROR_SUCCESS) + { + free(cbcmacs); + fatal(SB_FIRST_CRYPTO_ERROR + ret, "Crypto error: %d\n", ret); + } + printf(GREEN, " Decrypted key: "); + print_hex(YELLOW, decrypted_key, 16, false); + if(valid_key) + { + if(memcmp(real_key, decrypted_key, 16) == 0) + printf(RED, " Cross-Check Ok"); + else + printf(RED, " Cross-Check Failed"); + } + else + { + memcpy(real_key, decrypted_key, 16); + valid_key = true; + } + printf(OFF, "\n"); + } + else + printf(RED, " Don't Match\n"); + } + + free(cbcmacs); + + if(!valid_key) + fatal(SB_NO_VALID_KEY, "No valid key found\n"); + + if(getenv("SB_REAL_KEY") != 0) + { + struct crypto_key_t k; + char *env = getenv("SB_REAL_KEY"); + if(!parse_key(&env, &k) || *env) + fatal(SB_ERROR, "Invalid SB_REAL_KEY\n"); + memcpy(real_key, k.u.key, 16); + } + + printf(RED, " Summary:\n"); + printf(GREEN, " Real key: "); + print_hex(YELLOW, real_key, 16, true); + printf(GREEN, " IV : "); + print_hex(YELLOW, buf, 16, true); + + sb_file->override_real_key = true; + memcpy(sb_file->real_key, real_key, 16); + sb_file->override_crypto_iv = true; + memcpy(sb_file->crypto_iv, buf, 16); + } + + /* sections */ + if(!raw_mode) + { + sb_file->nr_sections = sb_header->nr_sections; + sb_file->sections = xmalloc(sb_file->nr_sections * sizeof(struct sb_section_t)); + memset(sb_file->sections, 0, sb_file->nr_sections * sizeof(struct sb_section_t)); + printf(BLUE, "Sections\n"); + for(int i = 0; i < sb_header->nr_sections; i++) + { + uint32_t ofs = sb_header->header_size * BLOCK_SIZE + i * sizeof(struct sb_section_header_t); + struct sb_section_header_t *sec_hdr = (struct sb_section_header_t *)&buf[ofs]; + + char name[5]; + sb_fill_section_name(name, sec_hdr->identifier); + int pos = sec_hdr->offset * BLOCK_SIZE; + int size = sec_hdr->size * BLOCK_SIZE; + int data_sec = !(sec_hdr->flags & SECTION_BOOTABLE); + int encrypted = !(sec_hdr->flags & SECTION_CLEARTEXT) && sb_header->nr_keys > 0; + + printf(GREEN, " Section "); + printf(YELLOW, "'%s'\n", name); + printf(GREEN, " pos = "); + printf(YELLOW, "%8x - %8x\n", pos, pos+size); + printf(GREEN, " len = "); + printf(YELLOW, "%8x\n", size); + printf(GREEN, " flags = "); + printf(YELLOW, "%8x", sec_hdr->flags); + if(data_sec) + printf(RED, " Data Section"); + else + printf(RED, " Boot Section"); + if(encrypted) + printf(RED, " (Encrypted)"); + printf(OFF, "\n"); + + /* save it */ + byte *sec = xmalloc(size); + if(encrypted) + cbc_mac(buf + pos, sec, size / BLOCK_SIZE, real_key, buf, NULL, 0); + else + memcpy(sec, buf + pos, size); + + struct sb_section_t *s = read_section(data_sec, sec_hdr->identifier, + sec, size, " ", u, cprintf, err); + if(s) + { + s->is_cleartext = !encrypted; + s->alignment = guess_alignment(pos); + memcpy(&sb_file->sections[i], s, sizeof(struct sb_section_t)); + free(s); + } + else + fatal(*err, "Error reading section\n"); + + free(sec); + } + } + else + { + /* advanced raw mode */ + printf(BLUE, "Commands\n"); + uint32_t offset = sb_header->first_boot_tag_off * BLOCK_SIZE; + byte iv[16]; + const char *indent = " "; + while(true) + { + /* restart with IV */ + memcpy(iv, buf, 16); + byte cmd[BLOCK_SIZE]; + if(sb_header->nr_keys > 0) + cbc_mac(buf + offset, cmd, 1, real_key, iv, &iv, 0); + else + memcpy(cmd, buf + offset, BLOCK_SIZE); + struct sb_instruction_header_t *hdr = (struct sb_instruction_header_t *)cmd; + printf(OFF, "%s", indent); + uint8_t checksum = instruction_checksum(hdr); + if(checksum != hdr->checksum) + printf(GREY, "[Bad checksum']"); + + if(hdr->opcode == SB_INST_NOP) + { + printf(RED, "NOOP\n"); + offset += BLOCK_SIZE; + } + else if(hdr->opcode == SB_INST_TAG) + { + struct sb_instruction_tag_t *tag = (struct sb_instruction_tag_t *)hdr; + printf(RED, "BTAG"); + printf(OFF, " | "); + printf(BLUE, "sec=0x%08x", tag->identifier); + printf(OFF, " | "); + printf(GREEN, "cnt=0x%08x", tag->len); + printf(OFF, " | "); + printf(YELLOW, "flg=0x%08x", tag->flags); + if(tag->hdr.flags & SB_INST_LAST_TAG) + { + printf(OFF, " | "); + printf(RED, " Last section"); + } + printf(OFF, "\n"); + offset += sizeof(struct sb_instruction_tag_t); + + char name[5]; + sb_fill_section_name(name, tag->identifier); + int pos = offset; + int size = tag->len * BLOCK_SIZE; + int data_sec = !(tag->flags & SECTION_BOOTABLE); + int encrypted = !(tag->flags & SECTION_CLEARTEXT) && sb_header->nr_keys > 0; + + printf(GREEN, "%sSection ", indent); + printf(YELLOW, "'%s'\n", name); + printf(GREEN, "%s pos = ", indent); + printf(YELLOW, "%8x - %8x\n", pos, pos+size); + printf(GREEN, "%s len = ", indent); + printf(YELLOW, "%8x\n", size); + printf(GREEN, "%s flags = ", indent); + printf(YELLOW, "%8x", tag->flags); + if(data_sec) + printf(RED, " Data Section"); + else + printf(RED, " Boot Section"); + if(encrypted) + printf(RED, " (Encrypted)"); + printf(OFF, "\n"); + + /* save it */ + byte *sec = xmalloc(size); + if(encrypted) + cbc_mac(buf + pos, sec, size / BLOCK_SIZE, real_key, buf, NULL, 0); + else + memcpy(sec, buf + pos, size); + + struct sb_section_t *s = read_section(data_sec, tag->identifier, + sec, size, " ", u, cprintf, err); + if(s) + { + s->is_cleartext = !encrypted; + s->alignment = guess_alignment(pos); + sb_file->sections = augment_array(sb_file->sections, + sizeof(struct sb_section_t), sb_file->nr_sections++, + s, 1); + free(s); + } + else + fatal(*err, "Error reading section\n"); + free(sec); + + /* last one ? */ + if(tag->hdr.flags & SB_INST_LAST_TAG) + break; + offset += size; + } + else + { + fatal(SB_FORMAT_ERROR, "Unknown instruction %d at address 0x%08lx\n", hdr->opcode, (long)offset); + break; + } + } + } + + /* final signature */ + printf(BLUE, "Final signature:\n"); + byte decrypted_block[32]; + if(sb_header->nr_keys > 0) + { + printf(GREEN, " Encrypted SHA-1:\n"); + byte *encrypted_block = &buf[filesize - 32]; + printf(OFF, " "); + print_hex(YELLOW, encrypted_block, 16, true); + printf(OFF, " "); + print_hex(YELLOW, encrypted_block + 16, 16, true); + /* decrypt it */ + cbc_mac(encrypted_block, decrypted_block, 2, real_key, buf, NULL, 0); + } + else + memcpy(decrypted_block, &buf[filesize - 32], 32); + printf(GREEN, " File SHA-1:\n "); + print_hex(YELLOW, decrypted_block, 20, false); + /* check it */ + sha_1_init(&sha_1_params); + sha_1_update(&sha_1_params, buf, filesize - 32); + sha_1_finish(&sha_1_params); + sha_1_output(&sha_1_params, computed_sha1); + if(memcmp(decrypted_block, computed_sha1, 20) == 0) + printf(RED, " Ok\n"); + else + { + printf(RED, " Failed\n"); + fatal(SB_CHECKSUM_ERROR, "File SHA-1 error\n"); + } + + return sb_file; + #undef printf + #undef fatal + #undef print_hex +} + +void sb_free_instruction(struct sb_inst_t inst) +{ + free(inst.padding); + free(inst.data); +} + +void sb_free_section(struct sb_section_t sec) +{ + for(int j = 0; j < sec.nr_insts; j++) + sb_free_instruction(sec.insts[j]); + free(sec.insts); +} + +void sb_free(struct sb_file_t *file) +{ + if(!file) return; + + for(int i = 0; i < file->nr_sections; i++) + sb_free_section(file->sections[i]); + + free(file->sections); + free(file); +} + +void sb_dump(struct sb_file_t *file, void *u, sb_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 + + printf(HEADER, "SB File\n"); + printf(TREE, "+-"); + printf(HEADER, "Version: "); + printf(TEXT, "1.%d\n", file->minor_version); + printf(TREE, "+-"); + printf(HEADER, "Flags: "); + printf(TEXT, "%x\n", file->flags); + printf(TREE, "+-"); + printf(HEADER, "Drive Tag: "); + printf(TEXT, "%x\n", file->drive_tag); + printf(TREE, "+-"); + printf(HEADER, "First Boot Section ID: "); + char name[5]; + sb_fill_section_name(name, file->first_boot_sec_id); + printf(TEXT, "%08x (%s)\n", file->first_boot_sec_id, name); + + if(file->override_real_key) + { + printf(TREE, "+-"); + printf(HEADER, "Real key: "); + print_hex(TEXT, file->real_key, 16, true); + } + if(file->override_crypto_iv) + { + printf(TREE, "+-"); + printf(HEADER, "IV : "); + print_hex(TEXT, file->crypto_iv, 16, true); + } + printf(TREE, "+-"); + printf(HEADER, "Product Version: "); + printf(TEXT, "%X.%X.%X\n", file->product_ver.major, file->product_ver.minor, + file->product_ver.revision); + printf(TREE, "+-"); + printf(HEADER, "Component Version: "); + printf(TEXT, "%X.%X.%X\n", file->component_ver.major, file->component_ver.minor, + file->component_ver.revision); + + for(int i = 0; i < file->nr_sections; i++) + { + struct sb_section_t *sec = &file->sections[i]; + printf(TREE, "+-"); + printf(HEADER, "Section\n"); + printf(TREE,"| +-"); + printf(HEADER, "Identifier: "); + sb_fill_section_name(name, sec->identifier); + printf(TEXT, "%08x (%s)\n", sec->identifier, name); + printf(TREE, "| +-"); + printf(HEADER, "Type: "); + printf(TEXT, "%s (%s)\n", sec->is_data ? "Data Section" : "Boot Section", + sec->is_cleartext ? "Cleartext" : "Encrypted"); + printf(TREE, "| +-"); + printf(HEADER, "Alignment: "); + printf(TEXT, "%d (bytes)\n", sec->alignment); + printf(TREE, "| +-"); + printf(HEADER, "Instructions\n"); + for(int j = 0; j < sec->nr_insts; j++) + { + struct sb_inst_t *inst = &sec->insts[j]; + printf(TREE, "| | +-"); + switch(inst->inst) + { + case SB_INST_DATA: + printf(HEADER, "DATA"); + printf(SEP, " | "); + printf(TEXT, "size=0x%08x\n", inst->size); + break; + case SB_INST_CALL: + case SB_INST_JUMP: + printf(HEADER, "%s", inst->inst == SB_INST_CALL ? "CALL" : "JUMP"); + printf(SEP, " | "); + printf(TEXT, "addr=0x%08x", inst->addr); + printf(SEP, " | "); + printf(TEXT2, "arg=0x%08x\n", inst->argument); + break; + case SB_INST_LOAD: + printf(HEADER, "LOAD"); + printf(SEP, " | "); + printf(TEXT, "addr=0x%08x", inst->addr); + printf(SEP, " | "); + printf(TEXT2, "len=0x%08x\n", inst->size); + break; + case SB_INST_FILL: + printf(HEADER, "FILL"); + printf(SEP, " | "); + printf(TEXT, "addr=0x%08x", inst->addr); + printf(SEP, " | "); + printf(TEXT2, "len=0x%08x", inst->size); + printf(SEP, " | "); + printf(TEXT2, "pattern=0x%08x\n", inst->pattern); + break; + case SB_INST_MODE: + printf(HEADER, "MODE"); + printf(SEP, " | "); + printf(TEXT, "mod=0x%08x\n", inst->addr); + break; + case SB_INST_NOP: + printf(HEADER, "NOOP\n"); + break; + default: + printf(GREY, "[Unknown instruction %x]\n", inst->inst); + } + } + } + + #undef printf + #undef print_hex +} diff --git a/utils/imxtools/sbtools/sb.h b/utils/imxtools/sbtools/sb.h new file mode 100644 index 0000000000..74d86f303a --- /dev/null +++ b/utils/imxtools/sbtools/sb.h @@ -0,0 +1,243 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2011 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 __SB_H__ +#define __SB_H__ + +#include +#include + +#include "misc.h" + +#define BLOCK_SIZE 16 + +/* All fields are in big-endian BCD */ +struct sb_version_t +{ + uint16_t major; + uint16_t pad0; + uint16_t minor; + uint16_t pad1; + uint16_t revision; + uint16_t pad2; +}; + +struct sb_header_t +{ + uint8_t sha1_header[20]; /* SHA-1 of the rest of the header */ + uint8_t signature[4]; /* Signature "STMP" */ + uint8_t major_ver; /* Should be 1 */ + uint8_t minor_ver; /* Should be 1 */ + uint16_t flags; + uint32_t image_size; /* In blocks (=16bytes) */ + uint32_t first_boot_tag_off; /* Offset in blocks */ + uint32_t first_boot_sec_id; /* First bootable section ID */ + uint16_t nr_keys; /* Number of encryption keys */ + uint16_t key_dict_off; /* Offset to key dictionary (in blocks) */ + uint16_t header_size; /* In blocks */ + uint16_t nr_sections; /* Number of sections */ + uint16_t sec_hdr_size; /* Section header size (in blocks) */ + uint8_t rand_pad0[6]; /* Random padding */ + uint64_t timestamp; /* In microseconds since 2000/1/1 00:00:00 */ + struct sb_version_t product_ver; + struct sb_version_t component_ver; + uint16_t drive_tag; /* first tag to boot ? */ + uint8_t rand_pad1[6]; /* Random padding */ +} __attribute__((packed)); + +struct sb_section_header_t +{ + uint32_t identifier; + uint32_t offset; /* In blocks */ + uint32_t size; /* In blocks */ + uint32_t flags; +} __attribute__((packed)); + +struct sb_key_dictionary_entry_t +{ + uint8_t hdr_cbc_mac[16]; /* CBC-MAC of the header */ + uint8_t key[16]; /* Actual AES Key (encrypted by the global key) */ +} __attribute__((packed)); + +#define IMAGE_MAJOR_VERSION 1 +#define IMAGE_MINOR_VERSION 1 + +#define SECTION_BOOTABLE (1 << 0) +#define SECTION_CLEARTEXT (1 << 1) + +#define SB_INST_NOP 0x0 +#define SB_INST_TAG 0x1 +#define SB_INST_LOAD 0x2 +#define SB_INST_FILL 0x3 +#define SB_INST_JUMP 0x4 +#define SB_INST_CALL 0x5 +#define SB_INST_MODE 0x6 + +/* flags */ +#define SB_INST_LAST_TAG 1 /* for TAG */ +#define SB_INST_LOAD_DCD 1 /* for LOAD */ +#define SB_INST_FILL_BYTE 0 /* for FILL */ +#define SB_INST_FILL_HWORD 1 /* for FILL */ +#define SB_INST_FILL_WORD 2 /* for FILL */ +#define SB_INST_HAB_EXEC 1 /* for JUMP/CALL */ + +struct sb_instruction_header_t +{ + uint8_t checksum; + uint8_t opcode; + uint16_t flags; +} __attribute__((packed)); + +struct sb_instruction_common_t +{ + struct sb_instruction_header_t hdr; + uint32_t addr; + uint32_t len; + uint32_t data; +} __attribute__((packed)); + +struct sb_instruction_load_t +{ + struct sb_instruction_header_t hdr; + uint32_t addr; + uint32_t len; + uint32_t crc; +} __attribute__((packed)); + +struct sb_instruction_fill_t +{ + struct sb_instruction_header_t hdr; + uint32_t addr; + uint32_t len; + uint32_t pattern; +} __attribute__((packed)); + +struct sb_instruction_mode_t +{ + struct sb_instruction_header_t hdr; + uint32_t zero1; + uint32_t zero2; + uint32_t mode; +} __attribute__((packed)); + +struct sb_instruction_call_t +{ + struct sb_instruction_header_t hdr; + uint32_t addr; + uint32_t zero; + uint32_t arg; +} __attribute__((packed)); + +struct sb_instruction_tag_t +{ + struct sb_instruction_header_t hdr; + uint32_t identifier; /* section identifier */ + uint32_t len; /* length of the section */ + uint32_t flags; /* section flags */ +} __attribute__((packed)); + +/******* + * API * + *******/ + +#define SB_INST_DATA 0xff + +struct sb_inst_t +{ + uint8_t inst; /* SB_INST_* */ + uint32_t size; + uint32_t addr; + // + void *data; + uint32_t pattern; + // + uint32_t argument; // for call, jump and mode + /* for production use */ + uint32_t padding_size; + uint8_t *padding; +}; + +struct sb_section_t +{ + uint32_t identifier; + bool is_data; + bool is_cleartext; + uint32_t alignment; + // data sections are handled as one or more SB_INST_DATA virtual instruction + int nr_insts; + struct sb_inst_t *insts; + /* for production use */ + uint32_t file_offset; /* in blocks */ + uint32_t sec_size; /* in blocks */ +}; + +struct sb_file_t +{ + /* override real, otherwise it is randomly generated */ + bool override_real_key; + uint8_t real_key[16]; + /* override crypto IV, use with caution ! Use NULL to generate it */ + bool override_crypto_iv; + uint8_t crypto_iv[16]; + + int nr_sections; + uint16_t drive_tag; + uint32_t first_boot_sec_id; + uint16_t flags; + uint8_t minor_version; + struct sb_section_t *sections; + struct sb_version_t product_ver; + struct sb_version_t component_ver; + /* for production use */ + uint32_t image_size; /* in blocks */ +}; + +enum sb_error_t +{ + SB_SUCCESS = 0, + SB_ERROR = -1, + SB_OPEN_ERROR = -2, + SB_READ_ERROR = -3, + SB_WRITE_ERROR = -4, + SB_FORMAT_ERROR = -5, + SB_CHECKSUM_ERROR = -6, + SB_NO_VALID_KEY = -7, + SB_FIRST_CRYPTO_ERROR = -8, + SB_LAST_CRYPTO_ERROR = SB_FIRST_CRYPTO_ERROR - CRYPTO_NUM_ERRORS, +}; + +enum sb_error_t sb_write_file(struct sb_file_t *sb, const char *filename); + +typedef void (*sb_color_printf)(void *u, bool err, color_t c, const char *f, ...); +struct sb_file_t *sb_read_file(const char *filename, bool raw_mode, void *u, + sb_color_printf printf, enum sb_error_t *err); +/* use size_t(-1) to use maximum size */ +struct sb_file_t *sb_read_file_ex(const char *filename, size_t offset, size_t size, bool raw_mode, void *u, + sb_color_printf printf, enum sb_error_t *err); +struct sb_file_t *sb_read_memory(void *buffer, size_t size, bool raw_mode, void *u, + sb_color_printf printf, enum sb_error_t *err); + +void sb_fill_section_name(char name[5], uint32_t identifier); +void sb_dump(struct sb_file_t *file, void *u, sb_color_printf printf); +void sb_free_instruction(struct sb_inst_t inst); +void sb_free_section(struct sb_section_t file); +void sb_free(struct sb_file_t *file); + +#endif /* __SB_H__ */ diff --git a/utils/imxtools/sbtools/sbloader.c b/utils/imxtools/sbtools/sbloader.c new file mode 100644 index 0000000000..e1534ea7d5 --- /dev/null +++ b/utils/imxtools/sbtools/sbloader.c @@ -0,0 +1,193 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 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 + +void put32le(uint8_t *buf, uint32_t i) +{ + *buf++ = i & 0xff; + *buf++ = (i >> 8) & 0xff; + *buf++ = (i >> 16) & 0xff; + *buf++ = (i >> 24) & 0xff; +} + +void put32be(uint8_t *buf, uint32_t i) +{ + *buf++ = (i >> 24) & 0xff; + *buf++ = (i >> 16) & 0xff; + *buf++ = (i >> 8) & 0xff; + *buf++ = i & 0xff; +} + +struct dev_info_t +{ + uint16_t vendor_id; + uint16_t product_id; + unsigned xfer_size; +}; + +struct dev_info_t g_dev_info[] = +{ + {0x066f, 0x3780, 1024}, /* i.MX233 / STMP3780 */ + {0x066f, 0x3770, 48}, /* STMP3770 */ + {0x15A2, 0x004F, 1024}, /* i.MX28 */ +}; + +int main(int argc, char **argv) +{ + int ret; + FILE *f; + int i, xfer_size, nr_xfers, recv_size; + + if(argc != 3) + { + printf("usage: %s \n", argv[0]); + printf("If is set to zero, the preferred one is used.\n"); + return 1; + } + + char *end; + xfer_size = strtol(argv[1], &end, 0); + if(end != (argv[1] + strlen(argv[1]))) + { + printf("Invalid transfer size !\n"); + return 1; + } + + libusb_device_handle *dev; + + libusb_init(NULL); + + libusb_set_debug(NULL, 3); + + for(unsigned i = 0; i < sizeof(g_dev_info) / sizeof(g_dev_info[0]); i++) + { + dev = libusb_open_device_with_vid_pid(NULL, + g_dev_info[i].vendor_id, g_dev_info[i].product_id); + if(dev == NULL) + continue; + if(xfer_size == 0) + xfer_size = g_dev_info[i].xfer_size; + printf("Found a match for %04x:%04x\n", + g_dev_info[i].vendor_id, g_dev_info[i].product_id); + break; + } + if(dev == NULL) + { + printf("Cannot open device\n"); + return 1; + } + + libusb_detach_kernel_driver(dev, 0); + libusb_detach_kernel_driver(dev, 4); + + libusb_claim_interface (dev, 0); + libusb_claim_interface (dev, 4); + + if(!dev) + { + printf("No dev\n"); + exit(1); + } + + f = fopen(argv[2], "r"); + if(f == NULL) + { + perror("cannot open file"); + return 1; + } + fseek(f, 0, SEEK_END); + size_t size = ftell(f); + fseek(f, 0, SEEK_SET); + + printf("Transfer size: %d\n", xfer_size); + nr_xfers = (size + xfer_size - 1) / xfer_size; + uint8_t *file_buf = malloc(nr_xfers * xfer_size); + memset(file_buf, 0xff, nr_xfers * xfer_size); // pad with 0xff + if(fread(file_buf, size, 1, f) != 1) + { + perror("read error"); + fclose(f); + return 1; + } + fclose(f); + + uint8_t *xfer_buf = malloc(1 + xfer_size); + uint8_t *p = xfer_buf; + + *p++ = 0x01; /* Report id */ + + /* Command block wrapper */ + *p++ = 'B'; /* Signature */ + *p++ = 'L'; + *p++ = 'T'; + *p++ = 'C'; + put32le(p, 0x1); /* Tag */ + p += 4; + put32le(p, size); /* Payload size */ + p += 4; + *p++ = 0; /* Flags (host to device) */ + p += 2; /* Reserved */ + + /* Command descriptor block */ + *p++ = 0x02; /* Firmware download */ + put32be(p, size); /* Download size */ + + ret = libusb_control_transfer(dev, + LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, 0x9, 0x201, 0, + xfer_buf, xfer_size + 1, 1000); + if(ret < 0) + { + printf("transfer error at init step\n"); + return 1; + } + + for(i = 0; i < nr_xfers; i++) + { + xfer_buf[0] = 0x2; + memcpy(&xfer_buf[1], &file_buf[i * xfer_size], xfer_size); + + ret = libusb_control_transfer(dev, + LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + 0x9, 0x202, 0, xfer_buf, xfer_size + 1, 1000); + if(ret < 0) + { + printf("transfer error at send step %d\n", i); + return 1; + } + } + + ret = libusb_interrupt_transfer(dev, 0x81, xfer_buf, xfer_size, &recv_size, + 1000); + if(ret < 0) + { + printf("transfer error at final stage\n"); + return 1; + } + + printf("ret %i\n", ret); + + return 0; +} + diff --git a/utils/imxtools/sbtools/sbtoelf.c b/utils/imxtools/sbtools/sbtoelf.c new file mode 100644 index 0000000000..c1d1e9aa34 --- /dev/null +++ b/utils/imxtools/sbtools/sbtoelf.c @@ -0,0 +1,303 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 Bertrik Sikken + * + * 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. + * + ****************************************************************************/ + +/* + * .sb file parser and chunk extractor + * + * Based on amsinfo, which is + * Copyright © 2008 Rafaël Carré + */ + +#define _ISOC99_SOURCE /* snprintf() */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "crypto.h" +#include "elf.h" +#include "sb.h" +#include "misc.h" + +/* all blocks are sized as a multiple of 0x1ff */ +#define PAD_TO_BOUNDARY(x) (((x) + 0x1ff) & ~0x1ff) + +/* If you find a firmware that breaks the known format ^^ */ +#define assert(a) do { if(!(a)) { fprintf(stderr,"Assertion \"%s\" failed in %s() line %d!\n\nPlease send us your firmware!\n",#a,__func__,__LINE__); exit(1); } } while(0) + +#define crypto_cbc(...) \ + do { int ret = crypto_cbc(__VA_ARGS__); \ + if(ret != CRYPTO_ERROR_SUCCESS) \ + bug("crypto_cbc error: %d\n", ret); \ + }while(0) + +/* globals */ + +char *g_out_prefix; + +static void elf_printf(void *user, bool error, const char *fmt, ...) +{ + if(!g_debug && !error) + return; + (void) user; + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} + +static void elf_write(void *user, uint32_t addr, const void *buf, size_t count) +{ + FILE *f = user; + fseek(f, addr, SEEK_SET); + fwrite(buf, count, 1, f); +} + +static void extract_elf_section(struct elf_params_t *elf, int count, uint32_t id) +{ + char name[5]; + char *filename = xmalloc(strlen(g_out_prefix) + 32); + sb_fill_section_name(name, id); + sprintf(filename, "%s%s.%d.elf", g_out_prefix, name, count); + if(g_debug) + printf("Write boot section %s to %s\n", name, filename); + + FILE *fd = fopen(filename, "wb"); + free(filename); + + if(fd == NULL) + return ; + elf_write_file(elf, elf_write, elf_printf, fd); + fclose(fd); +} + +static void extract_sb_section(struct sb_section_t *sec) +{ + if(sec->is_data) + { + char sec_name[5]; + char *filename = xmalloc(strlen(g_out_prefix) + 32); + sb_fill_section_name(sec_name, sec->identifier); + sprintf(filename, "%s%s.bin", g_out_prefix, sec_name); + FILE *fd = fopen(filename, "wb"); + if(fd == NULL) + bugp("Cannot open %s for writing\n", filename); + if(g_debug) + printf("Write data section %s to %s\n", sec_name, filename); + free(filename); + + for(int j = 0; j < sec->nr_insts; j++) + { + assert(sec->insts[j].inst == SB_INST_DATA); + fwrite(sec->insts[j].data, sec->insts[j].size, 1, fd); + } + fclose(fd); + } + + int elf_count = 0; + struct elf_params_t elf; + elf_init(&elf); + + for(int i = 0; i < sec->nr_insts; i++) + { + struct sb_inst_t *inst = &sec->insts[i]; + switch(inst->inst) + { + case SB_INST_LOAD: + elf_add_load_section(&elf, inst->addr, inst->size, inst->data); + break; + case SB_INST_FILL: + elf_add_fill_section(&elf, inst->addr, inst->size, inst->pattern); + break; + case SB_INST_CALL: + case SB_INST_JUMP: + elf_set_start_addr(&elf, inst->addr); + extract_elf_section(&elf, elf_count++, sec->identifier); + elf_release(&elf); + elf_init(&elf); + break; + default: + /* ignore mode and nop */ + break; + } + } + + if(!elf_is_empty(&elf)) + extract_elf_section(&elf, elf_count, sec->identifier); + elf_release(&elf); +} + +static void extract_sb_file(struct sb_file_t *file) +{ + for(int i = 0; i < file->nr_sections; i++) + extract_sb_section(&file->sections[i]); +} + +static void usage(void) +{ + printf("Usage: sbtoelf [options] sb-file\n"); + printf("Options:\n"); + printf(" -?/--help\tDisplay this message\n"); + printf(" -o \tEnable output and set prefix\n"); + printf(" -d/--debug\tEnable debug output*\n"); + printf(" -k \tAdd key file\n"); + printf(" -z\t\tAdd zero key\n"); + printf(" -r\t\tUse raw command mode\n"); + printf(" -a/--add-key \tAdd single key (hex or usbotp)\n"); + printf(" -n/--no-color\tDisable output colors\n"); + printf(" -l/--loopback \tProduce sb file out of extracted description*\n"); + printf("Options marked with a * are for debug purpose only\n"); + exit(1); +} + +static void sb_printf(void *user, bool error, color_t c, const char *fmt, ...) +{ + (void) user; + (void) error; + va_list args; + va_start(args, fmt); + color(c); + vprintf(fmt, args); + va_end(args); +} + +static struct crypto_key_t g_zero_key = +{ + .method = CRYPTO_KEY, + .u.key = {0} +}; + +int main(int argc, char **argv) +{ + bool raw_mode = false; + const char *loopback = NULL; + + while(1) + { + static struct option long_options[] = + { + {"help", no_argument, 0, '?'}, + {"debug", no_argument, 0, 'd'}, + {"add-key", required_argument, 0, 'a'}, + {"no-color", no_argument, 0, 'n'}, + {"loopback", required_argument, 0, 'l'}, + {0, 0, 0, 0} + }; + + int c = getopt_long(argc, argv, "?do:k:zra:nl:", long_options, NULL); + if(c == -1) + break; + switch(c) + { + case -1: + break; + case 'l': + if(loopback) + bug("Only one loopback file can be specified !\n"); + loopback = optarg; + break; + case 'n': + enable_color(false); + break; + case 'd': + g_debug = true; + break; + case '?': + usage(); + break; + case 'o': + g_out_prefix = optarg; + break; + case 'k': + { + if(!add_keys_from_file(optarg)) + bug("Cannot add keys from %s\n", optarg); + break; + } + case 'z': + { + add_keys(&g_zero_key, 1); + break; + } + case 'r': + raw_mode = true; + break; + case 'a': + { + struct crypto_key_t key; + char *s = optarg; + if(!parse_key(&s, &key)) + bug("Invalid key specified as argument"); + if(*s != 0) + bug("Trailing characters after key specified as argument"); + add_keys(&key, 1); + break; + } + default: + abort(); + } + } + + if(argc - optind != 1) + { + usage(); + return 1; + } + + 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) + { + 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); + clear_keys(); + + return 0; +} diff --git a/utils/imxtools/sbtools/sha1.c b/utils/imxtools/sbtools/sha1.c new file mode 100644 index 0000000000..0ad05bb5cd --- /dev/null +++ b/utils/imxtools/sbtools/sha1.c @@ -0,0 +1,150 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 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. + * + ****************************************************************************/ +/* Based on http://en.wikipedia.org/wiki/SHA-1 */ +#include "crypto.h" + +static uint32_t rot_left(uint32_t val, int rot) +{ + return (val << rot) | (val >> (32 - rot)); +} + +static inline void byte_swapxx(byte *ptr, int size) +{ + for(int i = 0; i < size / 2; i++) + { + byte c = ptr[i]; + ptr[i] = ptr[size - i - 1]; + ptr[size - i - 1] = c; + } +} + +static void byte_swap32(uint32_t *v) +{ + byte_swapxx((byte *)v, 4); +} + +void sha_1_init(struct sha_1_params_t *params) +{ + params->hash[0] = 0x67452301; + params->hash[1] = 0xEFCDAB89; + params->hash[2] = 0x98BADCFE; + params->hash[3] = 0x10325476; + params->hash[4] = 0xC3D2E1F0; + params->buffer_nr_bits = 0; +} + +void sha_1_update(struct sha_1_params_t *params, byte *buffer, int size) +{ + int buffer_nr_bytes = (params->buffer_nr_bits / 8) % 64; + params->buffer_nr_bits += 8 * size; + int pos = 0; + if(buffer_nr_bytes + size >= 64) + { + pos = 64 - buffer_nr_bytes; + memcpy((byte *)(params->w) + buffer_nr_bytes, buffer, 64 - buffer_nr_bytes); + sha_1_block(params, params->hash, (byte *)params->w); + for(; pos + 64 <= size; pos += 64) + sha_1_block(params, params->hash, buffer + pos); + buffer_nr_bytes = 0; + } + memcpy((byte *)(params->w) + buffer_nr_bytes, buffer + pos, size - pos); +} + +void sha_1_finish(struct sha_1_params_t *params) +{ + /* length (in bits) in big endian BEFORE preprocessing */ + byte length_big_endian[8]; + memcpy(length_big_endian, ¶ms->buffer_nr_bits, 8); + byte_swapxx(length_big_endian, 8); + /* append '1' and then '0's to the message to get 448 bit length for the last block */ + byte b = 0x80; + sha_1_update(params, &b, 1); + b = 0; + while((params->buffer_nr_bits % 512) != 448) + sha_1_update(params, &b, 1); + /* append length */ + sha_1_update(params, length_big_endian, 8); + /* go back to big endian */ + for(int i = 0; i < 5; i++) + byte_swap32(¶ms->hash[i]); +} + +void sha_1_output(struct sha_1_params_t *params, byte *out) +{ + memcpy(out, params->hash, 20); +} + +void sha_1_block(struct sha_1_params_t *params, uint32_t cur_hash[5], byte *data) +{ + uint32_t a, b, c, d, e; + a = cur_hash[0]; + b = cur_hash[1]; + c = cur_hash[2]; + d = cur_hash[3]; + e = cur_hash[4]; + + #define w params->w + + memmove(w, data, 64); + for(int i = 0; i < 16; i++) + byte_swap32(&w[i]); + + for(int i = 16; i <= 79; i++) + w[i] = rot_left(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1); + + for(int i = 0; i<= 79; i++) + { + uint32_t f, k; + if(i <= 19) + { + f = (b & c) | ((~b) & d); + k = 0x5A827999; + } + else if(i <= 39) + { + f = b ^ c ^ d; + k = 0x6ED9EBA1; + } + else if(i <= 59) + { + f = (b & c) | (b & d) | (c & d); + k = 0x8F1BBCDC; + } + else + { + f = b ^ c ^ d; + k = 0xCA62C1D6; + } + uint32_t temp = rot_left(a, 5) + f + e + k + w[i]; + e = d; + d = c; + c = rot_left(b, 30); + b = a; + a = temp; + } + #undef w + + cur_hash[0] += a; + cur_hash[1] += b; + cur_hash[2] += c; + cur_hash[3] += d; + cur_hash[4] += e; +} -- cgit v1.2.3