├── .gitignore ├── .gitmodules ├── Dockerfile ├── JTAGInABox.AppDir ├── AppRun ├── JTAGInABox.desktop └── JTAGInABox.png ├── Makefile ├── README.md ├── build.sh ├── docs ├── building-mac.md ├── building.md └── img │ ├── flashing.png │ ├── home.png │ └── urjtag.png ├── libstlinkloader ├── .gitignore ├── Makefile ├── README.md ├── include │ └── stlinkloader.h ├── src │ ├── aes.c │ ├── aes.h │ ├── crypto.c │ ├── crypto.h │ ├── defines.h │ ├── dfu.c │ ├── dfu.h │ ├── flashing.c │ ├── stlinkloader.c │ └── types.h └── stlinkloader.vapi ├── res ├── Info.plist ├── appimagelibdep.sh ├── css │ └── jtaginabox.css ├── jtaginabox.sh ├── resources.xml ├── runtime-i686 ├── runtime-x86_64 └── ui │ ├── Homepage.ui │ ├── overlayspinner.ui │ └── stlinkinfobox.ui └── src ├── Flashing.vala ├── Homepage.vala ├── JTAGInABox.vala ├── JTAGInABoxWindow.vala ├── STLink.vala └── UrJTAG.vala /.gitignore: -------------------------------------------------------------------------------- 1 | res/resources.c 2 | src/*.o 3 | src/*.vapi 4 | jtaginabox 5 | *.AppImage 6 | JTAGinabox.app/ 7 | jtaginabox.dSYM/ 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "DirtyJTAG"] 2 | path = DirtyJTAG 3 | url = https://github.com/jeanthom/DirtyJTAG 4 | [submodule "urjtag"] 5 | path = urjtag 6 | url = https://git.code.sf.net/p/urjtag/git 7 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:jessie-slim 2 | 3 | RUN \ 4 | apt-get update && \ 5 | apt-get -o APT::Install-Suggests=0 -o APT::Install-Recommends=0 -y install \ 6 | valac \ 7 | libgtk-3-dev \ 8 | libusb-1.0-0-dev \ 9 | libvte-2.91-dev \ 10 | build-essential \ 11 | libglib2.0-dev \ 12 | libxml2-utils \ 13 | automake \ 14 | autotools-dev \ 15 | autopoint \ 16 | libtool \ 17 | python-dev \ 18 | tree \ 19 | gcc-arm-none-eabi \ 20 | libnewlib-arm-none-eabi \ 21 | wget \ 22 | squashfs-tools && \ 23 | rm -rf /var/lib/apt/lists/* 24 | 25 | WORKDIR /jtaginabox 26 | COPY . . 27 | RUN make appimage 28 | -------------------------------------------------------------------------------- /JTAGInABox.AppDir/AppRun: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | HERE="$(dirname "$(readlink -f "${0}")")" 3 | export LD_LIBRARY_PATH=${HERE}/usr/lib:$LD_LIBRARY_PATH 4 | export PATH=${HERE}/usr/bin:$PATH 5 | ${HERE}/usr/bin/jtaginabox 6 | -------------------------------------------------------------------------------- /JTAGInABox.AppDir/JTAGInABox.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=JTAG in a box 3 | Exec=jtaginabox 4 | Icon=JTAGInABox 5 | Type=Application 6 | Categories=Utility; -------------------------------------------------------------------------------- /JTAGInABox.AppDir/JTAGInABox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanthom/jtaginabox/cad17c32d7bb7b55cf26b27caa00ab4647b3db76/JTAGInABox.AppDir/JTAGInABox.png -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VALA_OBJS=src/JTAGInABox.o src/JTAGInABoxWindow.o src/Homepage.o src/Flashing.o src/UrJTAG.o src/STLink.o 2 | VALA_VAPI=$(VALA_OBJS:o=vapi) 3 | VALAC=valac 4 | 5 | MODULES=glib-2.0 gio-2.0 gtk+-3.0 vte-2.91 libusb-1.0 6 | LDFLAGS=-Llibstlinkloader -lstlinkloader $(shell pkg-config --libs --cflags $(MODULES)) #-Wl,-Rlibstlinkloader 7 | VALAFLAGS=--vapidir=libstlinkloader --pkg libusb-1.0 --pkg glib-2.0 --pkg gtk+-3.0 --pkg vte-2.91 --pkg stlinkloader --Xcc="-Ilibstlinkloader/include/" --target-glib=2.38 --gresources=res/resources.xml --target-glib=2.40 8 | 9 | 10 | ARCH=$(shell uname -m) 11 | 12 | all: jtaginabox 13 | 14 | libstlinkloader/libstlinkloader.so: 15 | cd libstlinkloader && $(MAKE) libstlinkloader.so 16 | 17 | libstlinkloader/libstlinkloader.dylib: 18 | cd libstlinkloader && $(MAKE) libstlinkloader.dylib 19 | 20 | %.vapi: %.vala 21 | $(VALAC) --fast-vapi=$@ $< 22 | 23 | %.o: %.vala $(VALA_VAPI) 24 | $(eval TMP_VAPI:=$(filter-out $(<:vala=vapi), $(VALA_VAPI))) 25 | $(VALAC) $(VALAFLAGS) --compile $(TMP_VAPI:%=--use-fast-vapi=%) $< 26 | mv $(@:src/%o=%vala.o) $@ 27 | 28 | res/resources.c: res/resources.xml 29 | glib-compile-resources --sourcedir=res res/resources.xml --target=res/resources.c --generate-source 30 | 31 | jtaginabox: $(VALA_OBJS) libstlinkloader/libstlinkloader.so res/resources.c 32 | $(CC) -g res/resources.c $(VALA_OBJS) $(LDFLAGS) -o $@ 33 | strip -s jtaginabox 34 | 35 | jtaginabox-mac: $(VALA_OBJS) libstlinkloader/libstlinkloader.dylib res/resources.c res/Info.plist \ 36 | urjtag-mac dirtyjtag.bin 37 | $(CC) -g res/resources.c $(VALA_OBJS) $(LDFLAGS) -o jtaginabox 38 | mkdir -p JTAGinabox.app 39 | mkdir -p JTAGinabox.app/Contents/MacOS 40 | cp jtaginabox JTAGinabox.app/Contents/MacOS/jtaginabox-bin 41 | cp libstlinkloader/libstlinkloader.dylib JTAGinabox.app/Contents/MacOS/ 42 | cp res/Info.plist JTAGinabox.app/Contents/Info.plist 43 | install_name_tool -id "@executable_path/libstlinkloader.dylib" JTAGinabox.app/Contents/MacOS/libstlinkloader.dylib 44 | install_name_tool -change libstlinkloader.dylib "@executable_path/libstlinkloader.dylib" JTAGinabox.app/Contents/MacOS/jtaginabox-bin 45 | cp urjtag/urjtag/src/apps/jtag/.libs/jtag JTAGinabox.app/Contents/MacOS/ 46 | cp urjtag/urjtag/src/.libs/liburjtag.0.dylib JTAGinabox.app/Contents/MacOS/ 47 | install_name_tool -id "@executable_path/liburjtag.0.dylib" JTAGinabox.app/Contents/MacOS/liburjtag.0.dylib 48 | install_name_tool -change "/usr/local/lib/liburjtag.0.dylib" "@executable_path/liburjtag.0.dylib" JTAGinabox.app/Contents/MacOS/jtag 49 | cp res/jtaginabox.sh JTAGInABox.app/Contents/MacOS/jtaginabox 50 | 51 | .PHONY: urjtag 52 | urjtag: 53 | cd urjtag/urjtag/ && ./autogen.sh && make 54 | 55 | .PHONY: urjtag-mac 56 | urjtag-mac: 57 | YACC=$(YACC) cd urjtag/urjtag/ && ./autogen.sh && ./configure --with-readline=no --enable-python=no --disable-debug --disable-dependency-tracking --disable-silent-rules --prefix=/usr/local && make 58 | 59 | .PHONY: appimage 60 | appimage: jtaginabox libstlinkloader/libstlinkloader.so res/runtime-$(ARCH) urjtag dirtyjtag.bin 61 | : > JTAGInABox-$(ARCH).AppImage # Erase AppImage file if existing 62 | mkdir -p JTAGInABox.AppDir/usr/bin/ 63 | mkdir -p JTAGInABox.AppDir/usr/lib/ 64 | cp jtaginabox JTAGInABox.AppDir/usr/bin/ 65 | cp libstlinkloader/libstlinkloader.so JTAGInABox.AppDir/usr/lib/ 66 | cp urjtag/urjtag/src/apps/jtag/.libs/jtag JTAGInABox.AppDir/usr/bin/ 67 | cp urjtag/urjtag/src/.libs/liburjtag.so.0.0.0 JTAGInABox.AppDir/usr/lib/ 68 | ln -f JTAGInABox.AppDir/usr/lib/liburjtag.so.0.0.0 JTAGInABox.AppDir/usr/lib/liburjtag.so 69 | ln -f JTAGInABox.AppDir/usr/lib/liburjtag.so.0.0.0 JTAGInABox.AppDir/usr/lib/liburjtag.so.0 70 | bash -c "res/appimagelibdep.sh jtaginabox | xargs -I library cp library JTAGInABox.AppDir/usr/lib/" 71 | @echo "Current AppDir tree :" 72 | @tree JTAGInABox.AppDir 73 | mksquashfs JTAGInABox.AppDir JTAGInABox.squashfs -root-owned -noappend 74 | cat res/runtime-$(ARCH) >> JTAGInABox-$(ARCH).AppImage 75 | cat JTAGInABox.squashfs >> JTAGInABox-$(ARCH).AppImage 76 | chmod a+x JTAGInABox-$(ARCH).AppImage 77 | 78 | dirtyjtag.bin: 79 | cd DirtyJTAG && $(MAKE) PLATFORM=stlinkv2dfu 80 | cp DirtyJTAG/src/dirtyjtag.stlinkv2dfu.bin dirtyjtag.bin 81 | 82 | .PHONY: clean 83 | clean: 84 | rm -f res/resources.c 85 | rm -f src/*.o 86 | rm -f jtaginabox 87 | rm -f libstlinkloader.so 88 | rm -f *.AppImage 89 | rm -f JTAGInABox.AppDir/usr/bin/* 90 | rm -f JTAGInABox.AppDir/usr/lib/* 91 | rm -f dirtyjtag.bin 92 | rm -rf JTAGInABox.app 93 | cd libstlinkloader/ && $(MAKE) clean 94 | cd urjtag/urjtag/ && $(MAKE) clean 95 | cd DirtyJTAG/ && $(MAKE) clean 96 | 97 | 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JTAG in a box 2 | 3 | Finally an inexpensive and easy to use JTAG solution (hardware+software). Intended for backyard hardware enthusiasts, also known as OpenWRT/LEDE router hackers :-) 4 | 5 | Pick yourself a $2 ST-Link dongle (available in many chinese shops), and you will be ready to hack in minutes! 6 | 7 | JTAG in a box is distributed as an AppImage. It should run pretty much everywhere and be self-sufficient (on major Linux distributions such as Ubuntu, Fedora or Arch). 8 | 9 | ## I want some ! 10 | 11 | * [Runs-on-everything AppImages built by our talented artisans (recommended)](https://github.com/jeanthom/jtaginabox/releases) 12 | * [Compile it yourself](docs/building.md) 13 | 14 | ## Screenshots 15 | 16 | ![JTAG in a box's home page](docs/img/home.png) 17 | 18 | ![JTAG in a box flashing an ST-Link dongle](docs/img/flashing.png) 19 | 20 | ![UrJTAG running inside JTAG in a box](docs/img/urjtag.png) 21 | 22 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | docker build -t jtaginabox -f Dockerfile . 4 | docker run --volume=$PWD:/host:z jtaginabox bash -c "chown "`id -u`":"`id -u`" *.AppImage && cp -p *.AppImage /host/" 5 | -------------------------------------------------------------------------------- /docs/building-mac.md: -------------------------------------------------------------------------------- 1 | # Building JTAG in a box on a Mac 2 | 3 | Building JTAG in a box on a Mac is a fairly difficult exercise, but somehow doable. 4 | 5 | ## Dependencies 6 | 7 | ``` 8 | brew install vala vte3 gettext libusb bison gcc readline 9 | ``` 10 | 11 | Make sure `/usr/local/opt/gettext/bin` is in your $PATH, otherwise autopoint won't work. 12 | 13 | ## Instructions 14 | 15 | ``` 16 | make jtaginabox-mac YACC=/usr/local/Cellar/bison/3.2/bin/yacc 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/building.md: -------------------------------------------------------------------------------- 1 | # Building JTAG in a box yourself 2 | 3 | Assuming you have Docker on your computer : 4 | 5 | ``` 6 | git clone https://github.com/jeanthom/jtaginabox 7 | cd jtaginabox 8 | git submodule update --init --recursive 9 | ./build.sh 10 | ``` 11 | 12 | This is the recommended way of building JTAG in a box. After initial Docker configuration (Ubuntu container setup and package installation), the build time is around a minute and a half. 13 | 14 | The build scripts does the following : 15 | 16 | * Compiling `libstlinkloader` 17 | * Compiling JTAG in a box executable 18 | * Compiling UrJTAG 19 | * Compiling DirtyJTAG 20 | * Bundle everything into an AppImage 21 | 22 | The Dockerfile uses Ubuntu 16.04 LTS. 23 | 24 | ## AppImage bundling inside Docker 25 | 26 | For those who have taken a look at my Makefile, they probably noticed that the whole AppImage bundling was done manually. You way wonder why... Well, AppImage uses a squashfs to store everything (which will be mounted by the runtime when the user will execute your AppImage). The official `appimagetool` uses FUSE to mount a squashfs image. The problem is that on Docker you can't run FUSE properly (or at least without tweaks). The solution here is to use mksquashfs and create our AppImage manually. 27 | 28 | However this causes an issue on Fedora : mksquashfs is only available to a root user. Meh. 29 | -------------------------------------------------------------------------------- /docs/img/flashing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanthom/jtaginabox/cad17c32d7bb7b55cf26b27caa00ab4647b3db76/docs/img/flashing.png -------------------------------------------------------------------------------- /docs/img/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanthom/jtaginabox/cad17c32d7bb7b55cf26b27caa00ab4647b3db76/docs/img/home.png -------------------------------------------------------------------------------- /docs/img/urjtag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanthom/jtaginabox/cad17c32d7bb7b55cf26b27caa00ab4647b3db76/docs/img/urjtag.png -------------------------------------------------------------------------------- /libstlinkloader/.gitignore: -------------------------------------------------------------------------------- 1 | src/*.o 2 | libstlinkloader.so 3 | libstlinkloader.a 4 | libstlinkloader.dylib 5 | -------------------------------------------------------------------------------- /libstlinkloader/Makefile: -------------------------------------------------------------------------------- 1 | LDFLAGS := $(shell pkg-config --libs libusb-1.0 glib-2.0) 2 | CFLAGS := -Wall -Wextra -Werror $(shell pkg-config --cflags libusb-1.0 glib-2.0) -Iinclude -fPIC -g 3 | 4 | OBJS := src/dfu.o src/stlinkloader.o src/flashing.o src/crypto.o src/aes.o 5 | 6 | all: libstlinkloader.so 7 | 8 | libstlinkloader.so: $(OBJS) 9 | $(CC) -shared $(LDFLAGS) $(OBJS) -o $@ 10 | 11 | libstlinkloader.dylib: $(OBJS) 12 | $(CC) -dynamiclib $(LDFLAGS) $(OBJS) -o $@ 13 | 14 | %.o: %.c 15 | $(CC) $(CFLAGS) -c $< -o $@ 16 | 17 | .PHONY: clean 18 | clean: 19 | rm -f libstlinkloader.so 20 | rm -f src/*.o 21 | -------------------------------------------------------------------------------- /libstlinkloader/README.md: -------------------------------------------------------------------------------- 1 | # libstlinkloader 2 | 3 | [stlink-tool](https://github.com/jeanthom/stlink-tool) but as a C library with Vala VAPI. 4 | 5 | ## Example 6 | 7 | ```c 8 | #include 9 | #include 10 | #include 11 | 12 | int main(void) { 13 | struct stlinkloadercontext *ctx; 14 | 15 | ctx = stlinkloader_init(); 16 | if (ctx == NULL) { 17 | return EXIT_FAILURE; 18 | } 19 | 20 | if (stlinkloader_is_correct_mode(ctx) != 1) { 21 | /* The dongle can't be reprogrammed in this mode */ 22 | printf("Please unplug and replug your ST-Link\n"); 23 | return EXIT_SUCCESS; 24 | } 25 | 26 | stlinkloader_read_infos(ctx); 27 | 28 | stlinkloader_flash(ctx, "/tmp/myfirmware.bin", 0x08004000, 1024); 29 | 30 | stlinkloader_free(ctx); 31 | 32 | return EXIT_SUCCESS; 33 | } 34 | ``` 35 | -------------------------------------------------------------------------------- /libstlinkloader/include/stlinkloader.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | typedef void(*progress_func)(double progress, gpointer user_data); 5 | 6 | struct stlinkloadercontext; 7 | 8 | struct stlinkloadercontext* stlinkloader_init(void); 9 | void stlinkloader_free(struct stlinkloadercontext *context); 10 | int stlinkloader_read_infos(struct stlinkloadercontext *context); 11 | int stlinkloader_is_correct_mode(struct stlinkloadercontext *context); 12 | uint8_t* stlinkloader_get_id(struct stlinkloadercontext *context, int *id_length); 13 | uint8_t stlinkloader_get_stlink_version(struct stlinkloadercontext *context); 14 | uint8_t stlinkloader_get_jtag_version(struct stlinkloadercontext *context); 15 | uint8_t stlinkloader_get_swim_version(struct stlinkloadercontext *context); 16 | int stlinkloader_flash(struct stlinkloadercontext *context, 17 | const char *filename, 18 | unsigned int base_offset, 19 | unsigned int chunk_size, 20 | progress_func progress_callback, 21 | gpointer user_data); 22 | -------------------------------------------------------------------------------- /libstlinkloader/src/aes.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode. 4 | Block size can be chosen in aes.h - available choices are AES128, AES192, AES256. 5 | 6 | The implementation is verified against the test vectors in: 7 | National Institute of Standards and Technology Special Publication 800-38A 2001 ED 8 | 9 | ECB-AES128 10 | ---------- 11 | 12 | plain-text: 13 | 6bc1bee22e409f96e93d7e117393172a 14 | ae2d8a571e03ac9c9eb76fac45af8e51 15 | 30c81c46a35ce411e5fbc1191a0a52ef 16 | f69f2445df4f9b17ad2b417be66c3710 17 | 18 | key: 19 | 2b7e151628aed2a6abf7158809cf4f3c 20 | 21 | resulting cipher 22 | 3ad77bb40d7a3660a89ecaf32466ef97 23 | f5d3d58503b9699de785895a96fdbaaf 24 | 43b1cd7f598ece23881b00e3ed030688 25 | 7b0c785e27e8ad3f8223207104725dd4 26 | 27 | 28 | NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0) 29 | You should pad the end of the string with zeros if this is not the case. 30 | For AES192/256 the key size is proportionally larger. 31 | 32 | */ 33 | 34 | 35 | /*****************************************************************************/ 36 | /* Includes: */ 37 | /*****************************************************************************/ 38 | #include 39 | #include // CBC mode, for memset 40 | #include "aes.h" 41 | 42 | /*****************************************************************************/ 43 | /* Defines: */ 44 | /*****************************************************************************/ 45 | // The number of columns comprising a state in AES. This is a constant in AES. Value=4 46 | #define Nb 4 47 | 48 | #if defined(AES256) && (AES256 == 1) 49 | #define Nk 8 50 | #define Nr 14 51 | #elif defined(AES192) && (AES192 == 1) 52 | #define Nk 6 53 | #define Nr 12 54 | #else 55 | #define Nk 4 // The number of 32 bit words in a key. 56 | #define Nr 10 // The number of rounds in AES Cipher. 57 | #endif 58 | 59 | // jcallan@github points out that declaring Multiply as a function 60 | // reduces code size considerably with the Keil ARM compiler. 61 | // See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3 62 | #ifndef MULTIPLY_AS_A_FUNCTION 63 | #define MULTIPLY_AS_A_FUNCTION 0 64 | #endif 65 | 66 | 67 | 68 | 69 | /*****************************************************************************/ 70 | /* Private variables: */ 71 | /*****************************************************************************/ 72 | // state - array holding the intermediate results during decryption. 73 | typedef uint8_t state_t[4][4]; 74 | 75 | 76 | 77 | // The lookup-tables are marked const so they can be placed in read-only storage instead of RAM 78 | // The numbers below can be computed dynamically trading ROM for RAM - 79 | // This can be useful in (embedded) bootloader applications, where ROM is often limited. 80 | static const uint8_t sbox[256] = { 81 | //0 1 2 3 4 5 6 7 8 9 A B C D E F 82 | 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 83 | 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 84 | 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 85 | 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 86 | 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 87 | 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 88 | 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 89 | 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 90 | 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 91 | 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 92 | 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 93 | 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 94 | 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 95 | 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 96 | 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 97 | 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; 98 | 99 | static const uint8_t rsbox[256] = { 100 | 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 101 | 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 102 | 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 103 | 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 104 | 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 105 | 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 106 | 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 107 | 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 108 | 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 109 | 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 110 | 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 111 | 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 112 | 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 113 | 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 114 | 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 115 | 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }; 116 | 117 | // The round constant word array, Rcon[i], contains the values given by 118 | // x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8) 119 | static const uint8_t Rcon[11] = { 120 | 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 }; 121 | 122 | /* 123 | * Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12), 124 | * that you can remove most of the elements in the Rcon array, because they are unused. 125 | * 126 | * From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon 127 | * 128 | * "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys are needed), 129 | * up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm." 130 | */ 131 | 132 | 133 | /*****************************************************************************/ 134 | /* Private functions: */ 135 | /*****************************************************************************/ 136 | /* 137 | static uint8_t getSBoxValue(uint8_t num) 138 | { 139 | return sbox[num]; 140 | } 141 | */ 142 | #define getSBoxValue(num) (sbox[(num)]) 143 | /* 144 | static uint8_t getSBoxInvert(uint8_t num) 145 | { 146 | return rsbox[num]; 147 | } 148 | */ 149 | #define getSBoxInvert(num) (rsbox[(num)]) 150 | 151 | // This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states. 152 | static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key) 153 | { 154 | unsigned i, j, k; 155 | uint8_t tempa[4]; // Used for the column/row operations 156 | 157 | // The first round key is the key itself. 158 | for (i = 0; i < Nk; ++i) 159 | { 160 | RoundKey[(i * 4) + 0] = Key[(i * 4) + 0]; 161 | RoundKey[(i * 4) + 1] = Key[(i * 4) + 1]; 162 | RoundKey[(i * 4) + 2] = Key[(i * 4) + 2]; 163 | RoundKey[(i * 4) + 3] = Key[(i * 4) + 3]; 164 | } 165 | 166 | // All other round keys are found from the previous round keys. 167 | for (i = Nk; i < Nb * (Nr + 1); ++i) 168 | { 169 | { 170 | k = (i - 1) * 4; 171 | tempa[0]=RoundKey[k + 0]; 172 | tempa[1]=RoundKey[k + 1]; 173 | tempa[2]=RoundKey[k + 2]; 174 | tempa[3]=RoundKey[k + 3]; 175 | 176 | } 177 | 178 | if (i % Nk == 0) 179 | { 180 | // This function shifts the 4 bytes in a word to the left once. 181 | // [a0,a1,a2,a3] becomes [a1,a2,a3,a0] 182 | 183 | // Function RotWord() 184 | { 185 | k = tempa[0]; 186 | tempa[0] = tempa[1]; 187 | tempa[1] = tempa[2]; 188 | tempa[2] = tempa[3]; 189 | tempa[3] = k; 190 | } 191 | 192 | // SubWord() is a function that takes a four-byte input word and 193 | // applies the S-box to each of the four bytes to produce an output word. 194 | 195 | // Function Subword() 196 | { 197 | tempa[0] = getSBoxValue(tempa[0]); 198 | tempa[1] = getSBoxValue(tempa[1]); 199 | tempa[2] = getSBoxValue(tempa[2]); 200 | tempa[3] = getSBoxValue(tempa[3]); 201 | } 202 | 203 | tempa[0] = tempa[0] ^ Rcon[i/Nk]; 204 | } 205 | #if defined(AES256) && (AES256 == 1) 206 | if (i % Nk == 4) 207 | { 208 | // Function Subword() 209 | { 210 | tempa[0] = getSBoxValue(tempa[0]); 211 | tempa[1] = getSBoxValue(tempa[1]); 212 | tempa[2] = getSBoxValue(tempa[2]); 213 | tempa[3] = getSBoxValue(tempa[3]); 214 | } 215 | } 216 | #endif 217 | j = i * 4; k=(i - Nk) * 4; 218 | RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0]; 219 | RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1]; 220 | RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2]; 221 | RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3]; 222 | } 223 | } 224 | 225 | void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key) 226 | { 227 | KeyExpansion(ctx->RoundKey, key); 228 | } 229 | #if defined(CBC) && (CBC == 1) 230 | void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv) 231 | { 232 | KeyExpansion(ctx->RoundKey, key); 233 | memcpy (ctx->Iv, iv, AES_BLOCKLEN); 234 | } 235 | void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv) 236 | { 237 | memcpy (ctx->Iv, iv, AES_BLOCKLEN); 238 | } 239 | #endif 240 | 241 | // This function adds the round key to state. 242 | // The round key is added to the state by an XOR function. 243 | static void AddRoundKey(uint8_t round,state_t* state,uint8_t* RoundKey) 244 | { 245 | uint8_t i,j; 246 | for (i = 0; i < 4; ++i) 247 | { 248 | for (j = 0; j < 4; ++j) 249 | { 250 | (*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j]; 251 | } 252 | } 253 | } 254 | 255 | // The SubBytes Function Substitutes the values in the 256 | // state matrix with values in an S-box. 257 | static void SubBytes(state_t* state) 258 | { 259 | uint8_t i, j; 260 | for (i = 0; i < 4; ++i) 261 | { 262 | for (j = 0; j < 4; ++j) 263 | { 264 | (*state)[j][i] = getSBoxValue((*state)[j][i]); 265 | } 266 | } 267 | } 268 | 269 | // The ShiftRows() function shifts the rows in the state to the left. 270 | // Each row is shifted with different offset. 271 | // Offset = Row number. So the first row is not shifted. 272 | static void ShiftRows(state_t* state) 273 | { 274 | uint8_t temp; 275 | 276 | // Rotate first row 1 columns to left 277 | temp = (*state)[0][1]; 278 | (*state)[0][1] = (*state)[1][1]; 279 | (*state)[1][1] = (*state)[2][1]; 280 | (*state)[2][1] = (*state)[3][1]; 281 | (*state)[3][1] = temp; 282 | 283 | // Rotate second row 2 columns to left 284 | temp = (*state)[0][2]; 285 | (*state)[0][2] = (*state)[2][2]; 286 | (*state)[2][2] = temp; 287 | 288 | temp = (*state)[1][2]; 289 | (*state)[1][2] = (*state)[3][2]; 290 | (*state)[3][2] = temp; 291 | 292 | // Rotate third row 3 columns to left 293 | temp = (*state)[0][3]; 294 | (*state)[0][3] = (*state)[3][3]; 295 | (*state)[3][3] = (*state)[2][3]; 296 | (*state)[2][3] = (*state)[1][3]; 297 | (*state)[1][3] = temp; 298 | } 299 | 300 | static uint8_t xtime(uint8_t x) 301 | { 302 | return ((x<<1) ^ (((x>>7) & 1) * 0x1b)); 303 | } 304 | 305 | // MixColumns function mixes the columns of the state matrix 306 | static void MixColumns(state_t* state) 307 | { 308 | uint8_t i; 309 | uint8_t Tmp, Tm, t; 310 | for (i = 0; i < 4; ++i) 311 | { 312 | t = (*state)[i][0]; 313 | Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ; 314 | Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ; 315 | Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ; 316 | Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ; 317 | Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ; 318 | } 319 | } 320 | 321 | // Multiply is used to multiply numbers in the field GF(2^8) 322 | #if MULTIPLY_AS_A_FUNCTION 323 | static uint8_t Multiply(uint8_t x, uint8_t y) 324 | { 325 | return (((y & 1) * x) ^ 326 | ((y>>1 & 1) * xtime(x)) ^ 327 | ((y>>2 & 1) * xtime(xtime(x))) ^ 328 | ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ 329 | ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))); 330 | } 331 | #else 332 | #define Multiply(x, y) \ 333 | ( ((y & 1) * x) ^ \ 334 | ((y>>1 & 1) * xtime(x)) ^ \ 335 | ((y>>2 & 1) * xtime(xtime(x))) ^ \ 336 | ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \ 337 | ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \ 338 | 339 | #endif 340 | 341 | // MixColumns function mixes the columns of the state matrix. 342 | // The method used to multiply may be difficult to understand for the inexperienced. 343 | // Please use the references to gain more information. 344 | static void InvMixColumns(state_t* state) 345 | { 346 | int i; 347 | uint8_t a, b, c, d; 348 | for (i = 0; i < 4; ++i) 349 | { 350 | a = (*state)[i][0]; 351 | b = (*state)[i][1]; 352 | c = (*state)[i][2]; 353 | d = (*state)[i][3]; 354 | 355 | (*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09); 356 | (*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d); 357 | (*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b); 358 | (*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e); 359 | } 360 | } 361 | 362 | 363 | // The SubBytes Function Substitutes the values in the 364 | // state matrix with values in an S-box. 365 | static void InvSubBytes(state_t* state) 366 | { 367 | uint8_t i, j; 368 | for (i = 0; i < 4; ++i) 369 | { 370 | for (j = 0; j < 4; ++j) 371 | { 372 | (*state)[j][i] = getSBoxInvert((*state)[j][i]); 373 | } 374 | } 375 | } 376 | 377 | static void InvShiftRows(state_t* state) 378 | { 379 | uint8_t temp; 380 | 381 | // Rotate first row 1 columns to right 382 | temp = (*state)[3][1]; 383 | (*state)[3][1] = (*state)[2][1]; 384 | (*state)[2][1] = (*state)[1][1]; 385 | (*state)[1][1] = (*state)[0][1]; 386 | (*state)[0][1] = temp; 387 | 388 | // Rotate second row 2 columns to right 389 | temp = (*state)[0][2]; 390 | (*state)[0][2] = (*state)[2][2]; 391 | (*state)[2][2] = temp; 392 | 393 | temp = (*state)[1][2]; 394 | (*state)[1][2] = (*state)[3][2]; 395 | (*state)[3][2] = temp; 396 | 397 | // Rotate third row 3 columns to right 398 | temp = (*state)[0][3]; 399 | (*state)[0][3] = (*state)[1][3]; 400 | (*state)[1][3] = (*state)[2][3]; 401 | (*state)[2][3] = (*state)[3][3]; 402 | (*state)[3][3] = temp; 403 | } 404 | 405 | 406 | // Cipher is the main function that encrypts the PlainText. 407 | static void Cipher(state_t* state, uint8_t* RoundKey) 408 | { 409 | uint8_t round = 0; 410 | 411 | // Add the First round key to the state before starting the rounds. 412 | AddRoundKey(0, state, RoundKey); 413 | 414 | // There will be Nr rounds. 415 | // The first Nr-1 rounds are identical. 416 | // These Nr-1 rounds are executed in the loop below. 417 | for (round = 1; round < Nr; ++round) 418 | { 419 | SubBytes(state); 420 | ShiftRows(state); 421 | MixColumns(state); 422 | AddRoundKey(round, state, RoundKey); 423 | } 424 | 425 | // The last round is given below. 426 | // The MixColumns function is not here in the last round. 427 | SubBytes(state); 428 | ShiftRows(state); 429 | AddRoundKey(Nr, state, RoundKey); 430 | } 431 | 432 | static void InvCipher(state_t* state,uint8_t* RoundKey) 433 | { 434 | uint8_t round = 0; 435 | 436 | // Add the First round key to the state before starting the rounds. 437 | AddRoundKey(Nr, state, RoundKey); 438 | 439 | // There will be Nr rounds. 440 | // The first Nr-1 rounds are identical. 441 | // These Nr-1 rounds are executed in the loop below. 442 | for (round = (Nr - 1); round > 0; --round) 443 | { 444 | InvShiftRows(state); 445 | InvSubBytes(state); 446 | AddRoundKey(round, state, RoundKey); 447 | InvMixColumns(state); 448 | } 449 | 450 | // The last round is given below. 451 | // The MixColumns function is not here in the last round. 452 | InvShiftRows(state); 453 | InvSubBytes(state); 454 | AddRoundKey(0, state, RoundKey); 455 | } 456 | 457 | 458 | /*****************************************************************************/ 459 | /* Public functions: */ 460 | /*****************************************************************************/ 461 | #if defined(ECB) && (ECB == 1) 462 | 463 | 464 | void AES_ECB_encrypt(struct AES_ctx *ctx,const uint8_t* buf) 465 | { 466 | // The next function call encrypts the PlainText with the Key using AES algorithm. 467 | Cipher((state_t*)buf, ctx->RoundKey); 468 | } 469 | 470 | void AES_ECB_decrypt(struct AES_ctx* ctx,const uint8_t* buf) 471 | { 472 | // The next function call decrypts the PlainText with the Key using AES algorithm. 473 | InvCipher((state_t*)buf, ctx->RoundKey); 474 | } 475 | 476 | 477 | #endif // #if defined(ECB) && (ECB == 1) 478 | 479 | 480 | 481 | 482 | 483 | #if defined(CBC) && (CBC == 1) 484 | 485 | 486 | static void XorWithIv(uint8_t* buf, uint8_t* Iv) 487 | { 488 | uint8_t i; 489 | for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size 490 | { 491 | buf[i] ^= Iv[i]; 492 | } 493 | } 494 | 495 | void AES_CBC_encrypt_buffer(struct AES_ctx *ctx,uint8_t* buf, uint32_t length) 496 | { 497 | uintptr_t i; 498 | uint8_t *Iv = ctx->Iv; 499 | for (i = 0; i < length; i += AES_BLOCKLEN) 500 | { 501 | XorWithIv(buf, Iv); 502 | Cipher((state_t*)buf, ctx->RoundKey); 503 | Iv = buf; 504 | buf += AES_BLOCKLEN; 505 | //printf("Step %d - %d", i/16, i); 506 | } 507 | /* store Iv in ctx for next call */ 508 | memcpy(ctx->Iv, Iv, AES_BLOCKLEN); 509 | } 510 | 511 | void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length) 512 | { 513 | uintptr_t i; 514 | uint8_t storeNextIv[AES_BLOCKLEN]; 515 | for (i = 0; i < length; i += AES_BLOCKLEN) 516 | { 517 | memcpy(storeNextIv, buf, AES_BLOCKLEN); 518 | InvCipher((state_t*)buf, ctx->RoundKey); 519 | XorWithIv(buf, ctx->Iv); 520 | memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN); 521 | buf += AES_BLOCKLEN; 522 | } 523 | 524 | } 525 | 526 | #endif // #if defined(CBC) && (CBC == 1) 527 | 528 | 529 | 530 | #if defined(CTR) && (CTR == 1) 531 | 532 | /* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */ 533 | void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length) 534 | { 535 | uint8_t buffer[AES_BLOCKLEN]; 536 | 537 | unsigned i; 538 | int bi; 539 | for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi) 540 | { 541 | if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */ 542 | { 543 | 544 | memcpy(buffer, ctx->Iv, AES_BLOCKLEN); 545 | Cipher((state_t*)buffer,ctx->RoundKey); 546 | 547 | /* Increment Iv and handle overflow */ 548 | for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi) 549 | { 550 | /* inc will owerflow */ 551 | if (ctx->Iv[bi] == 255) 552 | { 553 | ctx->Iv[bi] = 0; 554 | continue; 555 | } 556 | ctx->Iv[bi] += 1; 557 | break; 558 | } 559 | bi = 0; 560 | } 561 | 562 | buf[i] = (buf[i] ^ buffer[bi]); 563 | } 564 | } 565 | 566 | #endif // #if defined(CTR) && (CTR == 1) 567 | -------------------------------------------------------------------------------- /libstlinkloader/src/aes.h: -------------------------------------------------------------------------------- 1 | #ifndef _AES_H_ 2 | #define _AES_H_ 3 | 4 | #include 5 | 6 | // #define the macros below to 1/0 to enable/disable the mode of operation. 7 | // 8 | // CBC enables AES encryption in CBC-mode of operation. 9 | // CTR enables encryption in counter-mode. 10 | // ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously. 11 | 12 | // The #ifndef-guard allows it to be configured before #include'ing or at compile time. 13 | #ifndef CBC 14 | #define CBC 1 15 | #endif 16 | 17 | #ifndef ECB 18 | #define ECB 1 19 | #endif 20 | 21 | #ifndef CTR 22 | #define CTR 1 23 | #endif 24 | 25 | 26 | #define AES128 1 27 | //#define AES192 1 28 | //#define AES256 1 29 | 30 | #define AES_BLOCKLEN 16 //Block length in bytes AES is 128b block only 31 | 32 | #if defined(AES256) && (AES256 == 1) 33 | #define AES_KEYLEN 32 34 | #define AES_keyExpSize 240 35 | #elif defined(AES192) && (AES192 == 1) 36 | #define AES_KEYLEN 24 37 | #define AES_keyExpSize 208 38 | #else 39 | #define AES_KEYLEN 16 // Key length in bytes 40 | #define AES_keyExpSize 176 41 | #endif 42 | 43 | struct AES_ctx 44 | { 45 | uint8_t RoundKey[AES_keyExpSize]; 46 | #if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) 47 | uint8_t Iv[AES_BLOCKLEN]; 48 | #endif 49 | }; 50 | 51 | void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key); 52 | #if defined(CBC) && (CBC == 1) 53 | void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv); 54 | void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv); 55 | #endif 56 | 57 | #if defined(ECB) && (ECB == 1) 58 | // buffer size is exactly AES_BLOCKLEN bytes; 59 | // you need only AES_init_ctx as IV is not used in ECB 60 | // NB: ECB is considered insecure for most uses 61 | void AES_ECB_encrypt(struct AES_ctx* ctx, const uint8_t* buf); 62 | void AES_ECB_decrypt(struct AES_ctx* ctx, const uint8_t* buf); 63 | 64 | #endif // #if defined(ECB) && (ECB == !) 65 | 66 | 67 | #if defined(CBC) && (CBC == 1) 68 | // buffer size MUST be mutile of AES_BLOCKLEN; 69 | // Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme 70 | // NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv() 71 | // no IV should ever be reused with the same key 72 | void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); 73 | void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); 74 | 75 | #endif // #if defined(CBC) && (CBC == 1) 76 | 77 | 78 | #if defined(CTR) && (CTR == 1) 79 | 80 | // Same function for encrypting as for decrypting. 81 | // IV is incremented for every block, and used after encryption as XOR-compliment for output 82 | // Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme 83 | // NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv() 84 | // no IV should ever be reused with the same key 85 | void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); 86 | 87 | #endif // #if defined(CTR) && (CTR == 1) 88 | 89 | 90 | #endif //_AES_H_ 91 | 92 | -------------------------------------------------------------------------------- /libstlinkloader/src/crypto.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "aes.h" 6 | #include "crypto.h" 7 | 8 | static void convert_to_big_endian(unsigned char *array, unsigned int length); 9 | 10 | void my_encrypt(unsigned char *key, unsigned char *data, unsigned int length) { 11 | struct AES_ctx ctx; 12 | unsigned char key_be[16]; 13 | size_t i; 14 | 15 | memcpy(key_be, key, 16); 16 | convert_to_big_endian(key_be, 16); 17 | 18 | AES_init_ctx(&ctx, key_be); 19 | 20 | convert_to_big_endian(data, length); 21 | 22 | for (i = 0; i < length; i += 16) { 23 | AES_ECB_encrypt(&ctx, data+i); 24 | } 25 | convert_to_big_endian(data, length); 26 | } 27 | 28 | static void convert_to_big_endian(unsigned char *array, unsigned int length) { 29 | unsigned int i; 30 | 31 | for (i = 0; i < length; i += 4) { 32 | *(uint32_t*)(array+i) = htonl(*(uint32_t*)(array+i)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /libstlinkloader/src/crypto.h: -------------------------------------------------------------------------------- 1 | void my_encrypt(unsigned char *key, unsigned char *data, unsigned int length); 2 | -------------------------------------------------------------------------------- /libstlinkloader/src/defines.h: -------------------------------------------------------------------------------- 1 | #define STLINK_VID 0x0483 2 | #define STLINK_PID 0x3748 3 | 4 | #define EP_IN 1 | LIBUSB_ENDPOINT_IN 5 | #define EP_OUT 2 | LIBUSB_ENDPOINT_OUT 6 | 7 | #define USB_TIMEOUT 100 8 | 9 | #define DFU_DETACH 0x00 10 | #define DFU_DNLOAD 0x01 11 | #define DFU_UPLOAD 0x02 12 | #define DFU_GETSTATUS 0x03 13 | #define DFU_CLRSTATUS 0x04 14 | #define DFU_GETSTATE 0x05 15 | #define DFU_ABORT 0x06 16 | 17 | #define GET_COMMAND 0x00 18 | #define SET_ADDRESS_POINTER_COMMAND 0x21 19 | #define ERASE_COMMAND 0x41 20 | #define READ_UNPROTECT_COMMAND 0x92 21 | -------------------------------------------------------------------------------- /libstlinkloader/src/dfu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "defines.h" 8 | #include "types.h" 9 | #include "crypto.h" 10 | #include "dfu.h" 11 | 12 | static int dfu_status(struct stlinkloadercontext *context, 13 | struct DFUStatus *status); 14 | static uint16_t checksum(const unsigned char *firmware, 15 | size_t len); 16 | 17 | int current_mode(struct stlinkloadercontext *context) { 18 | unsigned char data[16]; 19 | int rw_bytes, res; 20 | 21 | memset(data, 0, sizeof(data)); 22 | 23 | data[0] = 0xF5; 24 | 25 | /* Write */ 26 | res = libusb_bulk_transfer(context->usb_dev_handle, 27 | EP_OUT, 28 | data, 29 | sizeof(data), 30 | &rw_bytes, 31 | USB_TIMEOUT); 32 | if (res) { 33 | return -1; 34 | } 35 | 36 | /* Read */ 37 | libusb_bulk_transfer(context->usb_dev_handle, 38 | EP_IN, 39 | data, 40 | 2, 41 | &rw_bytes, 42 | USB_TIMEOUT); 43 | if (res) { 44 | return -1; 45 | } 46 | 47 | return data[0] << 8 | data[1]; 48 | } 49 | 50 | int dfu_download(struct stlinkloadercontext *context, 51 | unsigned char *data, 52 | size_t data_len, 53 | uint16_t wBlockNum) { 54 | unsigned char download_request[16]; 55 | struct DFUStatus status; 56 | int rw_bytes, res; 57 | 58 | memset(download_request, 0, sizeof(download_request)); 59 | 60 | download_request[0] = 0xF3; 61 | download_request[1] = DFU_DNLOAD; 62 | *(uint16_t*)(download_request+2) = wBlockNum; /* wValue */ 63 | *(uint16_t*)(download_request+4) = checksum(data, data_len); /* wIndex */ 64 | *(uint16_t*)(download_request+6) = data_len; /* wLength */ 65 | 66 | if (wBlockNum >= 2) { 67 | my_encrypt(context->firmware_key, data, data_len); 68 | } 69 | 70 | res = libusb_bulk_transfer(context->usb_dev_handle, 71 | EP_OUT, 72 | download_request, 73 | sizeof(download_request), 74 | &rw_bytes, 75 | USB_TIMEOUT); 76 | if (res || rw_bytes != sizeof(download_request)) { 77 | return -1; 78 | } 79 | 80 | res = libusb_bulk_transfer(context->usb_dev_handle, 81 | EP_OUT, 82 | data, 83 | data_len, 84 | &rw_bytes, 85 | USB_TIMEOUT); 86 | if (res || rw_bytes != (int)data_len) { 87 | return -1; 88 | } 89 | 90 | if (dfu_status(context, &status)) { 91 | return -1; 92 | } 93 | 94 | if (status.bState != dfuDNBUSY) { 95 | return -2; 96 | } 97 | 98 | if (status.bStatus != OK) { 99 | return -3; 100 | } 101 | 102 | usleep(status.bwPollTimeout * 1000); 103 | 104 | if (dfu_status(context, &status)) { 105 | return -1; 106 | } 107 | 108 | if (status.bState != dfuDNLOAD_IDLE) { 109 | return -3; 110 | } 111 | 112 | return 0; 113 | } 114 | 115 | static int dfu_status(struct stlinkloadercontext *context, 116 | struct DFUStatus *status) { 117 | unsigned char data[16]; 118 | int rw_bytes, res; 119 | 120 | memset(data, 0, sizeof(data)); 121 | 122 | data[0] = 0xF3; 123 | data[1] = DFU_GETSTATUS; 124 | data[6] = 0x06; /* wLength */ 125 | 126 | res = libusb_bulk_transfer(context->usb_dev_handle, 127 | EP_OUT, 128 | data, 129 | 16, 130 | &rw_bytes, 131 | USB_TIMEOUT); 132 | if (res || rw_bytes != 16) { 133 | return -1; 134 | } 135 | res = libusb_bulk_transfer(context->usb_dev_handle, 136 | EP_IN, 137 | data, 138 | 6, 139 | &rw_bytes, 140 | USB_TIMEOUT); 141 | if (res || rw_bytes != 6) { 142 | return -1; 143 | } 144 | 145 | status->bStatus = data[0]; 146 | status->bwPollTimeout = data[1] | data[2] << 8 | data[3] << 16; 147 | status->bState = data[4]; 148 | status->iString = data[5]; 149 | 150 | return 0; 151 | } 152 | 153 | static uint16_t checksum(const unsigned char *firmware, 154 | size_t len) { 155 | unsigned int i; 156 | int ret = 0; 157 | 158 | for (i = 0; i < len; i++) { 159 | ret += firmware[i]; 160 | } 161 | 162 | return (uint16_t)ret & 0xFFFF; 163 | } 164 | -------------------------------------------------------------------------------- /libstlinkloader/src/dfu.h: -------------------------------------------------------------------------------- 1 | int dfu_download(struct stlinkloadercontext *context, 2 | unsigned char *data, 3 | size_t data_len, 4 | uint16_t wBlockNum); 5 | int current_mode(struct stlinkloadercontext *context); 6 | -------------------------------------------------------------------------------- /libstlinkloader/src/flashing.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "defines.h" 12 | #include "types.h" 13 | #include "dfu.h" 14 | 15 | static int set_address(struct stlinkloadercontext *context, 16 | uint32_t address) { 17 | unsigned char set_address_command[5]; 18 | int res; 19 | 20 | set_address_command[0] = SET_ADDRESS_POINTER_COMMAND; 21 | set_address_command[1] = address & 0xFF; 22 | set_address_command[2] = (address >> 8) & 0xFF; 23 | set_address_command[3] = (address >> 16) & 0xFF; 24 | set_address_command[4] = (address >> 24) & 0xFF; 25 | 26 | res = dfu_download(context, 27 | set_address_command, 28 | sizeof(set_address_command), 0); 29 | return res; 30 | } 31 | 32 | static int erase(struct stlinkloadercontext *context, 33 | uint32_t address) { 34 | unsigned char erase_command[5]; 35 | int res; 36 | 37 | erase_command[0] = ERASE_COMMAND; 38 | erase_command[1] = address & 0xFF; 39 | erase_command[2] = (address >> 8) & 0xFF; 40 | erase_command[3] = (address >> 16) & 0xFF; 41 | erase_command[4] = (address >> 24) & 0xFF; 42 | 43 | res = dfu_download(context, 44 | erase_command, 45 | sizeof(erase_command), 0); 46 | 47 | return res; 48 | } 49 | 50 | int stlinkloader_flash(struct stlinkloadercontext *context, 51 | const char *filename, 52 | unsigned int base_offset, 53 | unsigned int chunk_size, 54 | progress_func progress_callback, 55 | gpointer user_data) { 56 | unsigned char *firmware, firmware_chunk[chunk_size]; 57 | unsigned int cur_chunk_size, flashed_bytes, file_size; 58 | int fd, res; 59 | struct stat firmware_stat; 60 | 61 | fd = open(filename, O_RDONLY); 62 | if (fd == -1) { 63 | return -1; 64 | } 65 | 66 | fstat(fd, &firmware_stat); 67 | 68 | file_size = firmware_stat.st_size; 69 | 70 | firmware = mmap(NULL, file_size, PROT_WRITE, MAP_PRIVATE, fd, 0); 71 | if (firmware == MAP_FAILED) { 72 | close(fd); 73 | return -1; 74 | } 75 | 76 | flashed_bytes = 0; 77 | 78 | while (flashed_bytes < file_size) { 79 | if ((flashed_bytes+chunk_size) > file_size) { 80 | cur_chunk_size = file_size - flashed_bytes; 81 | } else { 82 | cur_chunk_size = chunk_size; 83 | } 84 | 85 | res = erase(context, base_offset+flashed_bytes); 86 | if (res) { 87 | munmap(firmware, file_size); 88 | close(fd); 89 | return -2; 90 | } 91 | 92 | res = set_address(context, base_offset+flashed_bytes); 93 | if (res) { 94 | munmap(firmware, file_size); 95 | close(fd); 96 | return -3; 97 | } 98 | 99 | memcpy(firmware_chunk, firmware+flashed_bytes, cur_chunk_size); 100 | memset(firmware_chunk+cur_chunk_size, 0, chunk_size-cur_chunk_size); 101 | res = dfu_download(context, firmware_chunk, cur_chunk_size, 2); 102 | if (res) { 103 | munmap(firmware, file_size); 104 | close(fd); 105 | return res; 106 | } 107 | 108 | flashed_bytes += cur_chunk_size; 109 | 110 | progress_callback((double)flashed_bytes/(double)file_size, user_data); 111 | } 112 | 113 | munmap(firmware, file_size); 114 | close(fd); 115 | 116 | return 0; 117 | } 118 | -------------------------------------------------------------------------------- /libstlinkloader/src/stlinkloader.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "defines.h" 8 | #include "types.h" 9 | #include "crypto.h" 10 | #include "dfu.h" 11 | 12 | struct stlinkloadercontext* stlinkloader_init(void) { 13 | struct stlinkloadercontext *context; 14 | int res; 15 | 16 | context = malloc(sizeof(struct stlinkloadercontext)); 17 | if (context == NULL) { 18 | return NULL; 19 | } 20 | 21 | res = libusb_init(&(context->usb_context)); 22 | if (res != 0) { 23 | free(context); 24 | return NULL; 25 | } 26 | 27 | context->usb_dev_handle = libusb_open_device_with_vid_pid(context->usb_context, 28 | STLINK_VID, 29 | STLINK_PID); 30 | if (context->usb_dev_handle == NULL) { 31 | libusb_exit(context->usb_context); 32 | free(context); 33 | return NULL; 34 | } 35 | 36 | return context; 37 | } 38 | 39 | void stlinkloader_free(struct stlinkloadercontext *context) { 40 | libusb_close(context->usb_dev_handle); 41 | libusb_exit(context->usb_context); 42 | free(context); 43 | } 44 | 45 | int stlinkloader_read_infos(struct stlinkloadercontext *context) { 46 | unsigned char data[20]; 47 | int res, rw_bytes; 48 | 49 | memset(data, 0, sizeof(data)); 50 | 51 | data[0] = 0xF1; 52 | data[1] = 0x80; 53 | 54 | /* Write */ 55 | res = libusb_bulk_transfer(context->usb_dev_handle, 56 | EP_OUT, 57 | data, 58 | 16, 59 | &rw_bytes, 60 | USB_TIMEOUT); 61 | if (res) { 62 | return -1; 63 | } 64 | 65 | /* Read */ 66 | res = libusb_bulk_transfer(context->usb_dev_handle, 67 | EP_IN, 68 | data, 69 | 6, 70 | &rw_bytes, 71 | USB_TIMEOUT); 72 | if (res) { 73 | return -1; 74 | } 75 | 76 | context->stlink_version = data[0] >> 4; 77 | context->jtag_version = (data[0] & 0x0F) << 2 | (data[1] & 0xC0) >> 6; 78 | context->swim_version = data[1] & 0x3F; 79 | context->loader_version = data[5] << 8 | data[4]; 80 | 81 | memset(data, 0, sizeof(data)); 82 | 83 | data[0] = 0xF3; 84 | data[1] = 0x08; 85 | 86 | /* Write */ 87 | res = libusb_bulk_transfer(context->usb_dev_handle, 88 | EP_OUT, 89 | data, 90 | 16, 91 | &rw_bytes, 92 | USB_TIMEOUT); 93 | if (res) { 94 | return -1; 95 | } 96 | 97 | /* Read */ 98 | libusb_bulk_transfer(context->usb_dev_handle, 99 | EP_IN, 100 | data, 101 | 20, 102 | &rw_bytes, 103 | USB_TIMEOUT); 104 | if (res) { 105 | return -1; 106 | } 107 | 108 | memcpy(context->id, data+8, 12); 109 | 110 | /* Firmware encryption key generation */ 111 | memcpy(context->firmware_key, data, 4); 112 | memcpy(context->firmware_key+4, data+8, 12); 113 | my_encrypt((unsigned char*)"I am key, wawawa", context->firmware_key, 16); 114 | 115 | return 0; 116 | } 117 | 118 | int stlinkloader_is_correct_mode(struct stlinkloadercontext *context) { 119 | int res; 120 | 121 | res = current_mode(context); 122 | 123 | if (res == 1) { 124 | return 1; 125 | } else if (res >= 0) { 126 | return 0; 127 | } else { 128 | return -1; 129 | } 130 | } 131 | 132 | uint8_t* stlinkloader_get_id(struct stlinkloadercontext *context, int *id_length) { 133 | if (id_length != NULL) { 134 | *id_length = 12; 135 | } 136 | 137 | return context->id; 138 | } 139 | 140 | uint8_t stlinkloader_get_stlink_version(struct stlinkloadercontext *context) { 141 | return context->stlink_version; 142 | } 143 | 144 | uint8_t stlinkloader_get_jtag_version(struct stlinkloadercontext *context) { 145 | return context->jtag_version; 146 | } 147 | 148 | uint8_t stlinkloader_get_swim_version(struct stlinkloadercontext *context) { 149 | return context->swim_version; 150 | } 151 | -------------------------------------------------------------------------------- /libstlinkloader/src/types.h: -------------------------------------------------------------------------------- 1 | struct stlinkloadercontext { 2 | libusb_context *usb_context; 3 | libusb_device_handle *usb_dev_handle; 4 | 5 | uint8_t firmware_key[16]; 6 | uint8_t id[12]; 7 | uint8_t stlink_version; 8 | uint8_t jtag_version; 9 | uint8_t swim_version; 10 | uint16_t loader_version; 11 | }; 12 | 13 | enum DeviceStatus { 14 | OK = 0x00, 15 | errTARGET = 0x01, 16 | errFILE = 0x02, 17 | errWRITE = 0x03, 18 | errERASE = 0x04, 19 | errCHECK_ERASED = 0x05, 20 | errPROG = 0x06, 21 | errVERIFY = 0x07, 22 | errADDRESS = 0x08, 23 | errNOTDONE = 0x09, 24 | errFIRMWARE = 0x0A, 25 | errVENDOR = 0x0B, 26 | errUSBR = 0x0C, 27 | errPOR = 0x0D, 28 | errUNKNOWN = 0x0E, 29 | errSTALLEDPKT = 0x0F 30 | }; 31 | 32 | enum DeviceState { 33 | appIDLE = 0, 34 | appDETACH = 1, 35 | dfuIDLE = 2, 36 | dfuDNLOAD_SYNC = 3, 37 | dfuDNBUSY = 4, 38 | dfuDNLOAD_IDLE = 5, 39 | dfuMANIFEST_SYNC = 6, 40 | dfuMANIFEST = 7, 41 | dfuMANIFEST_WAIT_RESET = 8, 42 | dfuUPLOAD_IDLE = 9, 43 | dfuERROR = 10 44 | }; 45 | 46 | struct DFUStatus { 47 | enum DeviceStatus bStatus : 8; 48 | unsigned int bwPollTimeout : 24; 49 | enum DeviceState bState : 8; 50 | unsigned char iString : 8; 51 | }; 52 | 53 | -------------------------------------------------------------------------------- /libstlinkloader/stlinkloader.vapi: -------------------------------------------------------------------------------- 1 | [CCode (cheader_filename = "stlinkloader.h")] 2 | namespace STLinkLoader { 3 | [Compact] 4 | [CCode (cname="struct stlinkloadercontext", destroy_function="stlinkloader_free", cprefix="stlinkloader_", has_type_id = false)] 5 | public class Context { 6 | [CCode (cname="progress_func")] 7 | public delegate void ProgressFunc(double progress); 8 | 9 | [CCode (cname = "stlinkloader_init")] 10 | public Context(); 11 | 12 | public int is_correct_mode(); 13 | public int read_infos(); 14 | public unowned uint8[] get_id(); 15 | public string get_id_str() { 16 | uint8[] id = this.get_id(); 17 | GLib.StringBuilder builder = new GLib.StringBuilder (); 18 | 19 | for (int i = 0; i < 12; i++) { 20 | builder.append("%02X".printf(id[i])); 21 | } 22 | 23 | return builder.str; 24 | } 25 | public uint8 get_stlink_version(); 26 | public uint8 get_jtag_version(); 27 | public uint8 get_swim_version(); 28 | public int flash(string filename, uint base_offset, uint chunk_size, ProgressFunc progress_callback); 29 | } 30 | } -------------------------------------------------------------------------------- /res/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleGetInfoString 6 | JTAG in a box 7 | CFBundleExecutable 8 | jtaginabox 9 | CFBundleIdentifier 10 | com.jeanthomas.jtaginabox 11 | CFBundleName 12 | jtaginabox 13 | CFBundleIconFile 14 | jtaginabox.icns 15 | CFBundleShortVersionString 16 | 0.01 17 | CFBundleInfoDictionaryVersion 18 | 6.0 19 | CFBundlePackageType 20 | APPL 21 | IFMajorVersion 22 | 0 23 | IFMinorVersion 24 | 1 25 | 26 | -------------------------------------------------------------------------------- /res/appimagelibdep.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | blacklist=$(wget --no-check-certificate https://raw.githubusercontent.com/probonopd/AppImages/master/excludelist -O - | sort | uniq | grep -v "^#.*" | grep "[^-\s]") 4 | blacklist_regex=`echo $blacklist | tr " " "|" | sed 's/\./\\\\./g'` 5 | 6 | if [ $# -lt 1 ] 7 | then 8 | echo "Usage: $0 path/to/executable" 9 | else 10 | ldd $1 | grep -o '\W/[^ ]*' | grep -vE $blacklist_regex | tr " " "\n" 11 | fi 12 | 13 | -------------------------------------------------------------------------------- /res/css/jtaginabox.css: -------------------------------------------------------------------------------- 1 | .overlayspinner { 2 | background-color: rgba(0,0,0,0.8); 3 | color: white; 4 | padding: 20px; 5 | font-size: 16px; 6 | border-radius: 7px; 7 | font-weight: 300; 8 | } 9 | 10 | .stlinkinfobox { 11 | background-color: white; 12 | color: black; 13 | } 14 | 15 | -------------------------------------------------------------------------------- /res/jtaginabox.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # https://stackoverflow.com/questions/59895/getting-the-source-directory-of-a-bash-script-from-within 4 | pushd . > /dev/null 5 | SCRIPT_PATH="${BASH_SOURCE[0]}" 6 | if ([ -h "${SCRIPT_PATH}" ]); then 7 | while([ -h "${SCRIPT_PATH}" ]); do cd `dirname "$SCRIPT_PATH"`; 8 | SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done 9 | fi 10 | cd `dirname ${SCRIPT_PATH}` > /dev/null 11 | SCRIPT_PATH=`pwd`; 12 | popd > /dev/null 13 | 14 | PATH=$SCRIPT_PATH:$PATH 15 | export PATH 16 | jtaginabox-bin -------------------------------------------------------------------------------- /res/resources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ui/Homepage.ui 5 | ui/overlayspinner.ui 6 | ui/stlinkinfobox.ui 7 | css/jtaginabox.css 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /res/runtime-i686: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanthom/jtaginabox/cad17c32d7bb7b55cf26b27caa00ab4647b3db76/res/runtime-i686 -------------------------------------------------------------------------------- /res/runtime-x86_64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeanthom/jtaginabox/cad17c32d7bb7b55cf26b27caa00ab4647b3db76/res/runtime-x86_64 -------------------------------------------------------------------------------- /res/ui/Homepage.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /res/ui/overlayspinner.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 29 | 30 | -------------------------------------------------------------------------------- /res/ui/stlinkinfobox.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 77 | 78 | -------------------------------------------------------------------------------- /src/Flashing.vala: -------------------------------------------------------------------------------- 1 | namespace JTAGInABox { 2 | [GtkTemplate (ui="/ui/overlayspinner.ui")] 3 | public class OverlaySpinner : Gtk.Box { 4 | [GtkChild] Gtk.Spinner spinner; 5 | [GtkChild] Gtk.Label info; 6 | 7 | public void set_spinner(bool status) { 8 | if (status) { 9 | this.spinner.show(); 10 | } else { 11 | this.spinner.hide(); 12 | } 13 | } 14 | 15 | public void set_text(string text) { 16 | this.info.set_label(text); 17 | } 18 | } 19 | 20 | [GtkTemplate (ui="/ui/stlinkinfobox.ui")] 21 | public class STLinkInfoBox : Gtk.Frame { 22 | [GtkChild] Gtk.Label infos_label; 23 | 24 | [GtkChild] Gtk.Label progress_label; 25 | [GtkChild] Gtk.ProgressBar progress_bar; 26 | 27 | [GtkChild] Gtk.Button install_button; 28 | 29 | public signal void install_action(); 30 | 31 | public STLinkInfoBox() { 32 | this.install_button.clicked.connect( 33 | () => { 34 | this.install_action(); 35 | }); 36 | } 37 | 38 | public void show_buttons(bool active) { 39 | if (active) { 40 | this.install_button.show(); 41 | } else { 42 | this.install_button.hide(); 43 | } 44 | } 45 | 46 | public void show_progress_bar(bool active) { 47 | if (active) { 48 | this.progress_bar.show(); 49 | this.progress_label.show(); 50 | } else { 51 | this.progress_bar.hide(); 52 | this.progress_label.hide(); 53 | } 54 | } 55 | 56 | public void set_infos(string serial, string version) { 57 | this.infos_label.label = "Serial : %s\nVersion : %s".printf(serial, version); 58 | } 59 | 60 | public void set_progress_bar(double level) { 61 | this.progress_bar.set_fraction(level); 62 | if (level == 1.0f) { 63 | this.progress_label.label = "Complete!"; 64 | } else { 65 | this.progress_label.label = "%02d%%".printf((int)(level * 100)); 66 | } 67 | this.progress_bar.queue_draw(); 68 | } 69 | 70 | public void set_progress_label(string text) { 71 | this.progress_label.label = text; 72 | } 73 | } 74 | 75 | public class Flashing : Gtk.Box { 76 | STLink* stlink = null; 77 | STLinkLoader.Context *loader_ctx = null; 78 | STLinkInfoBox stlinkInfoBox; 79 | OverlaySpinner overlay_spinner; 80 | bool should_wait_for_dongle = true; 81 | 82 | public Flashing() { 83 | Object(orientation: Gtk.Orientation.VERTICAL, spacing: 0); 84 | 85 | this.overlay_spinner = new OverlaySpinner(); 86 | this.overlay_spinner.set_text("Waiting for ST-Link dongle..."); 87 | this.set_center_widget(this.overlay_spinner); 88 | 89 | this.stlink = new STLink(); 90 | 91 | this.wait_for_dongle.begin((obj, res) => { 92 | this.wait_for_dongle.end(res); 93 | }); 94 | 95 | this.destroy.connect(() => { 96 | this.should_wait_for_dongle = false; 97 | }); 98 | } 99 | 100 | ~Flashing() { 101 | if (this.stlink != null) { 102 | delete this.stlink; 103 | } 104 | if (this.loader_ctx != null) { 105 | delete this.loader_ctx; 106 | } 107 | } 108 | 109 | private async void wait_for_dongle() { 110 | new Thread(null, () => { 111 | while (this.should_wait_for_dongle) { 112 | if (this.stlink->detect_vid_pid(0x0483, 0x3748)) { 113 | if (this.stlink->can_claim_vid_pid(0x0483, 0x3748, 0)) { 114 | GLib.MainContext.default().invoke( 115 | () => { 116 | this.should_wait_for_dongle = false; 117 | this.remove(this.overlay_spinner); 118 | this.overlay_spinner.destroy(); 119 | this.set_stlink_info_box(); 120 | return GLib.Source.REMOVE; 121 | }); 122 | } else { 123 | GLib.MainContext.default().invoke( 124 | () => { 125 | this.should_wait_for_dongle = false; 126 | this.overlay_spinner.set_text("Cannot connect correctly to ST-Link dongle.\nPlease start JTAG in a box as root user."); 127 | this.overlay_spinner.set_spinner(false); 128 | this.queue_draw(); /* Force redrawing otherwise the overlay borders are messy */ 129 | return GLib.Source.REMOVE; 130 | }); 131 | } 132 | } else if (this.stlink->detect_vid_pid(0x1209, 0xC0CA)) { 133 | GLib.MainContext.default().invoke( 134 | () => { 135 | this.overlay_spinner.set_text("Please unplug and replug your dongle..."); 136 | this.queue_draw(); /* Force redrawing otherwise the overlay borders are messy */ 137 | return GLib.Source.REMOVE; 138 | }); 139 | } 140 | 141 | Thread.usleep(10000); 142 | } 143 | 144 | return 0; 145 | }); 146 | 147 | yield; 148 | } 149 | 150 | private void set_stlink_info_box() { 151 | delete this.stlink; 152 | this.stlink = null; 153 | 154 | loader_ctx = new STLinkLoader.Context(); 155 | if (loader_ctx == null) { 156 | return; 157 | } 158 | loader_ctx->read_infos(); 159 | 160 | this.stlinkInfoBox = new STLinkInfoBox(); 161 | this.stlinkInfoBox.install_action.connect(this.upload_firmware); 162 | this.stlinkInfoBox.show_progress_bar(false); 163 | this.stlinkInfoBox.set_infos( 164 | loader_ctx->get_id_str(), 165 | "V%dJ%dS%d".printf( 166 | loader_ctx->get_stlink_version(), 167 | loader_ctx->get_jtag_version(), 168 | loader_ctx->get_swim_version() 169 | ) 170 | ); 171 | 172 | this.add(this.stlinkInfoBox); 173 | this.stlinkInfoBox.show_buttons(true); 174 | this.stlinkInfoBox.show(); 175 | } 176 | 177 | private void upload_firmware() { 178 | this.stlinkInfoBox.show_progress_bar(true); 179 | this.stlinkInfoBox.set_progress_bar(0.0f); 180 | this.stlinkInfoBox.show_buttons(false); 181 | new Thread(null, () => { 182 | int res = this.loader_ctx->flash("dirtyjtag.bin", 0x08004000, 1024, 183 | (progress) => { 184 | GLib.MainContext.default().invoke( 185 | () => { 186 | this.stlinkInfoBox.set_progress_bar(progress); 187 | return GLib.Source.REMOVE; 188 | }); 189 | }); 190 | GLib.MainContext.default().invoke( 191 | () => { 192 | if (res == 0) { 193 | this.stlinkInfoBox.set_progress_bar(1.0f); 194 | } else { 195 | this.stlinkInfoBox.set_progress_label("An error occured during firmware flashing (code=%d)".printf(res)); 196 | } 197 | 198 | return GLib.Source.REMOVE; 199 | }); 200 | return 0; 201 | }); 202 | } 203 | } 204 | } -------------------------------------------------------------------------------- /src/Homepage.vala: -------------------------------------------------------------------------------- 1 | namespace JTAGInABox { 2 | public class ActionList : Gtk.Frame { 3 | Gtk.ListBox listbox = null; 4 | 5 | public signal void action_chosen(string identifier); 6 | 7 | public ActionList() { 8 | Object(label: null); 9 | 10 | this.shadow_type = Gtk.ShadowType.IN; 11 | this.listbox = new Gtk.ListBox(); 12 | this.listbox.set_activate_on_single_click(true); 13 | this.listbox.set_selection_mode(Gtk.SelectionMode.NONE); 14 | this.listbox.row_activated.connect(row_activated); 15 | 16 | this.add(listbox); 17 | } 18 | 19 | public void add_action(Action action) { 20 | this.listbox.add(action); 21 | } 22 | 23 | private void row_activated(Gtk.ListBoxRow row) { 24 | string identifier = row.get_name(); 25 | 26 | if (identifier != null) { 27 | action_chosen(identifier); 28 | } 29 | } 30 | } 31 | 32 | public class Action : Gtk.ListBoxRow { 33 | Gtk.Box container = null; 34 | Gtk.Label actionLabel = null; 35 | Gtk.Image icon = null; 36 | 37 | public Action(string identifier, string action_name, string icon_name) { 38 | Object(); 39 | 40 | this.container = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); 41 | 42 | this.icon = new Gtk.Image.from_icon_name(icon_name, Gtk.IconSize.DIALOG); 43 | this.icon.margin = 15; 44 | this.container.add(this.icon); 45 | 46 | this.actionLabel = new Gtk.Label(action_name); 47 | this.container.add(this.actionLabel); 48 | 49 | this.add(this.container); 50 | this.set_name(identifier); 51 | } 52 | } 53 | 54 | [GtkTemplate (ui="/ui/Homepage.ui")] 55 | public class Homepage : Gtk.Box { 56 | ActionList actionList; 57 | Action flashAction; 58 | Action urjtagAction; 59 | 60 | public signal void action_chosen(string identifier); 61 | 62 | public Homepage() { 63 | /* Action list (flash DirtyJTAG, launch UrJTAG) */ 64 | this.actionList = new ActionList(); 65 | this.flashAction = new Action("flash", "Install DirtyJTAG on an ST-Link v2 dongle", "document-save"); 66 | this.urjtagAction = new Action("urjtag", "Launch UrJTAG", "utilities-terminal"); 67 | this.actionList.add_action(this.flashAction); 68 | this.actionList.add_action(this.urjtagAction); 69 | this.actionList.action_chosen.connect((identifier) => { 70 | this.action_chosen(identifier); 71 | }); 72 | this.add(this.actionList); 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /src/JTAGInABox.vala: -------------------------------------------------------------------------------- 1 | using Gtk; 2 | 3 | namespace JTAGInABox { 4 | public class JTAGInABox : Gtk.Application { 5 | private static JTAGInABox app; 6 | private JTAGInABoxWindow window = null; 7 | 8 | protected override void activate() { 9 | // if app is already open 10 | if (window != null) { 11 | window.present(); 12 | return; 13 | } 14 | 15 | try { 16 | Gtk.CssProvider cssProvider = new Gtk.CssProvider(); 17 | GLib.Bytes cssFile = GLib.resources_lookup_data("/css/jtaginabox.css", GLib.ResourceLookupFlags.NONE); 18 | cssProvider.load_from_data((string)cssFile.get_data(), (ssize_t)cssFile.get_size()); 19 | Gdk.Screen screen=Gdk.Screen.get_default(); 20 | Gtk.StyleContext.add_provider_for_screen(screen, cssProvider, Gtk.STYLE_PROVIDER_PRIORITY_USER); 21 | } catch (GLib.Error e) { 22 | stderr.printf("Error while loading CSS\n"); 23 | } 24 | 25 | window = new JTAGInABoxWindow(); 26 | window.set_application(this); 27 | window.delete_event.connect(window.main_quit); 28 | window.show(); 29 | } 30 | 31 | public static JTAGInABox get_instance() { 32 | if (app == null) { 33 | app = new JTAGInABox (); 34 | } 35 | 36 | return app; 37 | } 38 | 39 | public static int main (string[] args) { 40 | /* Intl.setlocale (LocaleCategory.ALL, ""); 41 | Intl.bind_textdomain_codeset (Build.GETTEXT_PACKAGE, "UTF-8"); 42 | Intl.textdomain (Build.GETTEXT_PACKAGE); */ 43 | 44 | app = new JTAGInABox(); 45 | 46 | return app.run(args); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/JTAGInABoxWindow.vala: -------------------------------------------------------------------------------- 1 | namespace JTAGInABox { 2 | public class JTAGInABoxWindow : Gtk.Window { 3 | Gtk.HeaderBar header; 4 | Gtk.Button previousButton; 5 | 6 | Homepage homepage = null; 7 | UrJTAG urjtag = null; 8 | Flashing flashing = null; 9 | 10 | public JTAGInABoxWindow() { 11 | /* Set up window UI */ 12 | this.header = new Gtk.HeaderBar(); 13 | this.header.show_close_button = true; 14 | this.header.title = "JTAG in a box"; 15 | this.header.show(); 16 | 17 | this.previousButton = new Gtk.Button.from_icon_name("go-previous-symbolic", Gtk.IconSize.BUTTON); 18 | this.previousButton.clicked.connect(this.action_previous); 19 | this.header.pack_start(this.previousButton); 20 | 21 | this.set_titlebar(this.header); 22 | 23 | this.title = "JTAG in a box"; 24 | 25 | this.set_default_size(640, 480); 26 | this.window_position = Gtk.WindowPosition.CENTER; 27 | 28 | setup_homepage(); 29 | } 30 | 31 | public bool main_quit () { 32 | this.destroy(); 33 | 34 | return false; 35 | } 36 | 37 | private void setup_homepage() { 38 | this.homepage = new Homepage(); 39 | this.homepage.action_chosen.connect(this.action_chosen); 40 | this.add(this.homepage); 41 | this.homepage.show_all(); 42 | this.previousButton.hide(); 43 | } 44 | 45 | private void setup_urjtag() { 46 | this.urjtag = new UrJTAG(); 47 | this.urjtag.exited.connect(this.action_previous); 48 | this.add(this.urjtag); 49 | this.urjtag.set_terminal_focus(true); 50 | this.urjtag.show(); 51 | this.previousButton.show(); 52 | } 53 | 54 | private void setup_flashing() { 55 | this.flashing = new Flashing(); 56 | this.add(this.flashing); 57 | this.flashing.show(); 58 | this.previousButton.show(); 59 | } 60 | 61 | public void action_chosen(string identifier) { 62 | if (identifier == "urjtag") { 63 | this.remove(this.homepage); 64 | this.homepage.destroy(); 65 | this.homepage = null; 66 | setup_urjtag(); 67 | } else if (identifier == "flash") { 68 | this.remove(this.homepage); 69 | this.homepage.destroy(); 70 | this.homepage = null; 71 | setup_flashing(); 72 | } 73 | } 74 | 75 | private void action_previous() { 76 | if (this.urjtag != null) { 77 | this.remove(this.urjtag); 78 | this.urjtag.destroy(); 79 | this.urjtag = null; 80 | setup_homepage(); 81 | } else if (this.flashing != null) { 82 | this.remove(this.flashing); 83 | this.flashing.destroy(); 84 | this.flashing = null; 85 | this.urjtag = null; 86 | setup_homepage(); 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /src/STLink.vala: -------------------------------------------------------------------------------- 1 | namespace JTAGInABox { 2 | public class STLink { 3 | LibUSB.Context* ctx; 4 | 5 | public STLink() { 6 | LibUSB.Context.init(out this.ctx); 7 | } 8 | 9 | ~STLink() { 10 | delete this.ctx; 11 | } 12 | 13 | public bool can_claim_vid_pid(uint16 vid, uint16 pid, int interface_num) { 14 | LibUSB.DeviceHandle dh; 15 | int res; 16 | bool ret; 17 | 18 | dh = this.ctx->open_device_with_vid_pid(vid, pid); 19 | if (dh != null) { 20 | res = dh.claim_interface(interface_num); 21 | 22 | if (res != 0) { 23 | ret = false; 24 | } else { 25 | ret = true; 26 | dh.release_interface(interface_num); 27 | } 28 | } else { 29 | ret = false; 30 | } 31 | 32 | return ret; 33 | } 34 | 35 | public bool detect_vid_pid(uint16 vid, uint16 pid) { 36 | LibUSB.Device[] devices; 37 | int i = 0; 38 | bool res = false; 39 | 40 | this.ctx->get_device_list(out devices); 41 | 42 | while (devices[i] != null) { 43 | LibUSB.DeviceDescriptor desc = LibUSB.DeviceDescriptor(devices[i]); 44 | 45 | if (desc.idVendor == vid && desc.idProduct == pid) { 46 | res = true; 47 | } 48 | 49 | i++; 50 | } 51 | 52 | return res; 53 | } 54 | 55 | public void exit_dfu() { 56 | LibUSB.DeviceHandle dh = this.ctx->open_device_with_vid_pid(0x0483, 0x3748); 57 | int transferred; 58 | 59 | if (dh != null) { 60 | // TODO : check that the interface has correctly been claimed 61 | dh.claim_interface(0); 62 | 63 | const uint8 cmd[] = {0xF3, 0x07}; 64 | dh.bulk_transfer(2, cmd, out transferred, 100); 65 | 66 | dh.release_interface(0); 67 | } 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /src/UrJTAG.vala: -------------------------------------------------------------------------------- 1 | namespace JTAGInABox { 2 | public class UrJTAG : Gtk.Overlay { 3 | Vte.Terminal terminal; 4 | Gtk.Box vbox; 5 | const string no_urjtag_error_message = "No UrJTAG executable detected. This might be related to a build or installation problem..."; 6 | const string cable_dirtyjtag_command = "cable dirtyjtag\n"; 7 | 8 | public signal void exited(); 9 | 10 | public UrJTAG() { 11 | Object(); 12 | 13 | this.vbox = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); 14 | this.add_overlay(this.vbox); 15 | //this.set_overlay_pass_through(this.vbox, true); 16 | 17 | this.terminal = new Vte.Terminal(); 18 | 19 | string? jtag_exec = GLib.Environment.find_program_in_path("jtag"); 20 | 21 | stdout.printf("exec file : %s\n", jtag_exec); 22 | 23 | if (jtag_exec != null) { 24 | try { 25 | this.terminal.spawn_sync(Vte.PtyFlags.DEFAULT, 26 | null, 27 | { jtag_exec }, 28 | null, 29 | GLib.SpawnFlags.DO_NOT_REAP_CHILD, 30 | null, 31 | null 32 | ); 33 | GLib.Thread usbDetectionThread = new GLib.Thread.try(null, this.usbDetection); 34 | usbDetectionThread.join(); 35 | } catch(Error e) { 36 | stderr.printf ("Error: %s\n", e.message); 37 | } 38 | } else { 39 | //this.terminal.feed_child(UrJTAG.no_urjtag_error_message.to_utf8()); 40 | GLib.MainContext.default().invoke( 41 | () => { 42 | this.showInfoMessage(UrJTAG.no_urjtag_error_message); 43 | return GLib.Source.REMOVE; 44 | }); 45 | } 46 | 47 | this.terminal.child_exited.connect(() => { 48 | this.exited(); 49 | }); 50 | 51 | this.add(this.terminal); 52 | this.terminal.show(); 53 | } 54 | 55 | private int usbDetection() { 56 | STLink stlink = new STLink(); 57 | bool dirtyjtagDetected = false; 58 | int i; 59 | 60 | stdout.printf("Attempting DirtyJTAG detection\n"); 61 | 62 | if (stlink.detect_vid_pid(0x1209, 0xC0CA)) { 63 | dirtyjtagDetected = true; 64 | } else { 65 | if (stlink.detect_vid_pid(0x0483, 0x3748)) { 66 | stlink.exit_dfu(); 67 | 68 | /* Check every 10ms if DirtyJTAG is detected */ 69 | for (i = 0; i < 100; i++) { 70 | if (stlink.detect_vid_pid(0x1209, 0xC0CA)) { 71 | dirtyjtagDetected = true; 72 | break; 73 | } 74 | 75 | GLib.Thread.usleep(10000); 76 | } 77 | 78 | if (!dirtyjtagDetected) { 79 | GLib.MainContext.default().invoke( 80 | () => { 81 | this.showInfoMessage("Unable to activate DirtyJTAG on your ST-Link dongle. Make sure DirtyJTAG is flashed, and try unplugging it and plugging it back."); 82 | return GLib.Source.REMOVE; 83 | }); 84 | } 85 | 86 | } else { 87 | GLib.MainContext.default().invoke( 88 | () => { 89 | this.showInfoMessage("No DirtyJTAG device detected"); 90 | return GLib.Source.REMOVE; 91 | }); 92 | } 93 | } 94 | 95 | if (dirtyjtagDetected) { 96 | GLib.MainContext.default().invoke( 97 | () => { 98 | this.terminal.feed_child(UrJTAG.cable_dirtyjtag_command.to_utf8()); 99 | return GLib.Source.REMOVE; 100 | }); 101 | } 102 | 103 | return 0; 104 | } 105 | 106 | public void showInfoMessage(string message) { 107 | Gtk.InfoBar bar = new Gtk.InfoBar(); 108 | bar.message_type = Gtk.MessageType.ERROR; 109 | bar.show_close_button = true; 110 | var content = bar.get_content_area(); 111 | Gtk.Label text = new Gtk.Label(message); 112 | content.add(text); 113 | this.vbox.add(bar); 114 | this.vbox.show(); 115 | bar.show_all(); 116 | bar.response.connect((bar, id) => { 117 | this.vbox.remove(bar); 118 | bar.hide(); 119 | }); 120 | } 121 | 122 | public void set_terminal_focus(bool focus) { 123 | this.terminal.has_focus = focus; 124 | } 125 | } 126 | } --------------------------------------------------------------------------------