// // -------------------------------------------------------------------------- // Gurux Ltd // // // // Filename: $HeadURL$ // // Version: $Revision$, // $Date$ // $Author$ // // Copyright (c) Gurux Ltd // //--------------------------------------------------------------------------- // // DESCRIPTION // // This file is a part of Gurux Device Framework. // // Gurux Device Framework is Open Source software; you can redistribute it // and/or modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; version 2 of the License. // Gurux Device Framework is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU General Public License for more details. // // This code is licensed under the GNU General Public License v2. // Full text may be retrieved at http://www.gnu.org/licenses/gpl-2.0.txt //--------------------------------------------------------------------------- #include "gxignore.h" #include "gxmem.h" #ifndef DLMS_IGNORE_HIGH_GMAC #if defined(_WIN32) || defined(_WIN64) || defined(__linux__) #include #endif #if defined(USE_AVR) || defined(ARDUINO_ARCH_AVR) //If AVR is used. #include #endif //#if defined(USE_AVR) || defined(ARDUINO_ARCH_AVR) #if _MSC_VER > 1400 #include #endif #include #include "enums.h" #include "errorcodes.h" #include "ciphering.h" #include "helpers.h" #include "gxaes.h" static const unsigned char DEFAUlT_BROADCAST_BLOCK_CIPHER_KEY[] = { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA }; static const unsigned char DEFAUlT_BLOCK_CIPHER_KEY[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; static const unsigned char DEFAULT_SYSTEM_TITLE[] = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48 }; static const unsigned char DEFAUlT_AUTHENTICATION_KEY[] = { 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF }; void cip_init(ciphering* target) { target->invocationCounter = 0; target->suite = DLMS_SECURITY_SUITE_V0; target->security = DLMS_SECURITY_NONE; target->encrypt = 0; #ifndef DLMS_IGNORE_MALLOC BYTE_BUFFER_INIT(&target->blockCipherKey); bb_set(&target->blockCipherKey, DEFAUlT_BLOCK_CIPHER_KEY, sizeof(DEFAUlT_BLOCK_CIPHER_KEY)); BYTE_BUFFER_INIT(&target->broadcastBlockCipherKey); bb_set(&target->broadcastBlockCipherKey, DEFAUlT_BROADCAST_BLOCK_CIPHER_KEY, sizeof(DEFAUlT_BROADCAST_BLOCK_CIPHER_KEY)); BYTE_BUFFER_INIT(&target->systemTitle); bb_set(&target->systemTitle, DEFAULT_SYSTEM_TITLE, sizeof(DEFAULT_SYSTEM_TITLE)); BYTE_BUFFER_INIT(&target->authenticationKey); bb_set(&target->authenticationKey, DEFAUlT_AUTHENTICATION_KEY, sizeof(DEFAUlT_AUTHENTICATION_KEY)); target->dedicatedKey = NULL; #else memcpy(target->blockCipherKey, DEFAUlT_BLOCK_CIPHER_KEY, sizeof(DEFAUlT_BLOCK_CIPHER_KEY)); memcpy(target->broadcastBlockCipherKey, DEFAUlT_BROADCAST_BLOCK_CIPHER_KEY, sizeof(DEFAUlT_BROADCAST_BLOCK_CIPHER_KEY)); memcpy(target->systemTitle, DEFAULT_SYSTEM_TITLE, sizeof(DEFAULT_SYSTEM_TITLE)); memcpy(target->authenticationKey, DEFAUlT_AUTHENTICATION_KEY, sizeof(DEFAUlT_AUTHENTICATION_KEY)); memset(target->dedicatedKey, 0, 16); #endif //DLMS_IGNORE_MALLOC target->broacast = 0; } void cip_clear(ciphering* target) { target->invocationCounter = 1; target->security = DLMS_SECURITY_NONE; target->encrypt = 0; #ifndef DLMS_IGNORE_MALLOC bb_clear(&target->blockCipherKey); bb_clear(&target->broadcastBlockCipherKey); bb_clear(&target->systemTitle); bb_clear(&target->authenticationKey); if (target->dedicatedKey != NULL) { bb_clear(target->dedicatedKey); gxfree(target->dedicatedKey); target->dedicatedKey = NULL; } #else memset(target->blockCipherKey, 0, sizeof(DEFAUlT_BLOCK_CIPHER_KEY)); memset(target->broadcastBlockCipherKey, 0, sizeof(DEFAUlT_BROADCAST_BLOCK_CIPHER_KEY)); memset(target->systemTitle, 0, 8); memset(target->authenticationKey, 0, sizeof(DEFAUlT_AUTHENTICATION_KEY)); memset(target->dedicatedKey, 0, sizeof(DEFAUlT_BLOCK_CIPHER_KEY)); #endif //DLMS_IGNORE_MALLOC } /** * Get nonse from frame counter and system title. * * @param frameCounter * Frame counter. * @param systemTitle * System title. * @return */ static int cip_getNonse(uint32_t frameCounter, unsigned char* systemTitle, gxByteBuffer* nonce) { int ret; nonce->size = 0; if ((ret = bb_capacity(nonce, 12)) != 0 || (ret = bb_set(nonce, systemTitle, 8)) != 0 || (ret = bb_setUInt32(nonce, frameCounter)) != 0) { return ret; } return 0; } /////////////////////////////////////////////////////////////////////////////////////// //Note! Rcon value is different than in aes! #ifndef USE_PROGMEM static const unsigned char __R_CON[11] = { #else static const unsigned char __R_CON[11] PROGMEM = { #endif //#if defined(_WIN32) || defined(_WIN64) 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 }; /////////////////////////////////////////////////////////////////////////////////////// //There is only one TE table. Counting is taking little bit longer, but memory is needed less. #ifndef USE_PROGMEM static const uint32_t __TE[256] = { #else static const uint32_t __TE[256] PROGMEM = { #endif //#if defined(_WIN32) || defined(_WIN64) 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, }; //Note! return value must be uint32_t. static inline uint32_t GetRcon(unsigned char offset) { #ifdef ARDUINO_ARCH_AVR return pgm_read_dword_far(__R_CON + offset); #else return __R_CON[offset]; #endif //ARDUINO_ARCH_AVR } static inline uint32_t GetTe(unsigned char offset) { #ifdef ARDUINO_ARCH_AVR return pgm_read_dword_far(__TE + offset); #else return __TE[offset]; #endif //ARDUINO_ARCH_AVR } #define RCON(i) (GetRcon(i) << 24) #define ROTATE(val, bits) ((val >> bits) | (val << (32 - bits))) #define TE0(i) GetTe(((i) >> 24) & 0xff) #define TE1(i) ROTATE(GetTe(((i) >> 16) & 0xff), 8) #define TE2(i) ROTATE(GetTe(((i) >> 8) & 0xff), 16) #define TE3(i) ROTATE(GetTe((i) & 0xff), 24) #define TE41(i) ((GetTe(((i) >> 24) & 0xff) << 8) & 0xff000000) #define TE42(i) (GetTe(((i) >> 16) & 0xff) & 0x00ff0000) #define TE43(i) (GetTe(((i) >> 8) & 0xff) & 0x0000ff00) #define TE44(i) ((GetTe((i) & 0xff) >> 8) & 0x000000ff) #define TE421(i) ((GetTe(((i) >> 16) & 0xff) << 8) & 0xff000000) #define TE432(i) (GetTe(((i) >> 8) & 0xff) & 0x00ff0000) #define TE443(i) (GetTe((i) & 0xff) & 0x0000ff00) #define TE414(i) ((GetTe(((i) >> 24) & 0xff) >> 8) & 0x000000ff) #define TE411(i) ((GetTe(((i) >> 24) & 0xff) << 8) & 0xff000000) #define TE422(i) (GetTe(((i) >> 16) & 0xff) & 0x00ff0000) #define TE433(i) (GetTe(((i) >> 8) & 0xff) & 0x0000ff00) #define TE444(i) ((GetTe((i) & 0xff) >> 8) & 0x000000ff) int cip_int(uint32_t* rk, const unsigned char* cipherKey, uint16_t keyBits) { uint8_t i; uint32_t temp; rk[0] = GETU32(cipherKey); rk[1] = GETU32(cipherKey + 4); rk[2] = GETU32(cipherKey + 8); rk[3] = GETU32(cipherKey + 12); if (keyBits == 128) { for (i = 0; i < 10; i++) { temp = rk[3]; rk[4] = rk[0] ^ TE421(temp) ^ TE432(temp) ^ TE443(temp) ^ TE414(temp); rk[4] = rk[0] ^ TE421(temp) ^ TE432(temp) ^ TE443(temp) ^ TE414(temp) ^ RCON(i); rk[5] = rk[1] ^ rk[4]; rk[6] = rk[2] ^ rk[5]; rk[7] = rk[3] ^ rk[6]; rk += 4; } return 0; } if (keyBits != 256) { return DLMS_ERROR_CODE_INVALID_PARAMETER; } rk[4] = GETU32(cipherKey + 16); rk[5] = GETU32(cipherKey + 20); rk[6] = GETU32(cipherKey + 24); rk[7] = GETU32(cipherKey + 28); for (i = 0; i < 7; i++) { temp = rk[7]; rk[8] = rk[0] ^ TE421(temp) ^ TE432(temp) ^ TE443(temp) ^ TE414(temp) ^ RCON(i); rk[9] = rk[1] ^ rk[8]; rk[10] = rk[2] ^ rk[9]; rk[11] = rk[3] ^ rk[10]; if (i == 6) { return 0; } temp = rk[11]; rk[12] = rk[4] ^ TE411(temp) ^ TE422(temp) ^ TE433(temp) ^ TE444(temp); rk[13] = rk[5] ^ rk[12]; rk[14] = rk[6] ^ rk[13]; rk[15] = rk[7] ^ rk[14]; rk += 8; } return DLMS_ERROR_CODE_OK; } //Arduino DOIT ESP32 uses aes_encrypt. For that reason aes_Encrypt is used. void aes_Encrypt(const uint32_t* rk, int Nr, const unsigned char* pt, unsigned char* ct) { uint32_t s0, s1, s2, s3, t0, t1, t2, t3; int r; s0 = GETU32(pt) ^ rk[0]; s1 = GETU32(pt + 4) ^ rk[1]; s2 = GETU32(pt + 8) ^ rk[2]; s3 = GETU32(pt + 12) ^ rk[3]; #define ROUND(i,d,s) \ d##0 = TE0(s##0) ^ TE1(s##1) ^ TE2(s##2) ^ TE3(s##3) ^ rk[4 * i]; \ d##1 = TE0(s##1) ^ TE1(s##2) ^ TE2(s##3) ^ TE3(s##0) ^ rk[4 * i + 1]; \ d##2 = TE0(s##2) ^ TE1(s##3) ^ TE2(s##0) ^ TE3(s##1) ^ rk[4 * i + 2]; \ d##3 = TE0(s##3) ^ TE1(s##0) ^ TE2(s##1) ^ TE3(s##2) ^ rk[4 * i + 3] /* Nr - 1 full rounds: */ r = Nr >> 1; for (;;) { ROUND(1, t, s); rk += 8; if (--r == 0) break; ROUND(0, s, t); } #undef ROUND s0 = TE41(t0) ^ TE42(t1) ^ TE43(t2) ^ TE44(t3) ^ rk[0]; PUT32(ct, s0); s1 = TE41(t1) ^ TE42(t2) ^ TE43(t3) ^ TE44(t0) ^ rk[1]; PUT32(ct + 4, s1); s2 = TE41(t2) ^ TE42(t3) ^ TE43(t0) ^ TE44(t1) ^ rk[2]; PUT32(ct + 8, s2); s3 = TE41(t3) ^ TE42(t0) ^ TE43(t1) ^ TE44(t2) ^ rk[3]; PUT32(ct + 12, s3); } /** * Make cip_xor for 128 bits. * * @param block * block. * @param val */ static void cip_xor( unsigned char* dst, const unsigned char* src) { int pos; for (pos = 0; pos != 16; ++pos) { dst[pos] ^= src[pos]; } /* uint32_t *d = (uint32_t *)dst; uint32_t *s = (uint32_t *)src; *d++ ^= *s++; *d++ ^= *s++; *d++ ^= *s++; *d++ ^= *s++; */ } static void shift_right_block(unsigned char* v) { uint32_t val = GETU32(v + 12); val >>= 1; if (v[11] & 0x01) { val |= 0x80000000; } PUT32(v + 12, val); val = GETU32(v + 8); val >>= 1; if (v[7] & 0x01) val |= 0x80000000; PUT32(v + 8, val); val = GETU32(v + 4); val >>= 1; if (v[3] & 0x01) val |= 0x80000000; PUT32(v + 4, val); val = GETU32(v); val >>= 1; PUT32(v, val); } static void cip_multiplyH(const unsigned char* x, const unsigned char* y, unsigned char* z) { unsigned char tmp[16]; int i, j; memset(z, 0, 16); memcpy(tmp, y, 16); for (i = 0; i < 16; i++) { for (j = 0; j < 8; j++) { if (x[i] & 1 << (7 - j)) { cip_xor(z, tmp); } //If last bit. if (tmp[15] & 0x01) { shift_right_block(tmp); tmp[0] ^= 0xe1; } else { shift_right_block(tmp); } } } } /** * Count GHash. */ static void cip_getGHash( const unsigned char* h, const unsigned char* x, int xlen, unsigned char* y) { int m, i; const unsigned char* xpos = x; unsigned char tmp[16]; m = xlen / 16; for (i = 0; i < m; i++) { cip_xor(y, xpos); xpos += 16; cip_multiplyH(y, h, tmp); memcpy(y, tmp, 16); } if (x + xlen > xpos) { size_t last = x + xlen - xpos; memcpy(tmp, xpos, last); memset(tmp + last, 0, sizeof(tmp) - last); cip_xor(y, tmp); cip_multiplyH(y, h, tmp); memcpy(y, tmp, 16); } } static void cip_init_j0( const unsigned char* iv, unsigned char len, const unsigned char* H, unsigned char* J0) { //IV length. if (len == 12) { memcpy(J0, iv, len); memset(J0 + len, 0, 16 - len); J0[15] = 0x01; } else { unsigned char tmp[16]; memset(J0, 0, 16); cip_getGHash(H, iv, len, J0); PUT32(tmp, (uint32_t)0); PUT32(tmp + 4, (uint32_t)0); //Here is expected that data is newer longger than 32 bit. //This is done because microcontrollers show warning here. PUT32(tmp + 8, (uint32_t)0); PUT32(tmp + 12, (uint32_t)(len * 8)); cip_getGHash(H, tmp, sizeof(tmp), J0); } } static void cip_inc32(unsigned char* block) { uint32_t val; val = GETU32(block + 16 - 4); val++; PUT32(block + 16 - 4, val); } static void cip_gctr(uint32_t* aes, const unsigned char* icb, unsigned char* in, int len, unsigned char* out) { size_t i, n, last; unsigned char cb[16], tmp[16]; unsigned char* pin = in; unsigned char* pout = out; if (len == 0) { return; } n = len / 16; memcpy(cb, icb, 16); //Full blocks. for (i = 0; i < n; i++) { if (out == NULL) { aes_Encrypt(aes, aes[60], cb, tmp); cip_xor(pin, tmp); } else { aes_Encrypt(aes, aes[60], cb, pout); cip_xor(pout, pin); } pin += 16; pout += 16; cip_inc32(cb); } last = in + len - pin; //Last, partial block. if (last) { aes_Encrypt(aes, aes[60], cb, tmp); for (i = 0; i < last; i++) { if (out == NULL) { *pin ^= tmp[i]; ++pin; } else { *pout = *pin++ ^ tmp[i]; ++pout; } } } } static void aes_gcm_gctr(uint32_t* aes, const unsigned char* J0, unsigned char* in, int len, unsigned char* out) { unsigned char J0inc[16]; if (len == 0) { return; } memcpy(J0inc, J0, 16); cip_inc32(J0inc); cip_gctr(aes, J0inc, in, len, out); } static void aes_gcm_ghash(const unsigned char* H, const unsigned char* aad, int aad_len, const unsigned char* crypt, int crypt_len, unsigned char* S) { unsigned char len_buf[16]; cip_getGHash(H, aad, aad_len, S); cip_getGHash(H, crypt, crypt_len, S); //Here is expected that data is never longer than 32 bit. //This is done because microcontrollers show warning here. PUT32(len_buf, (uint32_t)0); PUT32(len_buf + 4, (uint32_t)(aad_len * 8)); PUT32(len_buf + 8, (uint32_t)0); PUT32(len_buf + 12, (uint32_t)(crypt_len * 8)); cip_getGHash(H, len_buf, sizeof(len_buf), S); } #ifndef DLMS_IGNORE_MALLOC int cip_crypt( ciphering* settings, DLMS_SECURITY security, DLMS_COUNT_TYPE type, uint32_t frameCounter, unsigned char tag, unsigned char* systemTitle, gxByteBuffer* key, gxByteBuffer* input, unsigned char encrypt) #else int cip_crypt( ciphering* settings, DLMS_SECURITY security, DLMS_COUNT_TYPE type, uint32_t frameCounter, unsigned char tag, unsigned char* systemTitle, unsigned char* key, gxByteBuffer* input, unsigned char encrypt) #endif //DLMS_IGNORE_MALLOC { int ret; #ifdef GX_DLMS_MICROCONTROLLER static uint32_t aes[61] = { 0 }; static unsigned char H[16] = { 0 }; static unsigned char J0[16] = { 0 }; static unsigned char S[16] = { 0 }; //Nonse must be 20 bytes because it's used later. static unsigned char NONSE[20] = { 0 }; memset(H, 0, sizeof(H)); memset(S, 0, sizeof(S)); #else uint32_t aes[61] = { 0 }; unsigned char H[16] = { 0 }; unsigned char J0[16] = { 0 }; unsigned char S[16] = { 0 }; //Nonse must be 20 bytes because it's used later. unsigned char NONSE[20] = { 0 }; #endif //GX_DLMS_MICROCONTROLLER gxByteBuffer nonse; if (memcmp(systemTitle, EMPTY_SYSTEM_TITLE, 8) == 0) { return DLMS_ERROR_CODE_INVALID_PARAMETER; } bb_attach(&nonse, NONSE, 0, sizeof(NONSE)); if ((ret = cip_getNonse(frameCounter, systemTitle, &nonse)) != 0) { return ret; } if ((ret = cip_int( aes, #ifndef DLMS_IGNORE_MALLOC key->data, #else key, #endif //DLMS_IGNORE_MALLOC settings->suite == DLMS_SECURITY_SUITE_V2 ? 32 * 8 : 16 * 8)) != 0) { return ret; } if (settings->suite == DLMS_SECURITY_SUITE_V2) { aes[60] = 14; } else { aes[60] = 10; } //Hash subkey. aes_Encrypt(aes, aes[60], H, H); cip_init_j0(nonse.data, (unsigned char)nonse.size, H, J0); //Allocate space for authentication tag. if (security != DLMS_SECURITY_ENCRYPTION && !encrypt) { //Save authentication key to nonse. bb_clear(&nonse); ret = bb_set(&nonse, input->data + input->size - 12, 12); input->size -= 12; } unsigned char offset; if (settings->suite == DLMS_SECURITY_SUITE_V2) { offset = 33; } else { offset = 17; } if (security == DLMS_SECURITY_AUTHENTICATION) { if ((ret = bb_move(input, input->position, offset, bb_available(input))) == 0) { input->position = 0; bb_setUInt8ByIndex(input, 0, security | settings->suite); #ifndef DLMS_IGNORE_MALLOC memcpy(input->data + 1, settings->authenticationKey.data, settings->authenticationKey.size); #else memcpy(input->data + 1, settings->authenticationKey, offset - 1); #endif //DLMS_IGNORE_MALLOC aes_gcm_ghash(H, input->data, input->size, input->data, 0, S); if ((ret = bb_move(input, offset, 0, input->size - offset)) == 0) { cip_gctr(aes, J0, S, sizeof(S), input->data + input->size); if (encrypt) { input->size += 12; } else { if (memcmp(NONSE, input->data + input->size, 12) != 0) { ret = DLMS_ERROR_CODE_INVALID_TAG; } } } } } else if (security == DLMS_SECURITY_ENCRYPTION) { //Encrypt the data. aes_gcm_ghash(H, input->data, input->size, input->data, 0, S); if (!encrypt) { ret = bb_move(input, input->position, 0, bb_available(input)); input->position = 0; } aes_gcm_gctr(aes, J0, input->data + input->position, bb_available(input), NULL); } else if (security == DLMS_SECURITY_AUTHENTICATION_ENCRYPTION) { if (encrypt) { //Encrypt the data. aes_gcm_gctr(aes, J0, input->data + input->position, bb_available(input), NULL); } if ((ret = bb_move(input, input->position, offset, bb_available(input))) == 0) { input->position = 0; ret = bb_setUInt8ByIndex(input, 0, security | settings->suite); #ifndef DLMS_IGNORE_MALLOC memcpy(input->data + 1, settings->authenticationKey.data, settings->authenticationKey.size); #else memcpy(input->data + 1, settings->authenticationKey, offset - 1); #endif //DLMS_IGNORE_MALLOC aes_gcm_ghash(H, input->data, offset, input->data + offset, input->size - offset, S); if ((ret = bb_move(input, offset, 0, input->size - offset)) == 0) { if (!encrypt) { cip_gctr(aes, J0, S, sizeof(S), input->data + input->size); //Decrypt the data. aes_gcm_gctr(aes, J0, input->data + input->position, bb_available(input), NULL); } cip_gctr(aes, J0, S, sizeof(S), input->data + input->size); if (encrypt) { input->size += 12; } else { //Check authentication tag. if (memcmp(NONSE, input->data + input->size, 12) != 0) { ret = DLMS_ERROR_CODE_INVALID_TAG; } else { ret = bb_move(input, input->position, 0, bb_available(input)); } input->position = 0; } } } } if (ret == 0 && encrypt) { ++settings->invocationCounter; } if (ret == 0 && encrypt && type == DLMS_COUNT_TYPE_PACKET) { if ((ret = bb_clear(&nonse)) == 0 && (ret = bb_setUInt8(&nonse, tag)) == 0) { if (tag == DLMS_COMMAND_GENERAL_GLO_CIPHERING || tag == DLMS_COMMAND_GENERAL_DED_CIPHERING) { hlp_setObjectCount(8, &nonse); bb_set(&nonse, systemTitle, 8); } tag = security | settings->suite; if (settings->broacast) { tag |= 0x40; } if ((ret = hlp_setObjectCount(5 + input->size, &nonse)) == 0 && (ret = bb_setUInt8(&nonse, tag)) == 0 && (ret = bb_setUInt32(&nonse, frameCounter)) == 0 && (ret = bb_insert(nonse.data, nonse.size, input, 0)) == 0) { memcpy(input->data, nonse.data, nonse.size); } } } return ret; } #ifndef DLMS_IGNORE_MALLOC int cip_encrypt( ciphering* settings, DLMS_SECURITY security, DLMS_COUNT_TYPE type, uint32_t frameCounter, unsigned char tag, unsigned char* systemTitle, gxByteBuffer* key, gxByteBuffer* input) #else int cip_encrypt( ciphering* settings, DLMS_SECURITY security, DLMS_COUNT_TYPE type, uint32_t frameCounter, unsigned char tag, unsigned char* systemTitle, unsigned char* key, gxByteBuffer* input) #endif //DLMS_IGNORE_MALLOC { return cip_crypt( settings, security, type, frameCounter, tag, systemTitle, key, input, 1); } #ifndef DLMS_IGNORE_MALLOC int cip_decrypt( ciphering* settings, unsigned char* title, gxByteBuffer* key, gxByteBuffer* data, DLMS_SECURITY* security, DLMS_SECURITY_SUITE* suite, uint64_t* invocationCounter) #else int cip_decrypt( ciphering* settings, unsigned char* title, unsigned char* key, gxByteBuffer* data, DLMS_SECURITY* security, DLMS_SECURITY_SUITE* suite, uint64_t* invocationCounter) #endif //DLMS_IGNORE_MALLOC { #ifdef GX_DLMS_MICROCONTROLLER static unsigned char systemTitle[8]; #else unsigned char systemTitle[8]; #endif //GX_DLMS_MICROCONTROLLER uint16_t length; int ret; unsigned char ch; uint32_t frameCounter; DLMS_COMMAND cmd; if (data == NULL || data->size - data->position < 2) { return DLMS_ERROR_CODE_INVALID_PARAMETER; } if ((ret = bb_getUInt8(data, &ch)) != 0) { return ret; } cmd = (DLMS_COMMAND)ch; switch (cmd) { case DLMS_COMMAND_GENERAL_GLO_CIPHERING: case DLMS_COMMAND_GENERAL_DED_CIPHERING: if ((ret = hlp_getObjectCount2(data, &length)) != 0) { return ret; } if (length != 0) { if (length != 8) { return DLMS_ERROR_CODE_INVALID_PARAMETER; } bb_get(data, systemTitle, length); title = systemTitle; } break; case DLMS_COMMAND_GLO_INITIATE_REQUEST: case DLMS_COMMAND_GLO_INITIATE_RESPONSE: case DLMS_COMMAND_GLO_READ_REQUEST: case DLMS_COMMAND_GLO_READ_RESPONSE: case DLMS_COMMAND_GLO_WRITE_REQUEST: case DLMS_COMMAND_GLO_WRITE_RESPONSE: case DLMS_COMMAND_GLO_GET_REQUEST: case DLMS_COMMAND_GLO_GET_RESPONSE: case DLMS_COMMAND_GLO_SET_REQUEST: case DLMS_COMMAND_GLO_SET_RESPONSE: case DLMS_COMMAND_GLO_METHOD_REQUEST: case DLMS_COMMAND_GLO_METHOD_RESPONSE: case DLMS_COMMAND_GLO_EVENT_NOTIFICATION_REQUEST: case DLMS_COMMAND_DED_INITIATE_REQUEST: case DLMS_COMMAND_DED_INITIATE_RESPONSE: case DLMS_COMMAND_DED_GET_REQUEST: case DLMS_COMMAND_DED_GET_RESPONSE: case DLMS_COMMAND_DED_SET_REQUEST: case DLMS_COMMAND_DED_SET_RESPONSE: case DLMS_COMMAND_DED_METHOD_REQUEST: case DLMS_COMMAND_DED_METHOD_RESPONSE: case DLMS_COMMAND_DED_EVENT_NOTIFICATION: break; default: return DLMS_ERROR_CODE_INVALID_PARAMETER; } if ((ret = hlp_getObjectCount2(data, &length)) != 0) { return ret; } if ((ret = bb_getUInt8(data, &ch)) != 0) { return ret; } *security = (DLMS_SECURITY)(ch & 0x30); if (suite != NULL) { *suite = (DLMS_SECURITY_SUITE) (ch & 0x3); } //If Key_Set or authentication or encryption is not used. if (ch & 0x40 || *security == DLMS_SECURITY_NONE) { return DLMS_ERROR_CODE_INVALID_PARAMETER; } if ((ret = bb_getUInt32(data, &frameCounter)) != 0) { return ret; } if (invocationCounter != NULL) { *invocationCounter = frameCounter; } ret = cip_crypt( settings, *security, DLMS_COUNT_TYPE_DATA, frameCounter, 0, title, key, data, 0); return ret; } static const unsigned char WRAP_IV[] = { 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6 }; int cip_encryptKey( unsigned char* kek, unsigned char size, gxByteBuffer* data, gxByteBuffer* output) { unsigned char buf[16] = { 0 }; unsigned char buf2[16] = { 0 }; unsigned char n, j, i; if (kek == NULL || size != 16 || data == NULL || data->size != 16 || output == NULL) { return DLMS_ERROR_CODE_INVALID_PARAMETER; } bb_capacity(output, 24); output->size = 24; output->position = 0; // Amount of 64-bit blocks. n = (unsigned char)(data->size >> 3); memcpy(output->data, WRAP_IV, 8); memcpy(output->data + 8, data->data, data->size); for (j = 0; j != 6; j++) { for (i = 1; i <= n; i++) { memcpy(buf, output->data, 8); memcpy(buf + 8, output->data + (8 * i), 8); gxaes_ecb_encrypt(buf, kek, buf2, 16); unsigned int t = n * j + i; for (int k = 1; t != 0; k++) { unsigned char v = (unsigned char)t; buf2[sizeof(WRAP_IV) - k] ^= v; t = (int)((unsigned int)t >> 8); } memcpy(output->data, buf2, 8); memcpy(output->data + (8 * i), buf2 + 8, 8); } } return 0; } int cip_decryptKey( unsigned char* kek, //KEK size. unsigned char size, gxByteBuffer* data, gxByteBuffer* output) { int ret; unsigned char a[8]; unsigned char buf[16]; unsigned char buf2[16]; signed char j, i; unsigned char k, v, n; uint16_t t; // Amount of 64-bit blocks. n = (unsigned char)(bb_size(data) >> 3); if (kek == NULL || size != 16 || data == NULL || data->size != n * 8 || output == NULL) { return DLMS_ERROR_CODE_INVALID_PARAMETER; } if ((ret = bb_capacity(output, 16)) != 0) { return ret; } output->size = 16; output->position = 0; memcpy(a, data->data, sizeof(WRAP_IV)); memcpy(output->data, data->data + sizeof(WRAP_IV), data->size - sizeof(WRAP_IV)); if (--n == 0) { n = 1; } for (j = 5; j >= 0; j--) { for (i = n; i >= 1; i--) { memcpy(buf, a, sizeof(WRAP_IV)); memcpy(buf + sizeof(WRAP_IV), output->data + 8 * (i - 1), 8); t = n * j + i; for (k = 1; t != 0; k++) { v = (unsigned char)t; buf[sizeof(WRAP_IV) - k] ^= v; t = (uint16_t)(t >> 8); } gxaes_ecb_decrypt(buf, kek, buf2, 16); memcpy(a, buf2, 8); memcpy(output->data + 8 * (i - 1), buf2 + 8, 8); } } if (memcmp(a, WRAP_IV, sizeof(WRAP_IV)) != 0) { output->size = 0; return DLMS_ERROR_CODE_FALSE; } return 0; } #endif //DLMS_IGNORE_HIGH_GMAC