This imports Tiny AES128 from https://github.com/kokke/tiny-AES128-C for use in the decoding of OSTC3 firmwares.
This aes-code is released into the public domain.
Signed-off-by: Anton Lundin glance@acc.umu.se --- msvc/libdivecomputer.vcproj | 6 + src/Makefile.am | 1 + src/aes.c | 583 ++++++++++++++++++++++++++++++++++++++++++++ src/aes.h | 40 +++ 4 files changed, 630 insertions(+) create mode 100644 src/aes.c create mode 100644 src/aes.h
diff --git a/msvc/libdivecomputer.vcproj b/msvc/libdivecomputer.vcproj index 2fea2b9..c8a2a8b 100644 --- a/msvc/libdivecomputer.vcproj +++ b/msvc/libdivecomputer.vcproj @@ -255,6 +255,9 @@ > </File> <File + RelativePath="..\src\aes.c" + > + <File RelativePath="..\src\hw_ostc3.c" > </File> @@ -561,6 +564,9 @@ > </File> <File + RelativePath="..\src\aes.h" + > + <File RelativePath="..\include\libdivecomputer\hw_ostc3.h" > </File> diff --git a/src/Makefile.am b/src/Makefile.am index 1f52841..821bc1b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -44,6 +44,7 @@ libdivecomputer_la_SOURCES = \ ihex.h ihex.c \ hw_ostc.c hw_ostc_parser.c \ hw_frog.c \ + aes.h aes.c \ hw_ostc3.c \ cressi_edy.c cressi_edy_parser.c \ cressi_leonardo.c cressi_leonardo_parser.c \ diff --git a/src/aes.c b/src/aes.c new file mode 100644 index 0000000..4c7645f --- /dev/null +++ b/src/aes.c @@ -0,0 +1,583 @@ +/* + +This is an implementation of the AES128 algorithm, specifically ECB and CBC mode. + +The implementation is verified against the test vectors in: + National Institute of Standards and Technology Special Publication 800-38A 2001 ED + +ECB-AES128 +---------- + + plain-text: + 6bc1bee22e409f96e93d7e117393172a + ae2d8a571e03ac9c9eb76fac45af8e51 + 30c81c46a35ce411e5fbc1191a0a52ef + f69f2445df4f9b17ad2b417be66c3710 + + key: + 2b7e151628aed2a6abf7158809cf4f3c + + resulting cipher + 3ad77bb40d7a3660a89ecaf32466ef97 + f5d3d58503b9699de785895a96fdbaaf + 43b1cd7f598ece23881b00e3ed030688 + 7b0c785e27e8ad3f8223207104725dd4 + + +NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0) + You should pad the end of the string with zeros if this is not the case. + +*/ + + +/*****************************************************************************/ +/* Includes: */ +/*****************************************************************************/ +#include <stdint.h> +#include <string.h> // CBC mode, for memset +#include "aes.h" + + +/*****************************************************************************/ +/* Defines: */ +/*****************************************************************************/ +// The number of columns comprising a state in AES. This is a constant in AES. Value=4 +#define Nb 4 +// The number of 32 bit words in a key. +#define Nk 4 +// Key length in bytes [128 bit] +#define KEYLEN 16 +// The number of rounds in AES Cipher. +#define Nr 10 + +// jcallan@github points out that declaring Multiply as a function +// reduces code size considerably with the Keil ARM compiler. +// See this link for more information: https://github.com/kokke/tiny-AES128-C/pull/3 +#ifndef MULTIPLY_AS_A_FUNCTION + #define MULTIPLY_AS_A_FUNCTION 0 +#endif + + +/*****************************************************************************/ +/* Private variables: */ +/*****************************************************************************/ +// state - array holding the intermediate results during decryption. +typedef uint8_t state_t[4][4]; +static state_t* state; + +// The array that stores the round keys. +static uint8_t RoundKey[176]; + +// The Key input to the AES Program +static const uint8_t* Key; + +#if defined(CBC) && CBC + // Initial Vector used only for CBC mode + static uint8_t* Iv; +#endif + +// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM +// The numbers below can be computed dynamically trading ROM for RAM - +// This can be useful in (embedded) bootloader applications, where ROM is often limited. +static const uint8_t sbox[256] = { + //0 1 2 3 4 5 6 7 8 9 A B C D E F + 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 }; + +static const uint8_t rsbox[256] = +{ 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 }; + + +// The round constant word array, Rcon[i], contains the values given by +// x to th e power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8) +// Note that i starts at 1, not 0). +static const uint8_t Rcon[255] = { + 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, + 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, + 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, + 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, + 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, + 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, + 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, + 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, + 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, + 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, + 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, + 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, + 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, + 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, + 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, + 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb }; + + +/*****************************************************************************/ +/* Private functions: */ +/*****************************************************************************/ +static uint8_t getSBoxValue(uint8_t num) +{ + return sbox[num]; +} + +static uint8_t getSBoxInvert(uint8_t num) +{ + return rsbox[num]; +} + +// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states. +static void KeyExpansion(void) +{ + uint32_t i, j, k; + uint8_t tempa[4]; // Used for the column/row operations + + // The first round key is the key itself. + for(i = 0; i < Nk; ++i) + { + RoundKey[(i * 4) + 0] = Key[(i * 4) + 0]; + RoundKey[(i * 4) + 1] = Key[(i * 4) + 1]; + RoundKey[(i * 4) + 2] = Key[(i * 4) + 2]; + RoundKey[(i * 4) + 3] = Key[(i * 4) + 3]; + } + + // All other round keys are found from the previous round keys. + for(; (i < (Nb * (Nr + 1))); ++i) + { + for(j = 0; j < 4; ++j) + { + tempa[j]=RoundKey[(i-1) * 4 + j]; + } + if (i % Nk == 0) + { + // This function rotates the 4 bytes in a word to the left once. + // [a0,a1,a2,a3] becomes [a1,a2,a3,a0] + + // Function RotWord() + { + k = tempa[0]; + tempa[0] = tempa[1]; + tempa[1] = tempa[2]; + tempa[2] = tempa[3]; + tempa[3] = k; + } + + // SubWord() is a function that takes a four-byte input word and + // applies the S-box to each of the four bytes to produce an output word. + + // Function Subword() + { + tempa[0] = getSBoxValue(tempa[0]); + tempa[1] = getSBoxValue(tempa[1]); + tempa[2] = getSBoxValue(tempa[2]); + tempa[3] = getSBoxValue(tempa[3]); + } + + tempa[0] = tempa[0] ^ Rcon[i/Nk]; + } + else if (Nk > 6 && i % Nk == 4) + { + // Function Subword() + { + tempa[0] = getSBoxValue(tempa[0]); + tempa[1] = getSBoxValue(tempa[1]); + tempa[2] = getSBoxValue(tempa[2]); + tempa[3] = getSBoxValue(tempa[3]); + } + } + RoundKey[i * 4 + 0] = RoundKey[(i - Nk) * 4 + 0] ^ tempa[0]; + RoundKey[i * 4 + 1] = RoundKey[(i - Nk) * 4 + 1] ^ tempa[1]; + RoundKey[i * 4 + 2] = RoundKey[(i - Nk) * 4 + 2] ^ tempa[2]; + RoundKey[i * 4 + 3] = RoundKey[(i - Nk) * 4 + 3] ^ tempa[3]; + } +} + +// This function adds the round key to state. +// The round key is added to the state by an XOR function. +static void AddRoundKey(uint8_t round) +{ + uint8_t i,j; + for(i=0;i<4;++i) + { + for(j = 0; j < 4; ++j) + { + (*state)[i][j] ^= RoundKey[round * Nb * 4 + i * Nb + j]; + } + } +} + +// The SubBytes Function Substitutes the values in the +// state matrix with values in an S-box. +static void SubBytes(void) +{ + uint8_t i, j; + for(i = 0; i < 4; ++i) + { + for(j = 0; j < 4; ++j) + { + (*state)[j][i] = getSBoxValue((*state)[j][i]); + } + } +} + +// The ShiftRows() function shifts the rows in the state to the left. +// Each row is shifted with different offset. +// Offset = Row number. So the first row is not shifted. +static void ShiftRows(void) +{ + uint8_t temp; + + // Rotate first row 1 columns to left + temp = (*state)[0][1]; + (*state)[0][1] = (*state)[1][1]; + (*state)[1][1] = (*state)[2][1]; + (*state)[2][1] = (*state)[3][1]; + (*state)[3][1] = temp; + + // Rotate second row 2 columns to left + temp = (*state)[0][2]; + (*state)[0][2] = (*state)[2][2]; + (*state)[2][2] = temp; + + temp = (*state)[1][2]; + (*state)[1][2] = (*state)[3][2]; + (*state)[3][2] = temp; + + // Rotate third row 3 columns to left + temp = (*state)[0][3]; + (*state)[0][3] = (*state)[3][3]; + (*state)[3][3] = (*state)[2][3]; + (*state)[2][3] = (*state)[1][3]; + (*state)[1][3] = temp; +} + +static uint8_t xtime(uint8_t x) +{ + return ((x<<1) ^ (((x>>7) & 1) * 0x1b)); +} + +// MixColumns function mixes the columns of the state matrix +static void MixColumns(void) +{ + uint8_t i; + uint8_t Tmp,Tm,t; + for(i = 0; i < 4; ++i) + { + t = (*state)[i][0]; + Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ; + Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ; + Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ; + Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ; + Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ; + } +} + +// Multiply is used to multiply numbers in the field GF(2^8) +#if MULTIPLY_AS_A_FUNCTION +static uint8_t Multiply(uint8_t x, uint8_t y) +{ + return (((y & 1) * x) ^ + ((y>>1 & 1) * xtime(x)) ^ + ((y>>2 & 1) * xtime(xtime(x))) ^ + ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ + ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))); + } +#else +#define Multiply(x, y) \ + ( ((y & 1) * x) ^ \ + ((y>>1 & 1) * xtime(x)) ^ \ + ((y>>2 & 1) * xtime(xtime(x))) ^ \ + ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \ + ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \ + +#endif + +// MixColumns function mixes the columns of the state matrix. +// The method used to multiply may be difficult to understand for the inexperienced. +// Please use the references to gain more information. +static void InvMixColumns(void) +{ + int i; + uint8_t a,b,c,d; + for(i=0;i<4;++i) + { + a = (*state)[i][0]; + b = (*state)[i][1]; + c = (*state)[i][2]; + d = (*state)[i][3]; + + (*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09); + (*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d); + (*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b); + (*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e); + } +} + + +// The SubBytes Function Substitutes the values in the +// state matrix with values in an S-box. +static void InvSubBytes(void) +{ + uint8_t i,j; + for(i=0;i<4;++i) + { + for(j=0;j<4;++j) + { + (*state)[j][i] = getSBoxInvert((*state)[j][i]); + } + } +} + +static void InvShiftRows(void) +{ + uint8_t temp; + + // Rotate first row 1 columns to right + temp=(*state)[3][1]; + (*state)[3][1]=(*state)[2][1]; + (*state)[2][1]=(*state)[1][1]; + (*state)[1][1]=(*state)[0][1]; + (*state)[0][1]=temp; + + // Rotate second row 2 columns to right + temp=(*state)[0][2]; + (*state)[0][2]=(*state)[2][2]; + (*state)[2][2]=temp; + + temp=(*state)[1][2]; + (*state)[1][2]=(*state)[3][2]; + (*state)[3][2]=temp; + + // Rotate third row 3 columns to right + temp=(*state)[0][3]; + (*state)[0][3]=(*state)[1][3]; + (*state)[1][3]=(*state)[2][3]; + (*state)[2][3]=(*state)[3][3]; + (*state)[3][3]=temp; +} + + +// Cipher is the main function that encrypts the PlainText. +static void Cipher(void) +{ + uint8_t round = 0; + + // Add the First round key to the state before starting the rounds. + AddRoundKey(0); + + // There will be Nr rounds. + // The first Nr-1 rounds are identical. + // These Nr-1 rounds are executed in the loop below. + for(round = 1; round < Nr; ++round) + { + SubBytes(); + ShiftRows(); + MixColumns(); + AddRoundKey(round); + } + + // The last round is given below. + // The MixColumns function is not here in the last round. + SubBytes(); + ShiftRows(); + AddRoundKey(Nr); +} + +static void InvCipher(void) +{ + uint8_t round=0; + + // Add the First round key to the state before starting the rounds. + AddRoundKey(Nr); + + // There will be Nr rounds. + // The first Nr-1 rounds are identical. + // These Nr-1 rounds are executed in the loop below. + for(round=Nr-1;round>0;round--) + { + InvShiftRows(); + InvSubBytes(); + AddRoundKey(round); + InvMixColumns(); + } + + // The last round is given below. + // The MixColumns function is not here in the last round. + InvShiftRows(); + InvSubBytes(); + AddRoundKey(0); +} + +static void BlockCopy(uint8_t* output, uint8_t* input) +{ + uint8_t i; + for (i=0;i<KEYLEN;++i) + { + output[i] = input[i]; + } +} + + + +/*****************************************************************************/ +/* Public functions: */ +/*****************************************************************************/ +#if defined(ECB) && ECB + + +void AES128_ECB_encrypt(uint8_t* input, const uint8_t* key, uint8_t* output) +{ + // Copy input to output, and work in-memory on output + BlockCopy(output, input); + state = (state_t*)output; + + Key = key; + KeyExpansion(); + + // The next function call encrypts the PlainText with the Key using AES algorithm. + Cipher(); +} + +void AES128_ECB_decrypt(uint8_t* input, const uint8_t* key, uint8_t *output) +{ + // Copy input to output, and work in-memory on output + BlockCopy(output, input); + state = (state_t*)output; + + // The KeyExpansion routine must be called before encryption. + Key = key; + KeyExpansion(); + + InvCipher(); +} + + +#endif // #if defined(ECB) && ECB + + + + + +#if defined(CBC) && CBC + + +static void XorWithIv(uint8_t* buf) +{ + uint8_t i; + for(i = 0; i < KEYLEN; ++i) + { + buf[i] ^= Iv[i]; + } +} + +void AES128_CBC_encrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv) +{ + intptr_t i; + uint8_t remainders = length % KEYLEN; /* Remaining bytes in the last non-full block */ + + BlockCopy(output, input); + state = (state_t*)output; + + // Skip the key expansion if key is passed as 0 + if(0 != key) + { + Key = key; + KeyExpansion(); + } + + if(iv != 0) + { + Iv = (uint8_t*)iv; + } + + for(i = 0; i < length; i += KEYLEN) + { + XorWithIv(input); + BlockCopy(output, input); + state = (state_t*)output; + Cipher(); + Iv = output; + input += KEYLEN; + output += KEYLEN; + } + + if(remainders) + { + BlockCopy(output, input); + memset(output + remainders, 0, KEYLEN - remainders); /* add 0-padding */ + state = (state_t*)output; + Cipher(); + } +} + +void AES128_CBC_decrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv) +{ + intptr_t i; + uint8_t remainders = length % KEYLEN; /* Remaining bytes in the last non-full block */ + + BlockCopy(output, input); + state = (state_t*)output; + + // Skip the key expansion if key is passed as 0 + if(0 != key) + { + Key = key; + KeyExpansion(); + } + + // If iv is passed as 0, we continue to encrypt without re-setting the Iv + if(iv != 0) + { + Iv = (uint8_t*)iv; + } + + for(i = 0; i < length; i += KEYLEN) + { + BlockCopy(output, input); + state = (state_t*)output; + InvCipher(); + XorWithIv(output); + Iv = input; + input += KEYLEN; + output += KEYLEN; + } + + if(remainders) + { + BlockCopy(output, input); + memset(output+remainders, 0, KEYLEN - remainders); /* add 0-padding */ + state = (state_t*)output; + InvCipher(); + } +} + + +#endif // #if defined(CBC) && CBC + + diff --git a/src/aes.h b/src/aes.h new file mode 100644 index 0000000..708a09c --- /dev/null +++ b/src/aes.h @@ -0,0 +1,40 @@ +#ifndef _AES_H_ +#define _AES_H_ + +#include <stdint.h> + + +// #define the macros below to 1/0 to enable/disable the mode of operation. +// +// CBC enables AES128 encryption in CBC-mode of operation and handles 0-padding. +// ECB enables the basic ECB 16-byte block algorithm. Both can be enabled simultaneously. + +// The #ifndef-guard allows it to be configured before #include'ing or at compile time. +#ifndef CBC + #define CBC 1 +#endif + +#ifndef ECB + #define ECB 1 +#endif + + + +#if defined(ECB) && ECB + +void AES128_ECB_encrypt(uint8_t* input, const uint8_t* key, uint8_t *output); +void AES128_ECB_decrypt(uint8_t* input, const uint8_t* key, uint8_t *output); + +#endif // #if defined(ECB) && ECB + + +#if defined(CBC) && CBC + +void AES128_CBC_encrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv); +void AES128_CBC_decrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv); + +#endif // #if defined(CBC) && CBC + + + +#endif //_AES_H_
Signed-off-by: Anton Lundin glance@acc.umu.se --- src/aes.c | 251 ++++++++++++++++++++++++++++++++------------------------------ 1 file changed, 129 insertions(+), 122 deletions(-)
diff --git a/src/aes.c b/src/aes.c index 4c7645f..4315a65 100644 --- a/src/aes.c +++ b/src/aes.c @@ -63,18 +63,21 @@ NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0) /*****************************************************************************/ // state - array holding the intermediate results during decryption. typedef uint8_t state_t[4][4]; -static state_t* state;
-// The array that stores the round keys. -static uint8_t RoundKey[176]; +typedef struct aes_state_t { + state_t* state;
-// The Key input to the AES Program -static const uint8_t* Key; + // The array that stores the round keys. + uint8_t RoundKey[176]; + + // The Key input to the AES Program + const uint8_t* Key;
#if defined(CBC) && CBC - // Initial Vector used only for CBC mode - static uint8_t* Iv; + // Initial Vector used only for CBC mode + uint8_t* Iv; #endif +} aes_state_t;
// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM // The numbers below can be computed dynamically trading ROM for RAM - @@ -153,7 +156,7 @@ static uint8_t getSBoxInvert(uint8_t num) }
// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states. -static void KeyExpansion(void) +static void KeyExpansion(aes_state_t *state) { uint32_t i, j, k; uint8_t tempa[4]; // Used for the column/row operations @@ -161,10 +164,10 @@ static void KeyExpansion(void) // The first round key is the key itself. for(i = 0; i < Nk; ++i) { - RoundKey[(i * 4) + 0] = Key[(i * 4) + 0]; - RoundKey[(i * 4) + 1] = Key[(i * 4) + 1]; - RoundKey[(i * 4) + 2] = Key[(i * 4) + 2]; - RoundKey[(i * 4) + 3] = Key[(i * 4) + 3]; + state->RoundKey[(i * 4) + 0] = state->Key[(i * 4) + 0]; + state->RoundKey[(i * 4) + 1] = state->Key[(i * 4) + 1]; + state->RoundKey[(i * 4) + 2] = state->Key[(i * 4) + 2]; + state->RoundKey[(i * 4) + 3] = state->Key[(i * 4) + 3]; }
// All other round keys are found from the previous round keys. @@ -172,7 +175,7 @@ static void KeyExpansion(void) { for(j = 0; j < 4; ++j) { - tempa[j]=RoundKey[(i-1) * 4 + j]; + tempa[j]=state->RoundKey[(i-1) * 4 + j]; } if (i % Nk == 0) { @@ -211,37 +214,37 @@ static void KeyExpansion(void) tempa[3] = getSBoxValue(tempa[3]); } } - RoundKey[i * 4 + 0] = RoundKey[(i - Nk) * 4 + 0] ^ tempa[0]; - RoundKey[i * 4 + 1] = RoundKey[(i - Nk) * 4 + 1] ^ tempa[1]; - RoundKey[i * 4 + 2] = RoundKey[(i - Nk) * 4 + 2] ^ tempa[2]; - RoundKey[i * 4 + 3] = RoundKey[(i - Nk) * 4 + 3] ^ tempa[3]; + state->RoundKey[i * 4 + 0] = state->RoundKey[(i - Nk) * 4 + 0] ^ tempa[0]; + state->RoundKey[i * 4 + 1] = state->RoundKey[(i - Nk) * 4 + 1] ^ tempa[1]; + state->RoundKey[i * 4 + 2] = state->RoundKey[(i - Nk) * 4 + 2] ^ tempa[2]; + state->RoundKey[i * 4 + 3] = state->RoundKey[(i - Nk) * 4 + 3] ^ tempa[3]; } }
// This function adds the round key to state. // The round key is added to the state by an XOR function. -static void AddRoundKey(uint8_t round) +static void AddRoundKey(aes_state_t *state, uint8_t round) { uint8_t i,j; for(i=0;i<4;++i) { for(j = 0; j < 4; ++j) { - (*state)[i][j] ^= RoundKey[round * Nb * 4 + i * Nb + j]; + (*state->state)[i][j] ^= state->RoundKey[round * Nb * 4 + i * Nb + j]; } } }
// The SubBytes Function Substitutes the values in the // state matrix with values in an S-box. -static void SubBytes(void) +static void SubBytes(aes_state_t *state) { uint8_t i, j; for(i = 0; i < 4; ++i) { for(j = 0; j < 4; ++j) { - (*state)[j][i] = getSBoxValue((*state)[j][i]); + (*state->state)[j][i] = getSBoxValue((*state->state)[j][i]); } } } @@ -249,32 +252,32 @@ static void SubBytes(void) // The ShiftRows() function shifts the rows in the state to the left. // Each row is shifted with different offset. // Offset = Row number. So the first row is not shifted. -static void ShiftRows(void) +static void ShiftRows(aes_state_t *state) { uint8_t temp;
// Rotate first row 1 columns to left - temp = (*state)[0][1]; - (*state)[0][1] = (*state)[1][1]; - (*state)[1][1] = (*state)[2][1]; - (*state)[2][1] = (*state)[3][1]; - (*state)[3][1] = temp; + temp = (*state->state)[0][1]; + (*state->state)[0][1] = (*state->state)[1][1]; + (*state->state)[1][1] = (*state->state)[2][1]; + (*state->state)[2][1] = (*state->state)[3][1]; + (*state->state)[3][1] = temp;
// Rotate second row 2 columns to left - temp = (*state)[0][2]; - (*state)[0][2] = (*state)[2][2]; - (*state)[2][2] = temp; + temp = (*state->state)[0][2]; + (*state->state)[0][2] = (*state->state)[2][2]; + (*state->state)[2][2] = temp;
- temp = (*state)[1][2]; - (*state)[1][2] = (*state)[3][2]; - (*state)[3][2] = temp; + temp = (*state->state)[1][2]; + (*state->state)[1][2] = (*state->state)[3][2]; + (*state->state)[3][2] = temp;
// Rotate third row 3 columns to left - temp = (*state)[0][3]; - (*state)[0][3] = (*state)[3][3]; - (*state)[3][3] = (*state)[2][3]; - (*state)[2][3] = (*state)[1][3]; - (*state)[1][3] = temp; + temp = (*state->state)[0][3]; + (*state->state)[0][3] = (*state->state)[3][3]; + (*state->state)[3][3] = (*state->state)[2][3]; + (*state->state)[2][3] = (*state->state)[1][3]; + (*state->state)[1][3] = temp; }
static uint8_t xtime(uint8_t x) @@ -283,18 +286,18 @@ static uint8_t xtime(uint8_t x) }
// MixColumns function mixes the columns of the state matrix -static void MixColumns(void) +static void MixColumns(aes_state_t *state) { uint8_t i; uint8_t Tmp,Tm,t; for(i = 0; i < 4; ++i) { - t = (*state)[i][0]; - Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ; - Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ; - Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ; - Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ; - Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ; + t = (*state->state)[i][0]; + Tmp = (*state->state)[i][0] ^ (*state->state)[i][1] ^ (*state->state)[i][2] ^ (*state->state)[i][3] ; + Tm = (*state->state)[i][0] ^ (*state->state)[i][1] ; Tm = xtime(Tm); (*state->state)[i][0] ^= Tm ^ Tmp ; + Tm = (*state->state)[i][1] ^ (*state->state)[i][2] ; Tm = xtime(Tm); (*state->state)[i][1] ^= Tm ^ Tmp ; + Tm = (*state->state)[i][2] ^ (*state->state)[i][3] ; Tm = xtime(Tm); (*state->state)[i][2] ^= Tm ^ Tmp ; + Tm = (*state->state)[i][3] ^ t ; Tm = xtime(Tm); (*state->state)[i][3] ^= Tm ^ Tmp ; } }
@@ -321,117 +324,117 @@ static uint8_t Multiply(uint8_t x, uint8_t y) // MixColumns function mixes the columns of the state matrix. // The method used to multiply may be difficult to understand for the inexperienced. // Please use the references to gain more information. -static void InvMixColumns(void) +static void InvMixColumns(aes_state_t *state) { int i; uint8_t a,b,c,d; for(i=0;i<4;++i) { - a = (*state)[i][0]; - b = (*state)[i][1]; - c = (*state)[i][2]; - d = (*state)[i][3]; - - (*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09); - (*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d); - (*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b); - (*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e); + a = (*state->state)[i][0]; + b = (*state->state)[i][1]; + c = (*state->state)[i][2]; + d = (*state->state)[i][3]; + + (*state->state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09); + (*state->state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d); + (*state->state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b); + (*state->state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e); } }
// The SubBytes Function Substitutes the values in the // state matrix with values in an S-box. -static void InvSubBytes(void) +static void InvSubBytes(aes_state_t *state) { uint8_t i,j; for(i=0;i<4;++i) { for(j=0;j<4;++j) { - (*state)[j][i] = getSBoxInvert((*state)[j][i]); + (*state->state)[j][i] = getSBoxInvert((*state->state)[j][i]); } } }
-static void InvShiftRows(void) +static void InvShiftRows(aes_state_t *state) { uint8_t temp;
// Rotate first row 1 columns to right - temp=(*state)[3][1]; - (*state)[3][1]=(*state)[2][1]; - (*state)[2][1]=(*state)[1][1]; - (*state)[1][1]=(*state)[0][1]; - (*state)[0][1]=temp; + temp=(*state->state)[3][1]; + (*state->state)[3][1]=(*state->state)[2][1]; + (*state->state)[2][1]=(*state->state)[1][1]; + (*state->state)[1][1]=(*state->state)[0][1]; + (*state->state)[0][1]=temp;
// Rotate second row 2 columns to right - temp=(*state)[0][2]; - (*state)[0][2]=(*state)[2][2]; - (*state)[2][2]=temp; + temp=(*state->state)[0][2]; + (*state->state)[0][2]=(*state->state)[2][2]; + (*state->state)[2][2]=temp;
- temp=(*state)[1][2]; - (*state)[1][2]=(*state)[3][2]; - (*state)[3][2]=temp; + temp=(*state->state)[1][2]; + (*state->state)[1][2]=(*state->state)[3][2]; + (*state->state)[3][2]=temp;
// Rotate third row 3 columns to right - temp=(*state)[0][3]; - (*state)[0][3]=(*state)[1][3]; - (*state)[1][3]=(*state)[2][3]; - (*state)[2][3]=(*state)[3][3]; - (*state)[3][3]=temp; + temp=(*state->state)[0][3]; + (*state->state)[0][3]=(*state->state)[1][3]; + (*state->state)[1][3]=(*state->state)[2][3]; + (*state->state)[2][3]=(*state->state)[3][3]; + (*state->state)[3][3]=temp; }
// Cipher is the main function that encrypts the PlainText. -static void Cipher(void) +static void Cipher(aes_state_t *state) { uint8_t round = 0;
// Add the First round key to the state before starting the rounds. - AddRoundKey(0); + AddRoundKey(state, 0);
// There will be Nr rounds. // The first Nr-1 rounds are identical. // These Nr-1 rounds are executed in the loop below. for(round = 1; round < Nr; ++round) { - SubBytes(); - ShiftRows(); - MixColumns(); - AddRoundKey(round); + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, round); }
// The last round is given below. // The MixColumns function is not here in the last round. - SubBytes(); - ShiftRows(); - AddRoundKey(Nr); + SubBytes(state); + ShiftRows(state); + AddRoundKey(state, Nr); }
-static void InvCipher(void) +static void InvCipher(aes_state_t *state) { uint8_t round=0;
// Add the First round key to the state before starting the rounds. - AddRoundKey(Nr); + AddRoundKey(state, Nr);
// There will be Nr rounds. // The first Nr-1 rounds are identical. // These Nr-1 rounds are executed in the loop below. for(round=Nr-1;round>0;round--) { - InvShiftRows(); - InvSubBytes(); - AddRoundKey(round); - InvMixColumns(); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, round); + InvMixColumns(state); }
// The last round is given below. // The MixColumns function is not here in the last round. - InvShiftRows(); - InvSubBytes(); - AddRoundKey(0); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, 0); }
static void BlockCopy(uint8_t* output, uint8_t* input) @@ -453,28 +456,30 @@ static void BlockCopy(uint8_t* output, uint8_t* input)
void AES128_ECB_encrypt(uint8_t* input, const uint8_t* key, uint8_t* output) { + aes_state_t state; // Copy input to output, and work in-memory on output BlockCopy(output, input); - state = (state_t*)output; + state.state = (state_t*)output;
- Key = key; - KeyExpansion(); + state.Key = key; + KeyExpansion(&state);
// The next function call encrypts the PlainText with the Key using AES algorithm. - Cipher(); + Cipher(&state); }
void AES128_ECB_decrypt(uint8_t* input, const uint8_t* key, uint8_t *output) { + aes_state_t state; // Copy input to output, and work in-memory on output BlockCopy(output, input); - state = (state_t*)output; + state.state = (state_t*)output;
// The KeyExpansion routine must be called before encryption. - Key = key; - KeyExpansion(); + state.Key = key; + KeyExpansion(&state);
- InvCipher(); + InvCipher(&state); }
@@ -487,12 +492,12 @@ void AES128_ECB_decrypt(uint8_t* input, const uint8_t* key, uint8_t *output) #if defined(CBC) && CBC
-static void XorWithIv(uint8_t* buf) +static void XorWithIv(aes_state_t *state, uint8_t* buf) { uint8_t i; for(i = 0; i < KEYLEN; ++i) { - buf[i] ^= Iv[i]; + buf[i] ^= state->Iv[i]; } }
@@ -500,29 +505,30 @@ void AES128_CBC_encrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, { intptr_t i; uint8_t remainders = length % KEYLEN; /* Remaining bytes in the last non-full block */ + aes_state_t state;
BlockCopy(output, input); - state = (state_t*)output; + state.state = (state_t*)output;
// Skip the key expansion if key is passed as 0 if(0 != key) { - Key = key; - KeyExpansion(); + state.Key = key; + KeyExpansion(&state); }
if(iv != 0) { - Iv = (uint8_t*)iv; + state.Iv = (uint8_t*)iv; }
for(i = 0; i < length; i += KEYLEN) { - XorWithIv(input); + XorWithIv(&state, input); BlockCopy(output, input); - state = (state_t*)output; - Cipher(); - Iv = output; + state.state = (state_t*)output; + Cipher(&state); + state.Iv = output; input += KEYLEN; output += KEYLEN; } @@ -531,8 +537,8 @@ void AES128_CBC_encrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, { BlockCopy(output, input); memset(output + remainders, 0, KEYLEN - remainders); /* add 0-padding */ - state = (state_t*)output; - Cipher(); + state.state = (state_t*)output; + Cipher(&state); } }
@@ -540,30 +546,31 @@ void AES128_CBC_decrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, { intptr_t i; uint8_t remainders = length % KEYLEN; /* Remaining bytes in the last non-full block */ + aes_state_t state;
BlockCopy(output, input); - state = (state_t*)output; + state.state = (state_t*)output;
// Skip the key expansion if key is passed as 0 if(0 != key) { - Key = key; - KeyExpansion(); + state.Key = key; + KeyExpansion(&state); }
// If iv is passed as 0, we continue to encrypt without re-setting the Iv if(iv != 0) { - Iv = (uint8_t*)iv; + state.Iv = (uint8_t*)iv; }
for(i = 0; i < length; i += KEYLEN) { BlockCopy(output, input); - state = (state_t*)output; - InvCipher(); - XorWithIv(output); - Iv = input; + state.state = (state_t*)output; + InvCipher(&state); + XorWithIv(&state, output); + state.Iv = input; input += KEYLEN; output += KEYLEN; } @@ -572,8 +579,8 @@ void AES128_CBC_decrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, { BlockCopy(output, input); memset(output+remainders, 0, KEYLEN - remainders); /* add 0-padding */ - state = (state_t*)output; - InvCipher(); + state.state = (state_t*)output; + InvCipher(&state); } }
Suggested-by: Jef Driesen jef@libdivecomputer.org Signed-off-by: Anton Lundin glance@acc.umu.se --- src/aes.c | 1 - src/aes.h | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/aes.c b/src/aes.c index 4315a65..a2f00dd 100644 --- a/src/aes.c +++ b/src/aes.c @@ -33,7 +33,6 @@ NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0) /*****************************************************************************/ /* Includes: */ /*****************************************************************************/ -#include <stdint.h> #include <string.h> // CBC mode, for memset #include "aes.h"
diff --git a/src/aes.h b/src/aes.h index 708a09c..051d813 100644 --- a/src/aes.h +++ b/src/aes.h @@ -1,7 +1,12 @@ #ifndef _AES_H_ #define _AES_H_
+#ifdef _MSC_VER +typedef unsigned char uint8_t; +typedef unsigned int uint32_t; +#else #include <stdint.h> +#endif
// #define the macros below to 1/0 to enable/disable the mode of operation.
This code is inspired by JeanDo ostc-companion.
Reviewed-by: Jef Driesen jef@libdivecomputer.org Signed-off-by: Anton Lundin glance@acc.umu.se --- src/hw_ostc3.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+)
diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c index b78702c..a4885d4 100644 --- a/src/hw_ostc3.c +++ b/src/hw_ostc3.c @@ -21,6 +21,7 @@
#include <string.h> // memcmp, memcpy #include <stdlib.h> // malloc, free +#include <stdio.h> // FILE, fopen
#include <libdivecomputer/hw_ostc3.h>
@@ -30,6 +31,7 @@ #include "checksum.h" #include "ringbuffer.h" #include "array.h" +#include "aes.h"
#define ISINSTANCE(device) dc_device_isinstance((device), &hw_ostc3_device_vtable)
@@ -43,6 +45,7 @@ #define SZ_VERSION (SZ_CUSTOMTEXT + 4) #define SZ_MEMORY 0x200000 #define SZ_CONFIG 4 +#define SZ_FIRMWARE 0x01E000 // 120KB
#define RB_LOGBOOK_SIZE 256 #define RB_LOGBOOK_COUNT 256 @@ -66,6 +69,21 @@ typedef struct hw_ostc3_device_t { unsigned char fingerprint[5]; } hw_ostc3_device_t;
+typedef struct hw_ostc3_firmware_t { + unsigned char data[SZ_FIRMWARE]; + unsigned int checksum; +} hw_ostc3_firmware_t; + +// This key is used both for the Ostc3 and its cousin, +// the Ostc Sport. +// The Frog uses a similar protocol, and with another key. +static const unsigned char ostc3_key[16] = { + 0xF1, 0xE9, 0xB0, 0x30, + 0x45, 0x6F, 0xBE, 0x55, + 0xFF, 0xE7, 0xF8, 0x31, + 0x13, 0x6C, 0xF2, 0xFE +}; + static dc_status_t hw_ostc3_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size); static dc_status_t hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata); static dc_status_t hw_ostc3_device_close (dc_device_t *abstract); @@ -622,3 +640,121 @@ hw_ostc3_device_config_reset (dc_device_t *abstract)
return DC_STATUS_SUCCESS; } + +// This is a variant of fletcher16 with 16 bit sum instead of 8bit +// and modulo 256 instead of 255 +static unsigned int hw_ostc3_firmware_checksum(hw_ostc3_firmware_t *firmware) +{ + unsigned short low = 0; + unsigned short high = 0; + for(unsigned int i = 0;i < SZ_FIRMWARE;i++) + { + low += firmware->data[i]; + high += low; + } + return (((unsigned int)high) << 16) + low; +} + +static dc_status_t +hw_ostc3_firmware_readline(FILE *fp, unsigned int addr, unsigned char data[], unsigned int size) +{ + unsigned char ascii[40]; + // 1 byte :, 6 bytes addr, X*2 bytes hex -> X bytes data. + const unsigned line_size = size * 2 + 1 + 6 + 1; + unsigned char faddr_byte[3]; + unsigned int faddr = 0; + if (line_size > sizeof(ascii)) + return DC_STATUS_INVALIDARGS; + if (fread (ascii, sizeof(char), line_size, fp) != line_size) + return DC_STATUS_IO; + if (ascii[0] != ':') + return DC_STATUS_DATAFORMAT; + // Is it a CRLF file? + // Read away that trailing LF + if (ascii[line_size] == '\r') + if (fread (ascii + line_size, sizeof(char), 1, fp) != 1) + return DC_STATUS_DATAFORMAT; + if (array_convert_hex2bin(ascii + 1, 6, faddr_byte, sizeof(faddr_byte)) != 0) { + return DC_STATUS_DATAFORMAT; + } + faddr = array_uint24_be (faddr_byte); + if (faddr != addr) + return DC_STATUS_DATAFORMAT; + if (array_convert_hex2bin (ascii + 1 + 6, size*2, data, size) != 0) + return DC_STATUS_DATAFORMAT; + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +hw_ostc3_firmware_readfile (hw_ostc3_firmware_t *firmware, dc_context_t *context, const char *filename) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + FILE *fp; + unsigned char iv[16] = {0}; + unsigned char tmpbuf[16] = {0}; + unsigned char encrypted[16] = {0}; + unsigned int bytes = 0, addr = 0, faddr = 0; + unsigned char checksum[4]; + + if (firmware == NULL) { + ERROR (context, "Invalid arguments."); + return DC_STATUS_INVALIDARGS; + } + + // Initialize the buffers. + memset (firmware->data, 0xFF, sizeof (firmware->data)); + firmware->checksum = 0; + + fp = fopen (filename, "rb"); + if (fp == NULL) { + ERROR (context, "Failed to open the file."); + return DC_STATUS_IO; + } + + rc = hw_ostc3_firmware_readline(fp, 0, iv, sizeof(iv)); + if (rc != DC_STATUS_SUCCESS) { + fclose (fp); + ERROR (context, "Failed to parse header."); + return rc; + } + bytes += 16; + + // Load the iv for AES-FCB-mode + AES128_ECB_encrypt(iv, ostc3_key, tmpbuf); + + for(addr = 0; addr < SZ_FIRMWARE; addr += 16, bytes += 16) { + rc = hw_ostc3_firmware_readline(fp, bytes, encrypted, sizeof(encrypted)); + if (rc != DC_STATUS_SUCCESS) { + fclose (fp); + ERROR (context, "Failed to parse file data."); + return rc; + } + + // Decrypt AES-FCB data + for(int i=0;i < 16;i++) + firmware->data[addr + i] = encrypted[i] ^ tmpbuf[i]; + + // Run the next round of encryption + AES128_ECB_encrypt(encrypted, ostc3_key, tmpbuf); + } + + // This file format contains a tail with the checksum in + rc = hw_ostc3_firmware_readline(fp, bytes, checksum, sizeof(checksum)); + if (rc != DC_STATUS_SUCCESS) { + fclose (fp); + ERROR (context, "Failed to parse file tail."); + return rc; + } + fclose(fp); + + firmware->checksum = array_uint32_le(checksum); + + if (firmware->checksum != hw_ostc3_firmware_checksum(firmware)) { + ERROR (context, "Failed to verify file checksum."); + return DC_STATUS_IO; + } + + return DC_STATUS_SUCCESS; +}
On 2014-12-17 23:11, Anton Lundin wrote:
+// This is a variant of fletcher16 with 16 bit sum instead of 8bit +// and modulo 256 instead of 255 +static unsigned int hw_ostc3_firmware_checksum(hw_ostc3_firmware_t *firmware)
The comment is wrong. It's modulo 65536 (2^16) vs 65535 (2^16-1) due to the 16bit sum.
+static dc_status_t +hw_ostc3_firmware_readline(FILE *fp, unsigned int addr, unsigned char data[], unsigned int size) +{
- unsigned char ascii[40];
- // 1 byte :, 6 bytes addr, X*2 bytes hex -> X bytes data.
- const unsigned line_size = size * 2 + 1 + 6 + 1;
- unsigned char faddr_byte[3];
- unsigned int faddr = 0;
- if (line_size > sizeof(ascii))
return DC_STATUS_INVALIDARGS;
- if (fread (ascii, sizeof(char), line_size, fp) != line_size)
return DC_STATUS_IO;
- if (ascii[0] != ':')
return DC_STATUS_DATAFORMAT;
- // Is it a CRLF file?
- // Read away that trailing LF
- if (ascii[line_size] == '\r')
if (fread (ascii + line_size, sizeof(char), 1, fp) != 1)
return DC_STATUS_DATAFORMAT;
- if (array_convert_hex2bin(ascii + 1, 6, faddr_byte,
sizeof(faddr_byte)) != 0) {
return DC_STATUS_DATAFORMAT;
- }
- faddr = array_uint24_be (faddr_byte);
- if (faddr != addr)
return DC_STATUS_DATAFORMAT;
- if (array_convert_hex2bin (ascii + 1 + 6, size*2, data, size) != 0)
return DC_STATUS_DATAFORMAT;
- return DC_STATUS_SUCCESS;
+}
There are two bugs in this code. You read line_size bytes, and then try to access ascii[line_size], which is left uninitialized. The ascii array is also not large enough to store CRLF, resulting in a buffer overflow.
I have attached a different implementation, which is based on the existing ihex code. It can handle arbitrary line endings, and not just CRLF or LF. So if you don't mind, I prefer to use this version.
Jef
On 19 December, 2014 - Jef Driesen wrote:
On 2014-12-17 23:11, Anton Lundin wrote:
+// This is a variant of fletcher16 with 16 bit sum instead of 8bit +// and modulo 256 instead of 255 +static unsigned int hw_ostc3_firmware_checksum(hw_ostc3_firmware_t *firmware)
The comment is wrong. It's modulo 65536 (2^16) vs 65535 (2^16-1) due to the 16bit sum.
Yea, you're correct. I just wanted to document that its not fletcher16 or fletcher32, but some sort of in-between.
+static dc_status_t +hw_ostc3_firmware_readline(FILE *fp, unsigned int addr, unsigned char data[], unsigned int size) +{
- unsigned char ascii[40];
- // 1 byte :, 6 bytes addr, X*2 bytes hex -> X bytes data.
- const unsigned line_size = size * 2 + 1 + 6 + 1;
- unsigned char faddr_byte[3];
- unsigned int faddr = 0;
- if (line_size > sizeof(ascii))
return DC_STATUS_INVALIDARGS;
- if (fread (ascii, sizeof(char), line_size, fp) != line_size)
return DC_STATUS_IO;
- if (ascii[0] != ':')
return DC_STATUS_DATAFORMAT;
- // Is it a CRLF file?
- // Read away that trailing LF
- if (ascii[line_size] == '\r')
if (fread (ascii + line_size, sizeof(char), 1, fp) != 1)
return DC_STATUS_DATAFORMAT;
- if (array_convert_hex2bin(ascii + 1, 6, faddr_byte,
sizeof(faddr_byte)) != 0) {
return DC_STATUS_DATAFORMAT;
- }
- faddr = array_uint24_be (faddr_byte);
- if (faddr != addr)
return DC_STATUS_DATAFORMAT;
- if (array_convert_hex2bin (ascii + 1 + 6, size*2, data, size) != 0)
return DC_STATUS_DATAFORMAT;
- return DC_STATUS_SUCCESS;
+}
There are two bugs in this code. You read line_size bytes, and then try to access ascii[line_size], which is left uninitialized. The ascii array is also not large enough to store CRLF, resulting in a buffer overflow.
There are two -1 that was missing there to make the code do what i ment for it to do.
I never ment to store CRLF, only LF, so if there is a CRLF ending, throw away the CR.
I have attached a different implementation, which is based on the existing ihex code. It can handle arbitrary line endings, and not just CRLF or LF. So if you don't mind, I prefer to use this version.
I actually think its uglier, and thats why i didn't copy that pattern. But if thats your preferred way to read data, you're the maintainer.
And... a bit more thinking. The firmware files are distributed inside zip-files. If you do anything to them, i would say its a feature to reject that file. The odds of flipping some bits that decrypt into something that matches the checksum are pretty slim, but i wouldn't trust a firmware file that had a blank line in it...
//Anton
This lifts the OSTC3 INIT command out from the open function and does that separately. This is refactoring to be able to enter service mode so we can access service mode commands.
Reviewed-by: Jef Driesen jef@libdivecomputer.org Signed-off-by: Anton Lundin glance@acc.umu.se --- src/hw_ostc3.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 87 insertions(+), 18 deletions(-)
diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c index a4885d4..79f5945 100644 --- a/src/hw_ostc3.c +++ b/src/hw_ostc3.c @@ -63,10 +63,16 @@ #define INIT 0xBB #define EXIT 0xFF
+typedef enum hw_ostc3_state_t { + OPEN, + DOWNLOAD, +} hw_ostc3_state_t; + typedef struct hw_ostc3_device_t { dc_device_t base; serial_t *port; unsigned char fingerprint[5]; + hw_ostc3_state_t state; } hw_ostc3_device_t;
typedef struct hw_ostc3_firmware_t { @@ -264,18 +270,46 @@ hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, const char *name // Make sure everything is in a sane state. serial_sleep (device->port, 300); serial_flush (device->port, SERIAL_QUEUE_BOTH); + device->state = OPEN; + + *out = (dc_device_t *) device; + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +hw_ostc3_device_init_download (dc_device_t *abstract) +{ + hw_ostc3_device_t *device = (hw_ostc3_device_t*) abstract; + dc_context_t *context = (abstract ? abstract->context : NULL);
// Send the init command. dc_status_t status = hw_ostc3_transfer (device, NULL, INIT, NULL, 0, NULL, 0); if (status != DC_STATUS_SUCCESS) { ERROR (context, "Failed to send the command."); - serial_close (device->port); - free (device); return status; }
- *out = (dc_device_t *) device; + device->state = DOWNLOAD; + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +hw_ostc3_check_state_or_init(dc_device_t *abstract) +{ + hw_ostc3_device_t *device = (hw_ostc3_device_t*) abstract; + dc_status_t rc = DC_STATUS_SUCCESS;
+ if (device->state == OPEN) { + rc = hw_ostc3_device_init_download(abstract); + if (rc != DC_STATUS_SUCCESS) + return rc; + } else if (device->state != DOWNLOAD) { + return DC_STATUS_INVALIDARGS; + } return DC_STATUS_SUCCESS; }
@@ -284,14 +318,17 @@ static dc_status_t hw_ostc3_device_close (dc_device_t *abstract) { hw_ostc3_device_t *device = (hw_ostc3_device_t*) abstract; + dc_status_t rc = DC_STATUS_SUCCESS;
- // Send the exit command. - dc_status_t status = hw_ostc3_transfer (device, NULL, EXIT, NULL, 0, NULL, 0); - if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to send the command."); - serial_close (device->port); - free (device); - return status; + // Send the exit command + if (device->state != OPEN) { + rc = hw_ostc3_transfer (device, NULL, EXIT, NULL, 0, NULL, 0); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the command."); + serial_close (device->port); + free (device); + return rc; + } }
// Close the device. @@ -335,8 +372,12 @@ hw_ostc3_device_version (dc_device_t *abstract, unsigned char data[], unsigned i if (size != SZ_VERSION) return DC_STATUS_INVALIDARGS;
+ dc_status_t rc = hw_ostc3_check_state_or_init(abstract); + if (rc != DC_STATUS_SUCCESS) + return rc; + // Send the command. - dc_status_t rc = hw_ostc3_transfer (device, NULL, IDENTITY, NULL, 0, data, size); + rc = hw_ostc3_transfer (device, NULL, IDENTITY, NULL, 0, data, size); if (rc != DC_STATUS_SUCCESS) return rc;
@@ -354,9 +395,13 @@ hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, voi progress.maximum = (RB_LOGBOOK_SIZE * RB_LOGBOOK_COUNT) + SZ_MEMORY; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
+ dc_status_t rc = hw_ostc3_check_state_or_init(abstract); + if (rc != DC_STATUS_SUCCESS) + return rc; + // Download the version data. unsigned char id[SZ_VERSION] = {0}; - dc_status_t rc = hw_ostc3_device_version (abstract, id, sizeof (id)); + rc = hw_ostc3_device_version (abstract, id, sizeof (id)); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the version."); return rc; @@ -521,11 +566,15 @@ hw_ostc3_device_clock (dc_device_t *abstract, const dc_datetime_t *datetime) return DC_STATUS_INVALIDARGS; }
+ dc_status_t rc = hw_ostc3_check_state_or_init(abstract); + if (rc != DC_STATUS_SUCCESS) + return rc; + // Send the command. unsigned char packet[6] = { datetime->hour, datetime->minute, datetime->second, datetime->month, datetime->day, datetime->year - 2000}; - dc_status_t rc = hw_ostc3_transfer (device, NULL, CLOCK, packet, sizeof (packet), NULL, 0); + rc = hw_ostc3_transfer (device, NULL, CLOCK, packet, sizeof (packet), NULL, 0); if (rc != DC_STATUS_SUCCESS) return rc;
@@ -541,6 +590,10 @@ hw_ostc3_device_display (dc_device_t *abstract, const char *text) if (!ISINSTANCE (abstract)) return DC_STATUS_INVALIDARGS;
+ dc_status_t rc = hw_ostc3_check_state_or_init(abstract); + if (rc != DC_STATUS_SUCCESS) + return rc; + // Pad the data packet with spaces. unsigned char packet[SZ_DISPLAY] = {0}; if (hw_ostc3_strncpy (packet, sizeof (packet), text) != 0) { @@ -549,7 +602,7 @@ hw_ostc3_device_display (dc_device_t *abstract, const char *text) }
// Send the command. - dc_status_t rc = hw_ostc3_transfer (device, NULL, DISPLAY, packet, sizeof (packet), NULL, 0); + rc = hw_ostc3_transfer (device, NULL, DISPLAY, packet, sizeof (packet), NULL, 0); if (rc != DC_STATUS_SUCCESS) return rc;
@@ -565,6 +618,10 @@ hw_ostc3_device_customtext (dc_device_t *abstract, const char *text) if (!ISINSTANCE (abstract)) return DC_STATUS_INVALIDARGS;
+ dc_status_t rc = hw_ostc3_check_state_or_init(abstract); + if (rc != DC_STATUS_SUCCESS) + return rc; + // Pad the data packet with spaces. unsigned char packet[SZ_CUSTOMTEXT] = {0}; if (hw_ostc3_strncpy (packet, sizeof (packet), text) != 0) { @@ -573,7 +630,7 @@ hw_ostc3_device_customtext (dc_device_t *abstract, const char *text) }
// Send the command. - dc_status_t rc = hw_ostc3_transfer (device, NULL, CUSTOMTEXT, packet, sizeof (packet), NULL, 0); + rc = hw_ostc3_transfer (device, NULL, CUSTOMTEXT, packet, sizeof (packet), NULL, 0); if (rc != DC_STATUS_SUCCESS) return rc;
@@ -593,9 +650,13 @@ hw_ostc3_device_config_read (dc_device_t *abstract, unsigned int config, unsigne return DC_STATUS_INVALIDARGS; }
+ dc_status_t rc = hw_ostc3_check_state_or_init(abstract); + if (rc != DC_STATUS_SUCCESS) + return rc; + // Send the command. unsigned char command[1] = {config}; - dc_status_t rc = hw_ostc3_transfer (device, NULL, READ, command, sizeof (command), data, size); + rc = hw_ostc3_transfer (device, NULL, READ, command, sizeof (command), data, size); if (rc != DC_STATUS_SUCCESS) return rc;
@@ -615,10 +676,14 @@ hw_ostc3_device_config_write (dc_device_t *abstract, unsigned int config, const return DC_STATUS_INVALIDARGS; }
+ dc_status_t rc = hw_ostc3_check_state_or_init(abstract); + if (rc != DC_STATUS_SUCCESS) + return rc; + // Send the command. unsigned char command[SZ_CONFIG + 1] = {config}; memcpy(command + 1, data, size); - dc_status_t rc = hw_ostc3_transfer (device, NULL, WRITE, command, size + 1, NULL, 0); + rc = hw_ostc3_transfer (device, NULL, WRITE, command, size + 1, NULL, 0); if (rc != DC_STATUS_SUCCESS) return rc;
@@ -633,8 +698,12 @@ hw_ostc3_device_config_reset (dc_device_t *abstract) if (!ISINSTANCE (abstract)) return DC_STATUS_INVALIDARGS;
+ dc_status_t rc = hw_ostc3_check_state_or_init(abstract); + if (rc != DC_STATUS_SUCCESS) + return rc; + // Send the command. - dc_status_t rc = hw_ostc3_transfer (device, NULL, RESET, NULL, 0, NULL, 0); + rc = hw_ostc3_transfer (device, NULL, RESET, NULL, 0, NULL, 0); if (rc != DC_STATUS_SUCCESS) return rc;
This code is inspired by JeanDo ostc-companion.
Reviewed-by: Jef Driesen jef@libdivecomputer.org Signed-off-by: Anton Lundin glance@acc.umu.se --- src/hw_ostc3.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-)
diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c index 79f5945..f777cee 100644 --- a/src/hw_ostc3.c +++ b/src/hw_ostc3.c @@ -50,6 +50,7 @@ #define RB_LOGBOOK_SIZE 256 #define RB_LOGBOOK_COUNT 256
+#define S_READY 0x4C #define READY 0x4D #define HEADER 0x61 #define CLOCK 0x62 @@ -66,6 +67,7 @@ typedef enum hw_ostc3_state_t { OPEN, DOWNLOAD, + SERVICE, } hw_ostc3_state_t;
typedef struct hw_ostc3_device_t { @@ -205,6 +207,7 @@ hw_ostc3_transfer (hw_ostc3_device_t *device, if (cmd != EXIT) { // Read the ready byte. unsigned char ready[1] = {0}; + unsigned char expected = (device->state == SERVICE ? S_READY : READY); n = serial_read (device->port, ready, sizeof (ready)); if (n != sizeof (ready)) { ERROR (abstract->context, "Failed to receive the ready byte."); @@ -212,7 +215,7 @@ hw_ostc3_transfer (hw_ostc3_device_t *device, }
// Verify the ready byte. - if (ready[0] != READY) { + if (ready[0] != expected) { ERROR (abstract->context, "Unexpected ready byte."); return DC_STATUS_PROTOCOL; } @@ -307,7 +310,7 @@ hw_ostc3_check_state_or_init(dc_device_t *abstract) rc = hw_ostc3_device_init_download(abstract); if (rc != DC_STATUS_SUCCESS) return rc; - } else if (device->state != DOWNLOAD) { + } else if (device->state != DOWNLOAD && device->state != SERVICE) { return DC_STATUS_INVALIDARGS; } return DC_STATUS_SUCCESS; @@ -827,3 +830,46 @@ hw_ostc3_firmware_readfile (hw_ostc3_firmware_t *firmware, dc_context_t *context
return DC_STATUS_SUCCESS; } + + +static dc_status_t +hw_ostc3_device_init_service (dc_device_t *abstract) +{ + hw_ostc3_device_t *device = (hw_ostc3_device_t *) abstract; + unsigned char command[] = {0xAA, 0xAB, 0xCD, 0xEF}; + unsigned char output[5]; + dc_status_t rc = DC_STATUS_SUCCESS; + int n; + + if (device->state != OPEN) + return DC_STATUS_INVALIDARGS; + + // We cant use hw_ostc3_transfer here, due to the different echos + n = serial_write (device->port, command, sizeof (command)); + if (n != sizeof (command)) { + ERROR (abstract->context, "Failed to send the command."); + return EXITCODE (n); + } + + // Give the device some time to enter service mode + serial_sleep (device->port, 100); + + // Read the response + n = serial_read (device->port, output, sizeof (output)); + if (n != sizeof (output)) { + ERROR (abstract->context, "Failed to receive the echo."); + return EXITCODE (n); + } + + // Verify the response to service mode + if (output[0] != 0x4B || output[1] != 0xAB || + output[2] != 0xCD || output[3] != 0xEF || + output[4] != S_READY) { + ERROR (abstract->context, "Failed to verify echo."); + return DC_STATUS_IO; + } + + device->state = SERVICE; + + return DC_STATUS_SUCCESS; +}
Reviewed-by: Jef Driesen jef@libdivecomputer.org Signed-off-by: Anton Lundin glance@acc.umu.se --- src/array.c | 19 +++++++++++++++++++ src/array.h | 6 ++++++ 2 files changed, 25 insertions(+)
diff --git a/src/array.c b/src/array.c index 243ec34..be8d696 100644 --- a/src/array.c +++ b/src/array.c @@ -161,6 +161,16 @@ array_uint32_le (const unsigned char data[]) }
+void +array_uint32_le_set (unsigned char data[], const unsigned int input) +{ + data[0] = input & 0xFF; + data[1] = (input >> 8) & 0xFF; + data[2] = (input >> 16) & 0xFF; + data[3] = (input >> 24) & 0xFF; +} + + unsigned int array_uint24_be (const unsigned char data[]) { @@ -168,6 +178,15 @@ array_uint24_be (const unsigned char data[]) }
+void +array_uint24_be_set (unsigned char data[], const unsigned int input) +{ + data[0] = (input >> 16) & 0xFF; + data[1] = (input >> 8) & 0xFF; + data[2] = input & 0xFF; +} + + unsigned int array_uint24_le (const unsigned char data[]) { diff --git a/src/array.h b/src/array.h index b08ed2e..622e0d5 100644 --- a/src/array.h +++ b/src/array.h @@ -55,9 +55,15 @@ array_uint32_be (const unsigned char data[]); unsigned int array_uint32_le (const unsigned char data[]);
+void +array_uint32_le_set (unsigned char data[], const unsigned int input); + unsigned int array_uint24_be (const unsigned char data[]);
+void +array_uint24_be_set (unsigned char data[], const unsigned int input); + unsigned int array_uint24_le (const unsigned char data[]);
This is the fist step in the firmware upgrade process.
This code is inspired by JeanDo ostc-companion.
Reviewed-by: Jef Driesen jef@libdivecomputer.org Signed-off-by: Anton Lundin glance@acc.umu.se --- src/hw_ostc3.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c index f777cee..71f56a1 100644 --- a/src/hw_ostc3.c +++ b/src/hw_ostc3.c @@ -46,10 +46,13 @@ #define SZ_MEMORY 0x200000 #define SZ_CONFIG 4 #define SZ_FIRMWARE 0x01E000 // 120KB +#define SZ_FIRMWARE_BLOCK 0x1000 // 4KB +#define FIRMWARE_AREA 0x3E0000
#define RB_LOGBOOK_SIZE 256 #define RB_LOGBOOK_COUNT 256
+#define S_ERASE 0x42 #define S_READY 0x4C #define READY 0x4D #define HEADER 0x61 @@ -873,3 +876,18 @@ hw_ostc3_device_init_service (dc_device_t *abstract)
return DC_STATUS_SUCCESS; } + +static dc_status_t +hw_ostc3_firmware_erase (hw_ostc3_device_t *device, unsigned int addr, unsigned int size) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + // Convert size to number of pages, rounded up. + unsigned char blocks = ((size + SZ_FIRMWARE_BLOCK - 1) / SZ_FIRMWARE_BLOCK); + + // Erase just the needed pages. + unsigned char buffer[4]; + array_uint24_be_set(buffer, addr); + buffer[3] = blocks; + + return hw_ostc3_transfer (device, NULL, S_ERASE, buffer, sizeof(buffer), NULL, 0); +}
This is necessary to verify that the memory written got transfered correctly.
This code is inspired by JeanDo ostc-companion.
Reviewed-by: Jef Driesen jef@libdivecomputer.org Signed-off-by: Anton Lundin glance@acc.umu.se --- src/hw_ostc3.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c index 71f56a1..5584f31 100644 --- a/src/hw_ostc3.c +++ b/src/hw_ostc3.c @@ -52,6 +52,7 @@ #define RB_LOGBOOK_SIZE 256 #define RB_LOGBOOK_COUNT 256
+#define S_BLOCK_READ 0x20 #define S_ERASE 0x42 #define S_READY 0x4C #define READY 0x4D @@ -891,3 +892,13 @@ hw_ostc3_firmware_erase (hw_ostc3_device_t *device, unsigned int addr, unsigned
return hw_ostc3_transfer (device, NULL, S_ERASE, buffer, sizeof(buffer), NULL, 0); } + +static dc_status_t +hw_ostc3_firmware_block_read (hw_ostc3_device_t *device, unsigned int addr, unsigned char block[], unsigned int block_size) +{ + unsigned char buffer[6]; + array_uint24_be_set(buffer, addr); + array_uint24_be_set(buffer + 3, block_size); + + return hw_ostc3_transfer (device, NULL, S_BLOCK_READ, buffer, sizeof(buffer), block, block_size); +}
This is how you transfer a new firmware to the OSTC3.
This code is inspired by JeanDo ostc-companion.
Reviewed-by: Jef Driesen jef@libdivecomputer.org Signed-off-by: Anton Lundin glance@acc.umu.se --- src/hw_ostc3.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c index 5584f31..35df0c7 100644 --- a/src/hw_ostc3.c +++ b/src/hw_ostc3.c @@ -53,6 +53,7 @@ #define RB_LOGBOOK_COUNT 256
#define S_BLOCK_READ 0x20 +#define S_BLOCK_WRITE 0x30 #define S_ERASE 0x42 #define S_READY 0x4C #define READY 0x4D @@ -902,3 +903,17 @@ hw_ostc3_firmware_block_read (hw_ostc3_device_t *device, unsigned int addr, unsi
return hw_ostc3_transfer (device, NULL, S_BLOCK_READ, buffer, sizeof(buffer), block, block_size); } + +static dc_status_t +hw_ostc3_firmware_block_write (hw_ostc3_device_t *device, unsigned int addr, unsigned char block[], unsigned int block_size) +{ + unsigned char buffer[3 + SZ_FIRMWARE_BLOCK]; + // We currenty only support writing SZ_FIRMWARE_BLOCK sized blocks. + if (block_size > SZ_FIRMWARE_BLOCK) + return DC_STATUS_INVALIDARGS; + + array_uint24_be_set(buffer, addr); + memcpy(buffer + 3, block, block_size); + + return hw_ostc3_transfer (device, NULL, S_BLOCK_WRITE, buffer, sizeof(buffer), NULL, 0); +}
On 2014-12-17 23:11, Anton Lundin wrote:
+static dc_status_t +hw_ostc3_firmware_block_write (hw_ostc3_device_t *device, unsigned int addr, unsigned char block[], unsigned int block_size) +{
- unsigned char buffer[3 + SZ_FIRMWARE_BLOCK];
- // We currenty only support writing SZ_FIRMWARE_BLOCK sized blocks.
- if (block_size > SZ_FIRMWARE_BLOCK)
return DC_STATUS_INVALIDARGS;
- array_uint24_be_set(buffer, addr);
- memcpy(buffer + 3, block, block_size);
- return hw_ostc3_transfer (device, NULL, S_BLOCK_WRITE, buffer,
sizeof(buffer), NULL, 0); +}
I assume the check was supposed to be '!=' instead of '>'? Right now, if someone happens to call this function with less than SZ_FIRMWARE_BLOCK bytes, we send uninitialized data to the ostc3. If we check, we better do it right.
Jef
On 19 December, 2014 - Jef Driesen wrote:
On 2014-12-17 23:11, Anton Lundin wrote:
+static dc_status_t +hw_ostc3_firmware_block_write (hw_ostc3_device_t *device, unsigned int addr, unsigned char block[], unsigned int block_size) +{
- unsigned char buffer[3 + SZ_FIRMWARE_BLOCK];
- // We currenty only support writing SZ_FIRMWARE_BLOCK sized blocks.
- if (block_size > SZ_FIRMWARE_BLOCK)
return DC_STATUS_INVALIDARGS;
- array_uint24_be_set(buffer, addr);
- memcpy(buffer + 3, block, block_size);
- return hw_ostc3_transfer (device, NULL, S_BLOCK_WRITE, buffer,
sizeof(buffer), NULL, 0); +}
I assume the check was supposed to be '!=' instead of '>'? Right now, if someone happens to call this function with less than SZ_FIRMWARE_BLOCK bytes, we send uninitialized data to the ostc3. If we check, we better do it right.
The right fix here is rather to change the isize parameter to be block_size + 3 instead of sizeof(buffer).
You can write byte-by-byte of memory with this call to the device, or write the whole firmware in one go, but i haven't tested any of them.
//Anton
This function triggers a reboot into the bootloader which flashes the new firmware to Prom.
This code is inspired by JeanDo ostc-companion.
Reviewed-by: Jef Driesen jef@libdivecomputer.org Signed-off-by: Anton Lundin glance@acc.umu.se --- src/hw_ostc3.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c index 35df0c7..06e3827 100644 --- a/src/hw_ostc3.c +++ b/src/hw_ostc3.c @@ -57,6 +57,7 @@ #define S_ERASE 0x42 #define S_READY 0x4C #define READY 0x4D +#define S_UPGRADE 0x50 #define HEADER 0x61 #define CLOCK 0x62 #define CUSTOMTEXT 0x63 @@ -73,6 +74,7 @@ typedef enum hw_ostc3_state_t { OPEN, DOWNLOAD, SERVICE, + REBOOTING, } hw_ostc3_state_t;
typedef struct hw_ostc3_device_t { @@ -329,7 +331,7 @@ hw_ostc3_device_close (dc_device_t *abstract) dc_status_t rc = DC_STATUS_SUCCESS;
// Send the exit command - if (device->state != OPEN) { + if (device->state != REBOOTING && device->state != OPEN) { rc = hw_ostc3_transfer (device, NULL, EXIT, NULL, 0, NULL, 0); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to send the command."); @@ -917,3 +919,31 @@ hw_ostc3_firmware_block_write (hw_ostc3_device_t *device, unsigned int addr, uns
return hw_ostc3_transfer (device, NULL, S_BLOCK_WRITE, buffer, sizeof(buffer), NULL, 0); } + +static dc_status_t +hw_ostc3_firmware_upgrade (dc_device_t *abstract, unsigned int checksum) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + hw_ostc3_device_t *device = (hw_ostc3_device_t *) abstract; + dc_context_t *context = (abstract ? abstract->context : NULL); + unsigned char buffer[5]; + array_uint32_le_set(buffer, checksum); + + // Compute a one byte checksum, so the device can validate the firmware image. + buffer[4] = 0x55; + for(int i = 0; i < 4; i++) { + buffer[4] ^= buffer[i]; + buffer[4] = (buffer[4]<<1 | buffer[4]>>7); + } + + rc = hw_ostc3_transfer(device, NULL, S_UPGRADE, buffer, sizeof(buffer), NULL, 0); + if (rc != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to send flash firmware command"); + return rc; + } + + // Now the device resets, and if everything is well, it reprograms. + device->state = REBOOTING; + + return DC_STATUS_SUCCESS; +}
On 2014-12-17 23:11, Anton Lundin wrote:
@@ -329,7 +331,7 @@ hw_ostc3_device_close (dc_device_t *abstract) dc_status_t rc = DC_STATUS_SUCCESS;
// Send the exit command
- if (device->state != OPEN) {
- if (device->state != REBOOTING && device->state != OPEN) { rc = hw_ostc3_transfer (device, NULL, EXIT, NULL, 0, NULL, 0); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to send the command.");
This condition in the if statement is a bit counter intuitive. You basically list all the states when you should NOT send the exit command. I find it easier to understand when doing the opposite:
if (device->state == DOWNLOAD || device->state == SERVICE) { ... }
Jef
This connects the bits and implements firmware upgrade for the OSTC3.
This code is inspired by JeanDo ostc-companion.
Reviewed-by: Jef Driesen jef@libdivecomputer.org Signed-off-by: Anton Lundin glance@acc.umu.se --- include/libdivecomputer/hw_ostc3.h | 3 + src/hw_ostc3.c | 124 ++++++++++++++++++++++++++++++++++++- src/libdivecomputer.symbols | 1 + 3 files changed, 127 insertions(+), 1 deletion(-)
diff --git a/include/libdivecomputer/hw_ostc3.h b/include/libdivecomputer/hw_ostc3.h index 267b7e3..bc56a9d 100644 --- a/include/libdivecomputer/hw_ostc3.h +++ b/include/libdivecomputer/hw_ostc3.h @@ -58,6 +58,9 @@ hw_ostc3_device_config_write (dc_device_t *abstract, unsigned int config, const dc_status_t hw_ostc3_device_config_reset (dc_device_t *abstract);
+dc_status_t +hw_ostc3_device_fwupdate (dc_device_t *abstract, const char *filename); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c index 06e3827..1daa30e 100644 --- a/src/hw_ostc3.c +++ b/src/hw_ostc3.c @@ -936,7 +936,7 @@ hw_ostc3_firmware_upgrade (dc_device_t *abstract, unsigned int checksum) buffer[4] = (buffer[4]<<1 | buffer[4]>>7); }
- rc = hw_ostc3_transfer(device, NULL, S_UPGRADE, buffer, sizeof(buffer), NULL, 0); + rc = hw_ostc3_transfer (device, NULL, S_UPGRADE, buffer, sizeof(buffer), NULL, 0); if (rc != DC_STATUS_SUCCESS) { ERROR (context, "Failed to send flash firmware command"); return rc; @@ -947,3 +947,125 @@ hw_ostc3_firmware_upgrade (dc_device_t *abstract, unsigned int checksum)
return DC_STATUS_SUCCESS; } + + +dc_status_t +hw_ostc3_device_fwupdate (dc_device_t *abstract, const char *filename) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + hw_ostc3_device_t *device = (hw_ostc3_device_t *) abstract; + dc_context_t *context = (abstract ? abstract->context : NULL); + + // Enable progress notifications. + dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; + + if (!ISINSTANCE (abstract)) + return DC_STATUS_INVALIDARGS; + + // load, erase, upload FZ, verify FZ, reprogram + progress.maximum = 3 + SZ_FIRMWARE * 2 / SZ_FIRMWARE_BLOCK; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + // Allocate memory for the firmware data. + hw_ostc3_firmware_t *firmware = (hw_ostc3_firmware_t *) malloc (sizeof (hw_ostc3_firmware_t)); + if (firmware == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Read the hex file. + rc = hw_ostc3_firmware_readfile (firmware, context, filename); + if (rc != DC_STATUS_SUCCESS) { + free (firmware); + return rc; + } + + // Make sure the device is in service mode + if (device->state == OPEN) { + rc = hw_ostc3_device_init_service(abstract); + if (rc != DC_STATUS_SUCCESS) { + free (firmware); + return rc; + } + } else if (device->state != SERVICE) { + free (firmware); + return DC_STATUS_INVALIDARGS; + } + + // Device open and firmware loaded + progress.current++; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + hw_ostc3_device_display(abstract, " Erasing FW..."); + rc = hw_ostc3_firmware_erase(device, FIRMWARE_AREA, SZ_FIRMWARE); + if (rc != DC_STATUS_SUCCESS) { + free (firmware); + ERROR (context, "Failed to erase old firmware"); + return rc; + } + + // Memory erased + progress.current++; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + hw_ostc3_device_display(abstract, " Uploading..."); + + for(int len = 0; len < SZ_FIRMWARE; len += SZ_FIRMWARE_BLOCK) + { + char status[16]; // Status message on the display + snprintf (status, 16, " Uploading %2d%%", (100 * len) / SZ_FIRMWARE); + hw_ostc3_device_display (abstract, status); + + rc = hw_ostc3_firmware_block_write (device, FIRMWARE_AREA + len, firmware->data + len, SZ_FIRMWARE_BLOCK); + if (rc != DC_STATUS_SUCCESS) { + free(firmware); + ERROR (context, "Failed to write block to device"); + return rc; + } + // One block uploaded + progress.current++; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + } + + hw_ostc3_device_display (abstract, " Verifying..."); + + for(int len = 0; len < SZ_FIRMWARE; len += SZ_FIRMWARE_BLOCK) + { + unsigned char block[SZ_FIRMWARE_BLOCK]; + char status[16]; // Status message on the display + snprintf (status, 16, " Verifying %2d%%", (100 * len) / SZ_FIRMWARE); + hw_ostc3_device_display (abstract, status); + + rc = hw_ostc3_firmware_block_read (device, FIRMWARE_AREA + len, block, sizeof(block)); + if (rc != DC_STATUS_SUCCESS) { + free (firmware); + ERROR (context, "Failed to read block."); + return rc; + } + if (memcmp (firmware->data + len, block, sizeof (block)) != 0) { + free (firmware); + ERROR (context, "Failed verify."); + hw_ostc3_device_display (abstract, " Verify FAILED"); + return DC_STATUS_PROTOCOL; + } + // One block verified + progress.current++; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + } + + hw_ostc3_device_display(abstract, " Programming..."); + + rc = hw_ostc3_firmware_upgrade(abstract, firmware->checksum); + if (rc != DC_STATUS_SUCCESS) { + free (firmware); + ERROR (context, "Failed to start programing"); + return rc; + } + + // Programing done! + progress.current++; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + // Finished! + return DC_STATUS_SUCCESS; +} diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols index 8dfd7be..8109f96 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -160,6 +160,7 @@ hw_ostc3_device_customtext hw_ostc3_device_config_read hw_ostc3_device_config_write hw_ostc3_device_config_reset +hw_ostc3_device_fwupdate zeagle_n2ition3_device_open atomics_cobalt_device_open atomics_cobalt_device_version
This teaches the example firmware upgrader about how to upgrade the OSTC3's.
Signed-off-by: Anton Lundin glance@acc.umu.se --- examples/hw_ostc_fwupdate.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-)
diff --git a/examples/hw_ostc_fwupdate.c b/examples/hw_ostc_fwupdate.c index a9e7fde..7ce1347 100644 --- a/examples/hw_ostc_fwupdate.c +++ b/examples/hw_ostc_fwupdate.c @@ -19,10 +19,10 @@ * MA 02110-1301 USA */
-#include <stdio.h> // fopen, fwrite, fclose -#include <stdlib.h> +#include <string.h>
#include <libdivecomputer/hw_ostc.h> +#include <libdivecomputer/hw_ostc3.h>
#include "utils.h" #include "common.h" @@ -44,17 +44,23 @@ event_cb (dc_device_t *device, dc_event_type_t event, const void *data, void *us }
static dc_status_t -fwupdate (const char *name, const char *hexfile) +fwupdate (const char *name, const char *hexfile, int ostc3) { dc_context_t *context = NULL; dc_device_t *device = NULL; + dc_status_t rc = DC_EVENT_PROGRESS;
dc_context_new (&context); dc_context_set_loglevel (context, DC_LOGLEVEL_ALL); dc_context_set_logfunc (context, logfunc, NULL);
- message ("hw_ostc_device_open\n"); - dc_status_t rc = hw_ostc_device_open (&device, context, name); + if (ostc3) { + message ("hw_ostc3_device_open\n"); + rc = hw_ostc3_device_open (&device, context, name); + } else { + message ("hw_ostc_device_open\n"); + rc = hw_ostc_device_open (&device, context, name); + } if (rc != DC_STATUS_SUCCESS) { WARNING ("Error opening serial port."); dc_context_free (context); @@ -70,8 +76,13 @@ fwupdate (const char *name, const char *hexfile) return rc; }
- message ("hw_ostc_device_fwupdate\n"); - rc = hw_ostc_device_fwupdate (device, hexfile); + if (ostc3) { + message ("hw_ostc3_device_fwupdate\n"); + rc = hw_ostc3_device_fwupdate (device, hexfile); + } else { + message ("hw_ostc_device_fwupdate\n"); + rc = hw_ostc_device_fwupdate (device, hexfile); + } if (rc != DC_STATUS_SUCCESS) { WARNING ("Error flashing firmware."); dc_device_close (device); @@ -103,6 +114,7 @@ int main(int argc, char *argv[]) const char* name = "/dev/ttyUSB0"; #endif const char *hexfile = NULL; + int ostc3 = 0;
if (argc > 1) { name = argv[1]; @@ -110,11 +122,18 @@ int main(int argc, char *argv[]) if (argc > 2) { hexfile = argv[2]; } + if (argc > 3) { + if (strcmp(argv[3], "-3") == 0) { + ostc3 = 1; + } else { + ostc3 = 0; + } + }
message ("DEVICE=%s\n", name); message ("HEXFILE=%s\n", hexfile);
- dc_status_t a = fwupdate (name, hexfile); + dc_status_t a = fwupdate (name, hexfile, ostc3);
message ("SUMMARY\n"); message ("-------\n");
Signed-off-by: Anton Lundin glance@acc.umu.se --- src/hw_ostc3.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c index 1daa30e..0851daa 100644 --- a/src/hw_ostc3.c +++ b/src/hw_ostc3.c @@ -2,6 +2,7 @@ * libdivecomputer * * Copyright (C) 2013 Jef Driesen + * Copyright (C) 2014 Anton Lundin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public
Signed-off-by: Anton Lundin glance@acc.umu.se --- src/hw_ostc3.c | 2 -- 1 file changed, 2 deletions(-)
diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c index 0851daa..28cc1f8 100644 --- a/src/hw_ostc3.c +++ b/src/hw_ostc3.c @@ -29,8 +29,6 @@ #include "context-private.h" #include "device-private.h" #include "serial.h" -#include "checksum.h" -#include "ringbuffer.h" #include "array.h" #include "aes.h"