diff --git a/examples/knx-demo/.gitignore b/examples/knx-demo/.gitignore index 89cc49c..b9f3806 100644 --- a/examples/knx-demo/.gitignore +++ b/examples/knx-demo/.gitignore @@ -1,5 +1,2 @@ .pio -.vscode/.browse.c_cpp.db* -.vscode/c_cpp_properties.json -.vscode/launch.json -.vscode/ipch +.vscode diff --git a/examples/knx-demo/.vscode/extensions.json b/examples/knx-demo/.vscode/extensions.json deleted file mode 100644 index 272828b..0000000 --- a/examples/knx-demo/.vscode/extensions.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - // See http://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format - "recommendations": [ - "platformio.platformio-ide" - ] -} \ No newline at end of file diff --git a/examples/knx-demo/platformio-ci.ini b/examples/knx-demo/platformio-ci.ini index a0b0a11..57c4188 100644 --- a/examples/knx-demo/platformio-ci.ini +++ b/examples/knx-demo/platformio-ci.ini @@ -26,7 +26,7 @@ build_flags = ;--- ESP8266 ----------------------------------------------- [env:nodemcuv2] -platform = espressif8266 +platform = espressif8266@2.2.3 board = nodemcuv2 framework = arduino lib_deps = diff --git a/examples/knx-demo/platformio.ini b/examples/knx-demo/platformio.ini index 6037a2e..6134de6 100644 --- a/examples/knx-demo/platformio.ini +++ b/examples/knx-demo/platformio.ini @@ -35,7 +35,7 @@ build_flags = ;--- ESP8266 ----------------------------------------------- [env:nodemcuv2] -platform = espressif8266 +platform = espressif8266@2.2.3 board = nodemcuv2 framework = arduino ; We consider that the this projects is opened within its project directory @@ -43,7 +43,7 @@ framework = arduino lib_extra_dirs = ../../../ lib_deps = - WifiManager + WifiManager@0.15.0 knx build_flags = diff --git a/examples/knx-linux/CMakeLists.txt b/examples/knx-linux/CMakeLists.txt index f654713..fe87d43 100644 --- a/examples/knx-linux/CMakeLists.txt +++ b/examples/knx-linux/CMakeLists.txt @@ -4,7 +4,10 @@ set(LIBRARIES_FROM_REFERENCES "") add_executable(knx-linux ../../src/knx/address_table_object.cpp ../../src/knx/address_table_object.h - ../../src/knx/apdu.cpp + ../../src/knx/aes.c + ../../src/knx/aes.h + ../../src/knx/aes.hpp + ../../src/knx/apdu.cpp ../../src/knx/apdu.h ../../src/knx/application_layer.cpp ../../src/knx/application_layer.h @@ -42,7 +45,8 @@ add_executable(knx-linux ../../src/knx/dpt.h ../../src/knx/dptconvert.cpp ../../src/knx/dptconvert.h - ../../src/knx/group_object.cpp + ../../src/knx/function_property.h + ../../src/knx/group_object.cpp ../../src/knx/group_object.h ../../src/knx/group_object_table_object.cpp ../../src/knx/group_object_table_object.h @@ -87,6 +91,12 @@ add_executable(knx-linux ../../src/knx/rf_medium_object.h ../../src/knx/rf_physical_layer.cpp ../../src/knx/rf_physical_layer.h + ../../src/knx/secure_application_layer.cpp + ../../src/knx/secure_application_layer.h + ../../src/knx/security_interface_object.cpp + ../../src/knx/security_interface_object.h + ../../src/knx/simple_map.h + ../../src/knx/table_object.cpp ../../src/knx/save_restore.h ../../src/knx/table_object.cpp ../../src/knx/table_object.h @@ -102,6 +112,8 @@ add_executable(knx-linux ../../src/knx_facade.h ../../src/linux_platform.cpp ../../src/linux_platform.h + fdsk.cpp + fdsk.h main.cpp) target_link_libraries(knx-linux "${LIBRARIES_FROM_REFERENCES}") include_directories(../../src) diff --git a/examples/knx-linux/fdsk.cpp b/examples/knx-linux/fdsk.cpp new file mode 100644 index 0000000..8d9b28c --- /dev/null +++ b/examples/knx-linux/fdsk.cpp @@ -0,0 +1,196 @@ +#include "fdsk.h" + +#include + +// CRC-4 generator polynom: 10011 (x^4+x+1) +const uint8_t FdskCalculator::crc4_tab[16] = +{ + 0x0, 0x3, 0x6, 0x5, 0xc, 0xf, 0xa, 0x9, + 0xb, 0x8, 0xd, 0xe, 0x7, 0x4, 0x1, 0x2 +}; + +int FdskCalculator::snprintFdsk(char* str, int strSize, uint8_t* serialNumber, uint8_t* key) +{ + char* tmpStr = generateFdskString(serialNumber, key); + int written = 0; + + for (int i = 0; i < 36; i++) + { + if (((i % 6) == 0) && (i!=0)) + { + *(str+written++) = '-'; + if (written >= strSize-1) + break; + } + *(str+written++) = tmpStr[i]; + if (written >= strSize-1) + break; + } + + *(str+written++) = '\0'; + + delete[] tmpStr; + + return written; +} + +char* FdskCalculator::generateFdskString(uint8_t* serialNumber, uint8_t* key) +{ + uint8_t buffer[6 + 16 + 1]; // 6 bytes serialnumber + 16 bytes key + 1 byte placeholder for crc-4 + memcpy(&buffer[0], serialNumber, 6); + memcpy(&buffer[6], key, 16); + buffer[22] = (crc4Array(buffer, sizeof(buffer)-1)<<4) &0xFF; + + uint8_t* outEncoded = nullptr; + toBase32(buffer, sizeof(buffer), outEncoded, false); + + return (char*)outEncoded; +} + +int FdskCalculator::ceil(float num) +{ + int inum = (int)num; + if (num == (float)inum) { + return inum; + } + return inum + 1; +} + +int FdskCalculator::toBase32(uint8_t* in, long length, uint8_t*& out, bool usePadding) +{ + char base32StandardAlphabet[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"}; + char standardPaddingChar = '='; + + int result = 0; + int count = 0; + int bufSize = 8; + int index = 0; + int size = 0; // size of temporary array + uint8_t* temp = nullptr; + + if (length < 0 || length > 268435456LL) + { + return 0; + } + + size = 8 * ceil(length / 4.0); // Calculating size of temporary array. Not very precise. + temp = new uint8_t[size]; + + if (length > 0) + { + int buffer = in[0]; + int next = 1; + int bitsLeft = 8; + + while (count < bufSize && (bitsLeft > 0 || next < length)) + { + if (bitsLeft < 5) + { + if (next < length) + { + buffer <<= 8; + buffer |= in[next] & 0xFF; + next++; + bitsLeft += 8; + } + else + { + int pad = 5 - bitsLeft; + buffer <<= pad; + bitsLeft += pad; + } + } + index = 0x1F & (buffer >> (bitsLeft -5)); + + bitsLeft -= 5; + temp[result] = (uint8_t)base32StandardAlphabet[index]; + result++; + } + } + + if (usePadding) + { + int pads = (result % 8); + if (pads > 0) + { + pads = (8 - pads); + for (int i = 0; i < pads; i++) + { + temp[result] = standardPaddingChar; + result++; + } + } + } + + out = new uint8_t[result]; + + memcpy(out, temp, result); + delete [] temp; + + return result; +} + +int FdskCalculator::fromBase32(uint8_t* in, long length, uint8_t*& out) +{ + int result = 0; // Length of the array of decoded values. + int buffer = 0; + int bitsLeft = 0; + uint8_t* temp = NULL; + + temp = new uint8_t[length]; // Allocating temporary array. + + for (int i = 0; i < length; i++) + { + uint8_t ch = in[i]; + + // ignoring some characters: ' ', '\t', '\r', '\n', '=' + if (ch == 0xA0 || ch == 0x09 || ch == 0x0A || ch == 0x0D || ch == 0x3D) + continue; + + // recovering mistyped: '0' -> 'O', '1' -> 'L', '8' -> 'B' + if (ch == 0x30) + { + ch = 0x4F; + } + else if (ch == 0x31) + { + ch = 0x4C; + } + else if (ch == 0x38) + { + ch = 0x42; + } + + + // look up one base32 symbols: from 'A' to 'Z' or from 'a' to 'z' or from '2' to '7' + if ((ch >= 0x41 && ch <= 0x5A) || (ch >= 0x61 && ch <= 0x7A)) + { + ch = ((ch & 0x1F) - 1); + } + else if (ch >= 0x32 && ch <= 0x37) + { + ch -= (0x32 - 26); + } + else { + delete [] temp; + return 0; + } + + buffer <<= 5; + buffer |= ch; + bitsLeft += 5; + if (bitsLeft >= 8) + { + temp[result] = (unsigned char)((unsigned int)(buffer >> (bitsLeft - 8)) & 0xFF); + result++; + bitsLeft -= 8; + } + } + + out = new uint8_t[result]; + memcpy(out, temp, result); + delete [] temp; + + return result; +} + diff --git a/examples/knx-linux/fdsk.h b/examples/knx-linux/fdsk.h new file mode 100644 index 0000000..560ba22 --- /dev/null +++ b/examples/knx-linux/fdsk.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +class FdskCalculator +{ + public: + int snprintFdsk(char* str, int strSize, uint8_t* serialNumber, uint8_t* key); + + private: + char* generateFdskString(uint8_t* serialNumber, uint8_t* key); + + int toBase32(uint8_t* in, long length, uint8_t*& out, bool usePadding); + int fromBase32(uint8_t* in, long length, uint8_t*& out); + + uint8_t crc4Array(uint8_t* data, uint8_t len) { + uint8_t start = 0; + for (uint8_t i = 0; i > 4; + c = crc4_tab[c ^ high4Bits]; + c = crc4_tab[c ^ low4Bits]; + + return c; + } + + int ceil(float num); + + static const uint8_t crc4_tab[16]; +}; diff --git a/examples/knx-linux/main.cpp b/examples/knx-linux/main.cpp index dbca23d..f835e0e 100644 --- a/examples/knx-linux/main.cpp +++ b/examples/knx-linux/main.cpp @@ -14,6 +14,8 @@ #include #include +#include "fdsk.h" + volatile sig_atomic_t loopActive = 1; void signalHandler(int sig) { @@ -119,6 +121,14 @@ int main(int argc, char **argv) { printf("main() start.\n"); + uint8_t serialNumber[] = { 0x00, 0xFA, 0x01, 0x02, 0x03, 0x04}; + uint8_t key[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; + + FdskCalculator calc; + char fdskString[42]; // 6 * 6 chars + 5 dashes + nullbyte = 42 + calc.snprintFdsk(fdskString, sizeof(fdskString), serialNumber, key); + printf("FDSK: %s\n", fdskString); + // Prevent swapping of this process struct sched_param sp; memset(&sp, 0, sizeof(sp)); diff --git a/src/arduino_platform.cpp b/src/arduino_platform.cpp index 5ae6db5..b603a95 100644 --- a/src/arduino_platform.cpp +++ b/src/arduino_platform.cpp @@ -111,6 +111,23 @@ int ArduinoPlatform::readWriteSpi(uint8_t *data, size_t len) return 0; } +void printUint64(uint64_t value, int base = DEC) + { + char buf[8 * sizeof(uint64_t) + 1]; + char* str = &buf[sizeof(buf) - 1]; + *str = '\0'; + + uint64_t n = value; + do { + char c = n % base; + n /= base; + + *--str = c < 10 ? c + '0' : c + 'A' - 10; + } while (n > 0); + + print(str); +} + void print(const char* s) { ArduinoPlatform::SerialDebug->print(s); @@ -170,6 +187,16 @@ void print(unsigned long num, int base) ArduinoPlatform::SerialDebug->print(num, base); } +void print(unsigned long long num) +{ + printUint64(num); +} + +void print(unsigned long long num, int base) +{ + printUint64(num, base); +} + void print(double num) { ArduinoPlatform::SerialDebug->print(num); @@ -235,6 +262,18 @@ void println(unsigned long num, int base) ArduinoPlatform::SerialDebug->println(num, base); } +void println(unsigned long long num) +{ + printUint64(num); + println(""); +} + +void println(unsigned long long num, int base) +{ + printUint64(num, base); + println(""); +} + void println(double num) { ArduinoPlatform::SerialDebug->println(num); diff --git a/src/knx/aes.c b/src/knx/aes.c new file mode 100644 index 0000000..eaf2b69 --- /dev/null +++ b/src/knx/aes.c @@ -0,0 +1,569 @@ +/* + +This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode. +Block size can be chosen in aes.h - available choices are AES128, AES192, AES256. + +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. + For AES192/256 the key size is proportionally larger. + +*/ + + +/*****************************************************************************/ +/* Includes: */ +/*****************************************************************************/ +#include // 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 + +#if defined(AES256) && (AES256 == 1) + #define Nk 8 + #define Nr 14 +#elif defined(AES192) && (AES192 == 1) + #define Nk 6 + #define Nr 12 +#else + #define Nk 4 // The number of 32 bit words in a key. + #define Nr 10 // The number of rounds in AES Cipher. +#endif + +// 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-AES-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]; + + + +// 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 the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8) +static const uint8_t Rcon[11] = { + 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 }; + +/* + * Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12), + * that you can remove most of the elements in the Rcon array, because they are unused. + * + * From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon + * + * "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys are needed), + * up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm." + */ + + +/*****************************************************************************/ +/* Private functions: */ +/*****************************************************************************/ +/* +static uint8_t getSBoxValue(uint8_t num) +{ + return sbox[num]; +} +*/ +#define getSBoxValue(num) (sbox[(num)]) +/* +static uint8_t getSBoxInvert(uint8_t num) +{ + return rsbox[num]; +} +*/ +#define getSBoxInvert(num) (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(uint8_t* RoundKey, const uint8_t* Key) +{ + unsigned 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 = Nk; i < Nb * (Nr + 1); ++i) + { + { + k = (i - 1) * 4; + tempa[0]=RoundKey[k + 0]; + tempa[1]=RoundKey[k + 1]; + tempa[2]=RoundKey[k + 2]; + tempa[3]=RoundKey[k + 3]; + + } + + if (i % Nk == 0) + { + // This function shifts the 4 bytes in a word to the left once. + // [a0,a1,a2,a3] becomes [a1,a2,a3,a0] + + // Function RotWord() + { + const uint8_t u8tmp = tempa[0]; + tempa[0] = tempa[1]; + tempa[1] = tempa[2]; + tempa[2] = tempa[3]; + tempa[3] = u8tmp; + } + + // 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]; + } +#if defined(AES256) && (AES256 == 1) + if (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]); + } + } +#endif + j = i * 4; k=(i - Nk) * 4; + RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0]; + RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1]; + RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2]; + RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3]; + } +} + +void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key) +{ + KeyExpansion(ctx->RoundKey, key); +} +#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) +void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv) +{ + KeyExpansion(ctx->RoundKey, key); + memcpy (ctx->Iv, iv, AES_BLOCKLEN); +} +void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv) +{ + memcpy (ctx->Iv, iv, AES_BLOCKLEN); +} +#endif + +// 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, state_t* state, const uint8_t* RoundKey) +{ + 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(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]); + } + } +} + +// 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(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; + + // 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(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 ; + } +} + +// Multiply is used to multiply numbers in the field GF(2^8) +// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary +// The compiler seems to be able to vectorize the operation better this way. +// See https://github.com/kokke/tiny-AES-c/pull/34 +#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)))))); /* this last call to xtime() can be omitted */ + } +#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 + +#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) +// 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(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); + } +} + + +// The SubBytes Function Substitutes the values in the +// state matrix with values in an S-box. +static void InvSubBytes(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]); + } + } +} + +static void InvShiftRows(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; + + // 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; +} +#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) + +// Cipher is the main function that encrypts the PlainText. +static void Cipher(state_t* state, const uint8_t* RoundKey) +{ + uint8_t round = 0; + + // Add the First round key to the state before starting the rounds. + AddRoundKey(0, state, RoundKey); + + // There will be Nr rounds. + // The first Nr-1 rounds are identical. + // These Nr rounds are executed in the loop below. + // Last one without MixColumns() + for (round = 1; ; ++round) + { + SubBytes(state); + ShiftRows(state); + if (round == Nr) { + break; + } + MixColumns(state); + AddRoundKey(round, state, RoundKey); + } + // Add round key to last round + AddRoundKey(Nr, state, RoundKey); +} + +#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) +static void InvCipher(state_t* state, const uint8_t* RoundKey) +{ + uint8_t round = 0; + + // Add the First round key to the state before starting the rounds. + AddRoundKey(Nr, state, RoundKey); + + // There will be Nr rounds. + // The first Nr-1 rounds are identical. + // These Nr rounds are executed in the loop below. + // Last one without InvMixColumn() + for (round = (Nr - 1); ; --round) + { + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(round, state, RoundKey); + if (round == 0) { + break; + } + InvMixColumns(state); + } + +} +#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) + +/*****************************************************************************/ +/* Public functions: */ +/*****************************************************************************/ +#if defined(ECB) && (ECB == 1) + + +void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf) +{ + // The next function call encrypts the PlainText with the Key using AES algorithm. + Cipher((state_t*)buf, ctx->RoundKey); +} + +void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf) +{ + // The next function call decrypts the PlainText with the Key using AES algorithm. + InvCipher((state_t*)buf, ctx->RoundKey); +} + + +#endif // #if defined(ECB) && (ECB == 1) + + + + + +#if defined(CBC) && (CBC == 1) + + +static void XorWithIv(uint8_t* buf, const uint8_t* Iv) +{ + uint8_t i; + for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size + { + buf[i] ^= Iv[i]; + } +} + +void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t* buf, uint32_t length) +{ + uintptr_t i; + uint8_t *Iv = ctx->Iv; + for (i = 0; i < length; i += AES_BLOCKLEN) + { + XorWithIv(buf, Iv); + Cipher((state_t*)buf, ctx->RoundKey); + Iv = buf; + buf += AES_BLOCKLEN; + } + /* store Iv in ctx for next call */ + memcpy(ctx->Iv, Iv, AES_BLOCKLEN); +} + +void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length) +{ + uintptr_t i; + uint8_t storeNextIv[AES_BLOCKLEN]; + for (i = 0; i < length; i += AES_BLOCKLEN) + { + memcpy(storeNextIv, buf, AES_BLOCKLEN); + InvCipher((state_t*)buf, ctx->RoundKey); + XorWithIv(buf, ctx->Iv); + memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN); + buf += AES_BLOCKLEN; + } + +} + +#endif // #if defined(CBC) && (CBC == 1) + + + +#if defined(CTR) && (CTR == 1) + +/* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */ +void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length) +{ + uint8_t buffer[AES_BLOCKLEN]; + + unsigned i; + int bi; + for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi) + { + if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */ + { + + memcpy(buffer, ctx->Iv, AES_BLOCKLEN); + Cipher((state_t*)buffer,ctx->RoundKey); + + /* Increment Iv and handle overflow */ + for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi) + { + /* inc will overflow */ + if (ctx->Iv[bi] == 255) + { + ctx->Iv[bi] = 0; + continue; + } + ctx->Iv[bi] += 1; + break; + } + bi = 0; + } + + buf[i] = (buf[i] ^ buffer[bi]); + } +} + +#endif // #if defined(CTR) && (CTR == 1) + diff --git a/src/knx/aes.h b/src/knx/aes.h new file mode 100644 index 0000000..0d3b2e0 --- /dev/null +++ b/src/knx/aes.h @@ -0,0 +1,90 @@ +#ifndef _AES_H_ +#define _AES_H_ + +#include + +// #define the macros below to 1/0 to enable/disable the mode of operation. +// +// CBC enables AES encryption in CBC-mode of operation. +// CTR enables encryption in counter-mode. +// ECB enables the basic ECB 16-byte block algorithm. All 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 + +#ifndef CTR + #define CTR 1 +#endif + + +#define AES128 1 +//#define AES192 1 +//#define AES256 1 + +#define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only + +#if defined(AES256) && (AES256 == 1) + #define AES_KEYLEN 32 + #define AES_keyExpSize 240 +#elif defined(AES192) && (AES192 == 1) + #define AES_KEYLEN 24 + #define AES_keyExpSize 208 +#else + #define AES_KEYLEN 16 // Key length in bytes + #define AES_keyExpSize 176 +#endif + +struct AES_ctx +{ + uint8_t RoundKey[AES_keyExpSize]; +#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) + uint8_t Iv[AES_BLOCKLEN]; +#endif +}; + +void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key); +#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) +void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv); +void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv); +#endif + +#if defined(ECB) && (ECB == 1) +// buffer size is exactly AES_BLOCKLEN bytes; +// you need only AES_init_ctx as IV is not used in ECB +// NB: ECB is considered insecure for most uses +void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf); +void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf); + +#endif // #if defined(ECB) && (ECB == !) + + +#if defined(CBC) && (CBC == 1) +// buffer size MUST be mutile of AES_BLOCKLEN; +// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme +// NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv() +// no IV should ever be reused with the same key +void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); +void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); + +#endif // #if defined(CBC) && (CBC == 1) + + +#if defined(CTR) && (CTR == 1) + +// Same function for encrypting as for decrypting. +// IV is incremented for every block, and used after encryption as XOR-compliment for output +// Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme +// NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv() +// no IV should ever be reused with the same key +void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); + +#endif // #if defined(CTR) && (CTR == 1) + + +#endif // _AES_H_ diff --git a/src/knx/aes.hpp b/src/knx/aes.hpp new file mode 100644 index 0000000..ade1642 --- /dev/null +++ b/src/knx/aes.hpp @@ -0,0 +1,12 @@ +#ifndef _AES_HPP_ +#define _AES_HPP_ + +#ifndef __cplusplus +#error Do not include the hpp header in a c project! +#endif //__cplusplus + +extern "C" { +#include "aes.h" +} + +#endif //_AES_HPP_ diff --git a/src/knx/application_layer.cpp b/src/knx/application_layer.cpp index 297eb65..36b2ce2 100644 --- a/src/knx/application_layer.cpp +++ b/src/knx/application_layer.cpp @@ -8,6 +8,8 @@ #include "bits.h" #include +const SecurityControl ApplicationLayer::noSecurity {.toolAccess=false, .dataSecurity=DataSecurity::none}; + ApplicationLayer::ApplicationLayer(AssociationTableObject& assocTable, BusAccessUnit& bau): _assocTable(assocTable), _bau(bau) { @@ -21,6 +23,11 @@ void ApplicationLayer::transportLayer(TransportLayer& layer) #pragma region TL Callbacks void ApplicationLayer::dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu) +{ + dataGroupIndication(hopType, priority, tsap, apdu, noSecurity); +} + +void ApplicationLayer::dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl& secCtrl) { uint8_t len = apdu.length(); uint8_t dataArray[len]; @@ -44,13 +51,13 @@ void ApplicationLayer::dataGroupIndication(HopCountType hopType, Priority priori switch (apdu.type()) { case GroupValueRead: - _bau.groupValueReadIndication(asap, priority, hopType); + _bau.groupValueReadIndication(asap, priority, hopType, secCtrl); break; case GroupValueResponse: - _bau.groupValueReadAppLayerConfirm(asap, priority, hopType, data, len); + _bau.groupValueReadAppLayerConfirm(asap, priority, hopType, secCtrl, data, len); break; case GroupValueWrite: - _bau.groupValueWriteIndication(asap, priority, hopType, data, len); + _bau.groupValueWriteIndication(asap, priority, hopType, secCtrl, data, len); default: /* other apdutypes ar not valid here. If the appear do nothing */ break; @@ -58,18 +65,23 @@ void ApplicationLayer::dataGroupIndication(HopCountType hopType, Priority priori } } -void ApplicationLayer::dataGroupConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status) +void ApplicationLayer::dataGroupConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status) +{ + dataGroupConfirm(ack, hopType, priority, tsap, apdu, noSecurity, status); +} + +void ApplicationLayer::dataGroupConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl &secCtrl, bool status) { switch (apdu.type()) { case GroupValueRead: - _bau.groupValueReadLocalConfirm(ack, _savedAsapReadRequest, priority, hopType, status); + _bau.groupValueReadLocalConfirm(ack, _savedAsapReadRequest, priority, hopType, secCtrl, status); break; case GroupValueResponse: - _bau.groupValueReadResponseConfirm(ack, _savedAsapResponse, priority, hopType, apdu.data(), apdu.length() - 1, status); + _bau.groupValueReadResponseConfirm(ack, _savedAsapResponse, priority, hopType, secCtrl, apdu.data(), apdu.length() - 1, status); break; case GroupValueWrite: - _bau.groupValueWriteLocalConfirm(ack, _savedAsapWriteRequest, priority, hopType, apdu.data(), apdu.length() - 1, status); + _bau.groupValueWriteLocalConfirm(ack, _savedAsapWriteRequest, priority, hopType, secCtrl, apdu.data(), apdu.length() - 1, status); break; default: print("datagroup-confirm: unhandled APDU-Type: "); @@ -78,6 +90,11 @@ void ApplicationLayer::dataGroupConfirm(AckType ack, HopCountType hopType, Prior } void ApplicationLayer::dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) +{ + dataBroadcastIndication(hopType, priority, source, apdu, noSecurity); +} + +void ApplicationLayer::dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu, const SecurityControl& secCtrl) { uint8_t* data = apdu.data(); switch (apdu.type()) @@ -86,26 +103,26 @@ void ApplicationLayer::dataBroadcastIndication(HopCountType hopType, Priority pr { uint16_t newAddress; popWord(newAddress, data + 1); - _bau.individualAddressWriteIndication(hopType, newAddress); + _bau.individualAddressWriteIndication(hopType, secCtrl, newAddress); break; } case IndividualAddressRead: - _bau.individualAddressReadIndication(hopType); + _bau.individualAddressReadIndication(hopType, secCtrl); break; case IndividualAddressResponse: - _bau.individualAddressReadAppLayerConfirm(hopType, apdu.frame().sourceAddress()); + _bau.individualAddressReadAppLayerConfirm(hopType, secCtrl, apdu.frame().sourceAddress()); break; case IndividualAddressSerialNumberRead: { uint8_t* knxSerialNumber = &data[1]; - _bau.individualAddressSerialNumberReadIndication(priority, hopType, knxSerialNumber); + _bau.individualAddressSerialNumberReadIndication(priority, hopType, secCtrl, knxSerialNumber); break; } case IndividualAddressSerialNumberResponse: { uint16_t domainAddress; popWord(domainAddress, data + 7); - _bau.individualAddressSerialNumberReadAppLayerConfirm(hopType, data + 1, apdu.frame().sourceAddress(), + _bau.individualAddressSerialNumberReadAppLayerConfirm(hopType, secCtrl, data + 1, apdu.frame().sourceAddress(), domainAddress); break; } @@ -114,13 +131,13 @@ void ApplicationLayer::dataBroadcastIndication(HopCountType hopType, Priority pr uint8_t* knxSerialNumber = &data[1]; uint16_t newIndividualAddress; popWord(newIndividualAddress, &data[7]); - _bau.individualAddressSerialNumberWriteIndication(priority, hopType, newIndividualAddress, knxSerialNumber); + _bau.individualAddressSerialNumberWriteIndication(priority, hopType, secCtrl, newIndividualAddress, knxSerialNumber); break; } default: #if (MEDIUM_TYPE == 5)||(MEDIUM_TYPE == 0) // Make sure we also check if it is a service normally available only on SystemBroadcast on open media - dataSystemBroadcastIndication(hopType, priority, source, apdu); + dataSystemBroadcastIndication(hopType, priority, source, apdu, secCtrl); #else print("Broadcast-indication: unhandled APDU-Type: "); println(apdu.type()); @@ -131,6 +148,11 @@ void ApplicationLayer::dataBroadcastIndication(HopCountType hopType, Priority pr } void ApplicationLayer::dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, bool status) +{ + dataBroadcastConfirm(ack, hopType, priority, apdu, noSecurity, status); +} + +void ApplicationLayer::dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl& secCtrl, bool status) { uint8_t* data = apdu.data(); switch (apdu.type()) @@ -139,36 +161,36 @@ void ApplicationLayer::dataBroadcastConfirm(AckType ack, HopCountType hopType, P { uint16_t newAddress; popWord(newAddress, data + 1); - _bau.individualAddressWriteLocalConfirm(ack, hopType, newAddress, status); + _bau.individualAddressWriteLocalConfirm(ack, hopType, secCtrl, newAddress, status); break; } case IndividualAddressRead: - _bau.individualAddressReadLocalConfirm(ack, hopType, status); + _bau.individualAddressReadLocalConfirm(ack, hopType, secCtrl, status); break; case IndividualAddressResponse: - _bau.individualAddressReadResponseConfirm(ack, hopType, status); + _bau.individualAddressReadResponseConfirm(ack, hopType, secCtrl, status); break; case IndividualAddressSerialNumberRead: - _bau.individualAddressSerialNumberReadLocalConfirm(ack, hopType, data + 1, status); + _bau.individualAddressSerialNumberReadLocalConfirm(ack, hopType, secCtrl, data + 1, status); break; case IndividualAddressSerialNumberResponse: { uint16_t domainAddress; popWord(domainAddress, data + 7); - _bau.individualAddressSerialNumberReadResponseConfirm(ack, hopType, data + 1, domainAddress, status); + _bau.individualAddressSerialNumberReadResponseConfirm(ack, hopType, secCtrl, data + 1, domainAddress, status); break; } case IndividualAddressSerialNumberWrite: { uint16_t newAddress; popWord(newAddress, data + 7); - _bau.individualAddressSerialNumberWriteLocalConfirm(ack, hopType, data + 1, newAddress, status); + _bau.individualAddressSerialNumberWriteLocalConfirm(ack, hopType, secCtrl, data + 1, newAddress, status); break; } default: #if (MEDIUM_TYPE == 5)||(MEDIUM_TYPE == 0) // Make sure we also check if it is a service normally available only on SystemBroadcast on open media - dataSystemBroadcastConfirm(hopType, priority, apdu, status); + dataSystemBroadcastConfirm(hopType, priority, apdu, secCtrl, status); #else print("Broadcast-confirm: unhandled APDU-Type: "); println(apdu.type()); @@ -179,6 +201,11 @@ void ApplicationLayer::dataBroadcastConfirm(AckType ack, HopCountType hopType, P } void ApplicationLayer::dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) +{ + dataSystemBroadcastIndication(hopType, priority, source, apdu, noSecurity); +} + +void ApplicationLayer::dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu, const SecurityControl &secCtrl) { const uint8_t* data = apdu.data(); switch (apdu.type()) @@ -195,20 +222,20 @@ void ApplicationLayer::dataSystemBroadcastIndication(HopCountType hopType, Prior popByte(testInfo[1], data + 5); propertyId = (propertyId >> 4) & 0x0FFF;; testInfo[0] &= 0x0F; - _bau.systemNetworkParameterReadIndication(priority, hopType, objectType, propertyId, testInfo, sizeof(testInfo)); + _bau.systemNetworkParameterReadIndication(priority, hopType, secCtrl, objectType, propertyId, testInfo, sizeof(testInfo)); break; } case DomainAddressSerialNumberWrite: { const uint8_t* knxSerialNumber = &data[1]; const uint8_t* domainAddress = &data[7]; - _bau.domainAddressSerialNumberWriteIndication(priority, hopType, domainAddress, knxSerialNumber); + _bau.domainAddressSerialNumberWriteIndication(priority, hopType, secCtrl, domainAddress, knxSerialNumber); break; } case DomainAddressSerialNumberRead: { const uint8_t* knxSerialNumber = &data[1]; - _bau.domainAddressSerialNumberReadIndication(priority, hopType, knxSerialNumber); + _bau.domainAddressSerialNumberReadIndication(priority, hopType, secCtrl, knxSerialNumber); break; } default: @@ -222,7 +249,11 @@ void ApplicationLayer::dataSystemBroadcastIndication(HopCountType hopType, Prior } } -void ApplicationLayer::dataSystemBroadcastConfirm(HopCountType hopType, Priority priority, APDU& apdu, bool status) +void ApplicationLayer::dataSystemBroadcastConfirm(HopCountType hopType, Priority priority, APDU& apdu, bool status) { + dataSystemBroadcastConfirm(hopType, priority, apdu, noSecurity, status); +} + +void ApplicationLayer::dataSystemBroadcastConfirm(HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl& secCtrl, bool status) { const uint8_t* data = apdu.data(); switch (apdu.type()) @@ -239,20 +270,20 @@ void ApplicationLayer::dataSystemBroadcastConfirm(HopCountType hopType, Priority popByte(testInfo[1], data + 5); propertyId = (propertyId >> 4) & 0x0FFF;; testInfo[0] &= 0x0F; - _bau.systemNetworkParameterReadLocalConfirm(priority, hopType, objectType, propertyId, testInfo, sizeof(testInfo), status); + _bau.systemNetworkParameterReadLocalConfirm(priority, hopType, secCtrl, objectType, propertyId, testInfo, sizeof(testInfo), status); break; } case DomainAddressSerialNumberWrite: { const uint8_t* knxSerialNumber = &data[1]; const uint8_t* domainAddress = &data[7]; - _bau.domainAddressSerialNumberWriteLocalConfirm(priority, hopType, domainAddress, knxSerialNumber, status); + _bau.domainAddressSerialNumberWriteLocalConfirm(priority, hopType, secCtrl, domainAddress, knxSerialNumber, status); break; } case DomainAddressSerialNumberRead: { const uint8_t* knxSerialNumber = &data[1]; - _bau.domainAddressSerialNumberReadLocalConfirm(priority, hopType, knxSerialNumber, status); + _bau.domainAddressSerialNumberReadLocalConfirm(priority, hopType, secCtrl, knxSerialNumber, status); break; } default: @@ -266,14 +297,24 @@ void ApplicationLayer::dataSystemBroadcastConfirm(HopCountType hopType, Priority } } -void ApplicationLayer::dataIndividualIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu) +void ApplicationLayer::dataIndividualIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) { - individualIndication(hopType, priority, tsap, apdu); + dataIndividualIndication(hopType, priority, source, apdu, noSecurity); +} + +void ApplicationLayer::dataIndividualIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl& secCtrl) +{ + individualIndication(hopType, priority, tsap, apdu, secCtrl); } void ApplicationLayer::dataIndividualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status) { - individualConfirm(ack, hopType, priority, tsap, apdu, status); + dataIndividualConfirm(ack, hopType, priority, tsap, apdu, noSecurity, status); +} + +void ApplicationLayer::dataIndividualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl &secCtrl, bool status) +{ + individualConfirm(ack, hopType, priority, tsap, apdu, secCtrl, status); } void ApplicationLayer::connectIndication(uint16_t tsap) @@ -304,15 +345,25 @@ void ApplicationLayer::disconnectConfirm(Priority priority, uint16_t tsap, bool void ApplicationLayer::dataConnectedIndication(Priority priority, uint16_t tsap, APDU& apdu) { - individualIndication(NetworkLayerParameter, priority, tsap, apdu); + dataConnectedIndication(priority, tsap, apdu, noSecurity); +} + +void ApplicationLayer::dataConnectedIndication(Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl& secCtrl) +{ + individualIndication(NetworkLayerParameter, priority, tsap, apdu, secCtrl); } void ApplicationLayer::dataConnectedConfirm(uint16_t tsap) +{ + dataConnectedConfirm(tsap, noSecurity); +} + +void ApplicationLayer::dataConnectedConfirm(uint16_t tsap, const SecurityControl& secCtrl) { } #pragma endregion -void ApplicationLayer::groupValueReadRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType) +void ApplicationLayer::groupValueReadRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl) { _savedAsapReadRequest = asap; CemiFrame frame(1); @@ -326,59 +377,59 @@ void ApplicationLayer::groupValueReadRequest(AckType ack, uint16_t asap, Priorit uint16_t tsap = (uint16_t)value; // first to bus then to itself - _transportLayer->dataGroupRequest(ack, hopType, priority, tsap, apdu); - dataGroupIndication(hopType, priority, tsap, apdu); + dataGroupRequest(ack, hopType, priority, tsap, apdu, secCtrl); + dataGroupIndication(hopType, priority, tsap, apdu, secCtrl); } -void ApplicationLayer::groupValueReadResponse(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength) +void ApplicationLayer::groupValueReadResponse(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint8_t * data, uint8_t dataLength) { _savedAsapResponse = asap; - groupValueSend(GroupValueResponse, ack, asap, priority, hopType, data, dataLength); + groupValueSend(GroupValueResponse, ack, asap, priority, hopType, secCtrl, data, dataLength); } -void ApplicationLayer::groupValueWriteRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength) +void ApplicationLayer::groupValueWriteRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint8_t * data, uint8_t dataLength) { _savedAsapWriteRequest = asap; - groupValueSend(GroupValueWrite, ack, asap, priority, hopType, data, dataLength); + groupValueSend(GroupValueWrite, ack, asap, priority, hopType, secCtrl, data, dataLength); } -void ApplicationLayer::individualAddressWriteRequest(AckType ack, HopCountType hopType, uint16_t newaddress) +void ApplicationLayer::individualAddressWriteRequest(AckType ack, HopCountType hopType, const SecurityControl& secCtrl, uint16_t newaddress) { CemiFrame frame(3); APDU& apdu = frame.apdu(); apdu.type(IndividualAddressWrite); uint8_t* apduData = apdu.data(); pushWord(newaddress, apduData + 1); - _transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu); + dataBroadcastRequest(ack, hopType, SystemPriority, apdu, secCtrl); } -void ApplicationLayer::individualAddressReadRequest(AckType ack, HopCountType hopType) +void ApplicationLayer::individualAddressReadRequest(AckType ack, HopCountType hopType, const SecurityControl& secCtrl) { CemiFrame frame(1); APDU& apdu = frame.apdu(); apdu.type(IndividualAddressRead); - _transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu); + dataBroadcastRequest(ack, hopType, SystemPriority, apdu, secCtrl); } -void ApplicationLayer::individualAddressReadResponse(AckType ack, HopCountType hopType) +void ApplicationLayer::individualAddressReadResponse(AckType ack, HopCountType hopType, const SecurityControl &secCtrl) { CemiFrame frame(1); APDU& apdu = frame.apdu(); apdu.type(IndividualAddressResponse); - _transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu); + dataBroadcastRequest(ack, hopType, SystemPriority, apdu, secCtrl); } -void ApplicationLayer::individualAddressSerialNumberReadRequest(AckType ack, HopCountType hopType, uint8_t * serialNumber) +void ApplicationLayer::individualAddressSerialNumberReadRequest(AckType ack, HopCountType hopType, const SecurityControl &secCtrl, uint8_t * serialNumber) { CemiFrame frame(7); APDU& apdu = frame.apdu(); apdu.type(IndividualAddressSerialNumberRead); uint8_t* data = apdu.data() + 1; memcpy(data, serialNumber, 6); - _transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu); + dataBroadcastRequest(ack, hopType, SystemPriority, apdu, secCtrl); } -void ApplicationLayer::individualAddressSerialNumberReadResponse(AckType ack, HopCountType hopType, +void ApplicationLayer::individualAddressSerialNumberReadResponse(AckType ack, HopCountType hopType, const SecurityControl& secCtrl, uint8_t * serialNumber, uint16_t domainAddress) { CemiFrame frame(7); @@ -388,10 +439,10 @@ void ApplicationLayer::individualAddressSerialNumberReadResponse(AckType ack, Ho memcpy(data, serialNumber, 6); data += 6; pushWord(domainAddress, data); - _transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu); + dataBroadcastRequest(ack, hopType, SystemPriority, apdu, secCtrl); } -void ApplicationLayer::individualAddressSerialNumberWriteRequest(AckType ack, HopCountType hopType, uint8_t * serialNumber, +void ApplicationLayer::individualAddressSerialNumberWriteRequest(AckType ack, HopCountType hopType, const SecurityControl &secCtrl, uint8_t * serialNumber, uint16_t newaddress) { CemiFrame frame(13); @@ -401,10 +452,10 @@ void ApplicationLayer::individualAddressSerialNumberWriteRequest(AckType ack, Ho memcpy(data, serialNumber, 6); data += 6; pushWord(newaddress, data); - _transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu); + dataBroadcastRequest(ack, hopType, SystemPriority, apdu, secCtrl); } -void ApplicationLayer::deviceDescriptorReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, +void ApplicationLayer::deviceDescriptorReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t descriptorType) { CemiFrame frame(1); @@ -413,10 +464,10 @@ void ApplicationLayer::deviceDescriptorReadRequest(AckType ack, Priority priorit uint8_t* data = apdu.data(); *data |= (descriptorType & 0x3f); - individualSend(ack, hopType, priority, asap, apdu); + individualSend(ack, hopType, priority, asap, apdu, secCtrl); } -void ApplicationLayer::deviceDescriptorReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, +void ApplicationLayer::deviceDescriptorReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t descriptorType, uint8_t* deviceDescriptor) { uint8_t length = 0; @@ -442,7 +493,7 @@ void ApplicationLayer::deviceDescriptorReadResponse(AckType ack, Priority priori if (length > 1) memcpy(data + 1, deviceDescriptor, length - 1); - individualSend(ack, hopType, priority, asap, apdu); + individualSend(ack, hopType, priority, asap, apdu, secCtrl); } void ApplicationLayer::connectRequest(uint16_t destination, Priority priority) @@ -455,19 +506,33 @@ void ApplicationLayer::disconnectRequest(Priority priority) _transportLayer->disconnectRequest(_connectedTsap, priority); } -void ApplicationLayer::restartRequest(AckType ack, Priority priority, HopCountType hopType) +void ApplicationLayer::restartRequest(AckType ack, Priority priority, HopCountType hopType, const SecurityControl& secCtrl) { CemiFrame frame(1); APDU& apdu = frame.apdu(); apdu.type(Restart); - individualSend(ack, hopType, priority, _connectedTsap, apdu); + individualSend(ack, hopType, priority, _connectedTsap, apdu, secCtrl); +} + +void ApplicationLayer::restartResponse(AckType ack, Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint8_t errorCode, uint16_t processTime) +{ + CemiFrame frame(4); + APDU& apdu = frame.apdu(); + apdu.type(Restart); + uint8_t* data = apdu.data(); + data[0] |= (1 << 5) | 1; // Set response bit and a restart type of "master reset". Only the master reset sends a response. + data[1] = errorCode; + data[2] = processTime >> 8; + data[3] = processTime & 0xFF; + + individualSend(ack, hopType, priority, _connectedTsap, apdu, secCtrl); } //TODO: ApplicationLayer::systemNetworkParameterReadRequest() -void ApplicationLayer::systemNetworkParameterReadResponse(Priority priority, HopCountType hopType, +void ApplicationLayer::systemNetworkParameterReadResponse(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint16_t objectType, uint16_t propertyId, - uint8_t* testInfo, uint16_t testInfoLength, + uint8_t* testInfo, uint16_t testInfoLength, uint8_t* testResult, uint16_t testResultLength) { CemiFrame frame(testInfoLength + testResultLength + 3 + 1); // PID and testInfo share an octet (+3) and +1 for APCI byte(?) @@ -482,12 +547,12 @@ void ApplicationLayer::systemNetworkParameterReadResponse(Priority priority, Hop //apdu.printPDU(); - _transportLayer->dataSystemBroadcastRequest(AckDontCare, hopType, SystemPriority, apdu); + dataSystemBroadcastRequest(AckDontCare, hopType, SystemPriority, apdu, secCtrl); } //TODO: ApplicationLayer::domainAddressSerialNumberWriteRequest() //TODO: ApplicationLayer::domainAddressSerialNumberReadRequest() -void ApplicationLayer::domainAddressSerialNumberReadResponse(Priority priority, HopCountType hopType, const uint8_t* rfDoA, +void ApplicationLayer::domainAddressSerialNumberReadResponse(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, const uint8_t* rfDoA, const uint8_t* knxSerialNumber) { CemiFrame frame(13); @@ -501,12 +566,12 @@ void ApplicationLayer::domainAddressSerialNumberReadResponse(Priority priority, //apdu.printPDU(); - _transportLayer->dataSystemBroadcastRequest(AckDontCare, hopType, SystemPriority, apdu); + dataSystemBroadcastRequest(AckDontCare, hopType, SystemPriority, apdu, secCtrl); } //TODO: ApplicationLayer::IndividualAddressSerialNumberWriteRequest() //TODO: ApplicationLayer::IndividualAddressSerialNumberReadRequest() -void ApplicationLayer::IndividualAddressSerialNumberReadResponse(Priority priority, HopCountType hopType, const uint8_t* rfDoA, +void ApplicationLayer::IndividualAddressSerialNumberReadResponse(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, const uint8_t* rfDoA, const uint8_t* knxSerialNumber) { CemiFrame frame(13); @@ -520,10 +585,10 @@ void ApplicationLayer::IndividualAddressSerialNumberReadResponse(Priority priori //apdu.printPDU(); - _transportLayer->dataBroadcastRequest(AckDontCare, hopType, SystemPriority, apdu); + dataBroadcastRequest(AckDontCare, hopType, SystemPriority, apdu, secCtrl); } -void ApplicationLayer::propertyValueReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, +void ApplicationLayer::propertyValueReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex) { CemiFrame frame(5); @@ -536,24 +601,39 @@ void ApplicationLayer::propertyValueReadRequest(AckType ack, Priority priority, pushWord(startIndex & 0xfff, data); *data &= ((numberOfElements & 0xf) << 4); - individualSend(ack, hopType, priority, asap, apdu); + individualSend(ack, hopType, priority, asap, apdu, secCtrl); } -void ApplicationLayer::propertyValueReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, +void ApplicationLayer::propertyValueReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length) { - propertyDataSend(PropertyValueResponse, ack, priority, hopType, asap, objectIndex, propertyId, numberOfElements, + propertyDataSend(PropertyValueResponse, ack, priority, hopType, asap, secCtrl, objectIndex, propertyId, numberOfElements, startIndex, data, length); } -void ApplicationLayer::propertyValueWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, +void ApplicationLayer::propertyValueExtReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, + uint16_t objectType, uint8_t objectInstance, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length) +{ + propertyExtDataSend(PropertyValueExtResponse, ack, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, numberOfElements, + startIndex, data, length); +} + +void ApplicationLayer::propertyValueExtWriteConResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, + uint16_t objectType, uint8_t objectInstance, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t returnCode) +{ + uint8_t noOfElem = (returnCode != ReturnCodes::Success) ? 0 : numberOfElements; + propertyExtDataSend(PropertyValueExtWriteConResponse, ack, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, noOfElem, + startIndex, &returnCode, 1); +} + +void ApplicationLayer::propertyValueWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t * data, uint8_t length) { - propertyDataSend(PropertyValueWrite, ack, priority, hopType, asap, objectIndex, propertyId, numberOfElements, + propertyDataSend(PropertyValueWrite, ack, priority, hopType, asap, secCtrl, objectIndex, propertyId, numberOfElements, startIndex, data, length); } -void ApplicationLayer::functionPropertyStateResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, +void ApplicationLayer::functionPropertyStateResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t* resultData, uint8_t resultLength) { CemiFrame frame(3 + resultLength + 1); @@ -567,12 +647,35 @@ void ApplicationLayer::functionPropertyStateResponse(AckType ack, Priority prior memcpy(&data[2], resultData, resultLength); if (asap == _connectedTsap) - _transportLayer->dataConnectedRequest(asap, priority, apdu); + dataConnectedRequest(asap, priority, apdu, secCtrl); else - _transportLayer->dataIndividualRequest(ack, hopType, priority, asap, apdu); + dataIndividualRequest(ack, hopType, priority, asap, apdu, secCtrl); } -void ApplicationLayer::propertyDescriptionReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, +void ApplicationLayer::functionPropertyExtStateResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, + uint16_t objectType, uint8_t objectInstance, uint16_t propertyId, uint8_t* resultData, uint8_t resultLength) +{ + CemiFrame frame(5 + resultLength + 1); + APDU& apdu = frame.apdu(); + apdu.type(FunctionPropertyExtStateResponse); + uint8_t* data = apdu.data() + 1; + + data[0] = ((uint16_t)objectType) >> 8; + data[1] = ((uint16_t)objectType) & 0xFF; + data[2] = objectInstance >> 4; + data[3] = ((objectInstance&0x0F) << 4) | (propertyId >> 8); + data[4] = (propertyId & 0xFF); + // data[5] must contain the return code + if (resultLength > 0) + memcpy(&data[5], resultData, resultLength); + + if (asap == _connectedTsap) + dataConnectedRequest(asap, priority, apdu, secCtrl); + else + dataIndividualRequest(ack, hopType, priority, asap, apdu, secCtrl); +} + +void ApplicationLayer::propertyDescriptionReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex) { CemiFrame frame(4); @@ -582,10 +685,10 @@ void ApplicationLayer::propertyDescriptionReadRequest(AckType ack, Priority prio data[1] = objectIndex; data[2] = propertyId; data[3] = propertyIndex; - individualSend(ack, hopType, priority, asap, apdu); + individualSend(ack, hopType, priority, asap, apdu, secCtrl); } -void ApplicationLayer::propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, +void ApplicationLayer::propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type, uint16_t maxNumberOfElements, uint8_t access) { @@ -601,10 +704,10 @@ void ApplicationLayer::propertyDescriptionReadResponse(AckType ack, Priority pri data[4] |= (type & 0x3f); pushWord(maxNumberOfElements & 0xfff, data + 5); data[7] = access; - individualSend(ack, hopType, priority, asap, apdu); + individualSend(ack, hopType, priority, asap, apdu, secCtrl); } -void ApplicationLayer::memoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, +void ApplicationLayer::memoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint16_t memoryAddress) { CemiFrame frame(3); @@ -613,22 +716,82 @@ void ApplicationLayer::memoryReadRequest(AckType ack, Priority priority, HopCoun uint8_t* data = apdu.data(); *data |= (number & 0x3f); pushWord(memoryAddress, data + 1); - individualSend(ack, hopType, priority, asap, apdu); + individualSend(ack, hopType, priority, asap, apdu, secCtrl); } -void ApplicationLayer::memoryReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, +void ApplicationLayer::memoryReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t * memoryData) { - memorySend(MemoryResponse, ack, priority, hopType, asap, number, memoryAddress, memoryData); + memorySend(MemoryResponse, ack, priority, hopType, asap, secCtrl, number, memoryAddress, memoryData); } -void ApplicationLayer::memoryWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, +void ApplicationLayer::memoryExtReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ReturnCodes code, + uint8_t number, uint32_t memoryAddress, uint8_t * memoryData) +{ + CemiFrame frame(5 + number); + APDU& apdu = frame.apdu(); + apdu.type(MemoryExtReadResponse); + uint8_t* data = apdu.data(); + data[1] = code; + data[2] = (memoryAddress >> 16); + data[3] = (memoryAddress >> 8); + data[4] = (memoryAddress & 0xFF); + + memcpy(&data[5], memoryData, number); + + individualSend(ack, hopType, priority, asap, apdu, secCtrl); +} + +uint16_t ApplicationLayer::crc16Ccitt(uint8_t* input, uint16_t length) +{ + uint32_t polynom = 0x1021; + uint8_t padded[length+2]; + + memcpy(padded, input, length); + memset(padded+length, 0x00, 2); + + uint32_t result = 0xffff; + for (uint32_t i = 0; i < 8 * (uint32_t)sizeof(padded); i++) { + result <<= 1; + uint32_t nextBit = (padded[i / 8] >> (7 - (i % 8))) & 0x1; + result |= nextBit; + if ((result & 0x10000) != 0) + result ^= polynom; + } + return result & 0xffff; +} + +void ApplicationLayer::memoryExtWriteResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ReturnCodes code, + uint8_t number, uint32_t memoryAddress, uint8_t * memoryData) +{ + bool withCrc = code == ReturnCodes::SuccessWithCrc; + + CemiFrame frame(5 + (withCrc ? 2 : 0)); + APDU& apdu = frame.apdu(); + apdu.type(MemoryExtWriteResponse); + uint8_t* data = apdu.data(); + data[1] = code; + data[2] = (memoryAddress >> 16); + data[3] = (memoryAddress >> 8); + data[4] = (memoryAddress & 0xFF); + + if (withCrc) + { + uint16_t crc = crc16Ccitt(memoryData, number); // TODO + data[5] = crc >> 8; + data[6] = crc & 0xFF; + } + + individualSend(ack, hopType, priority, asap, apdu, secCtrl); +} + +void ApplicationLayer::memoryWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t * data) { - memorySend(MemoryWrite, ack, priority, hopType, asap, number, memoryAddress, data); + memorySend(MemoryWrite, ack, priority, hopType, asap, secCtrl, number, memoryAddress, data); } -void ApplicationLayer::userMemoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, +void ApplicationLayer::userMemoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress) { CemiFrame frame(4); @@ -638,30 +801,30 @@ void ApplicationLayer::userMemoryReadRequest(AckType ack, Priority priority, Hop data[1] |= (number & 0xf); data[1] |= ((memoryAddress >> 12) & 0xf0); pushWord(memoryAddress & 0xff, data + 2); - individualSend(ack, hopType, priority, asap, apdu); + individualSend(ack, hopType, priority, asap, apdu, secCtrl); } -void ApplicationLayer::userMemoryReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, +void ApplicationLayer::userMemoryReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t * memoryData) { - userMemorySend(UserMemoryResponse, ack, priority, hopType, asap, number, memoryAddress, memoryData); + userMemorySend(UserMemoryResponse, ack, priority, hopType, asap, secCtrl, number, memoryAddress, memoryData); } -void ApplicationLayer::userMemoryWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, +void ApplicationLayer::userMemoryWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t * memoryData) { - userMemorySend(UserMemoryWrite, ack, priority, hopType, asap, number, memoryAddress, memoryData); + userMemorySend(UserMemoryWrite, ack, priority, hopType, asap, secCtrl, number, memoryAddress, memoryData); } -void ApplicationLayer::userManufacturerInfoReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap) +void ApplicationLayer::userManufacturerInfoReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl) { CemiFrame frame(1); APDU& apdu = frame.apdu(); apdu.type(UserManufacturerInfoRead); - individualSend(ack, hopType, priority, asap, apdu); + individualSend(ack, hopType, priority, asap, apdu, secCtrl); } -void ApplicationLayer::userManufacturerInfoReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, +void ApplicationLayer::userManufacturerInfoReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t* info) { CemiFrame frame(4); @@ -669,30 +832,30 @@ void ApplicationLayer::userManufacturerInfoReadResponse(AckType ack, Priority pr apdu.type(UserMemoryRead); uint8_t* data = apdu.data(); memcpy(data + 1, info, 3); - individualSend(ack, hopType, priority, asap, apdu); + individualSend(ack, hopType, priority, asap, apdu, secCtrl); } -void ApplicationLayer::authorizeRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint32_t key) +void ApplicationLayer::authorizeRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint32_t key) { CemiFrame frame(6); APDU& apdu = frame.apdu(); apdu.type(AuthorizeRequest); uint8_t* data = apdu.data(); pushInt(key, data + 2); - individualSend(ack, hopType, priority, asap, apdu); + individualSend(ack, hopType, priority, asap, apdu, secCtrl); } -void ApplicationLayer::authorizeResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level) +void ApplicationLayer::authorizeResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t level) { CemiFrame frame(2); APDU& apdu = frame.apdu(); apdu.type(AuthorizeResponse); uint8_t* data = apdu.data(); data[1] = level; - individualSend(ack, hopType, priority, asap, apdu); + individualSend(ack, hopType, priority, asap, apdu, secCtrl); } -void ApplicationLayer::keyWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, uint32_t key) +void ApplicationLayer::keyWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t level, uint32_t key) { CemiFrame frame(6); APDU& apdu = frame.apdu(); @@ -700,20 +863,20 @@ void ApplicationLayer::keyWriteRequest(AckType ack, Priority priority, HopCountT uint8_t* data = apdu.data(); data[1] = level; pushInt(key, data + 2); - individualSend(ack, hopType, priority, asap, apdu); + individualSend(ack, hopType, priority, asap, apdu, secCtrl); } -void ApplicationLayer::keyWriteResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level) +void ApplicationLayer::keyWriteResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t level) { CemiFrame frame(6); APDU& apdu = frame.apdu(); apdu.type(KeyResponse); uint8_t* data = apdu.data(); data[1] = level; - individualSend(ack, hopType, priority, asap, apdu); + individualSend(ack, hopType, priority, asap, apdu, secCtrl); } -void ApplicationLayer::propertyDataSend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, +void ApplicationLayer::propertyDataSend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length) { CemiFrame frame(5 + length); @@ -730,12 +893,39 @@ void ApplicationLayer::propertyDataSend(ApduType type, AckType ack, Priority pri memcpy(apduData, data, length); if (asap == _connectedTsap) - _transportLayer->dataConnectedRequest(asap, priority, apdu); + dataConnectedRequest(asap, priority, apdu, secCtrl); else - _transportLayer->dataIndividualRequest(ack, hopType, priority, asap, apdu); + dataIndividualRequest(ack, hopType, priority, asap, apdu, secCtrl); } -void ApplicationLayer::groupValueSend(ApduType type, AckType ack, uint16_t asap, Priority priority, HopCountType hopType, +void ApplicationLayer::propertyExtDataSend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, + uint16_t objectType, uint8_t objectInstance, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length) +{ + CemiFrame frame(9 + length); + APDU& apdu = frame.apdu(); + apdu.type(type); + uint8_t* apduData = apdu.data(); + apduData += 1; + + apduData[0] = ((uint16_t)objectType) >> 8; + apduData[1] = ((uint16_t)objectType) & 0xFF; + apduData[2] = objectInstance >> 4; + apduData[3] = ((objectInstance&0x0F) << 4) | (propertyId >> 8); + apduData[4] = (propertyId & 0xFF); + apduData[5] = numberOfElements; + apduData[6] = (startIndex & 0x0FFF)>> 8; + apduData[7] = startIndex & 0xFF; + + if (length > 0) + memcpy(apduData+8, data, length); + + if (asap == _connectedTsap) + dataConnectedRequest(asap, priority, apdu, secCtrl); + else + dataIndividualRequest(ack, hopType, priority, asap, apdu, secCtrl); +} + +void ApplicationLayer::groupValueSend(ApduType type, AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* data, uint8_t& dataLength) { CemiFrame frame(dataLength + 1); @@ -754,11 +944,11 @@ void ApplicationLayer::groupValueSend(ApduType type, AckType ack, uint16_t asap, } // no need to check if there is a tsap. This is a response, so the read got trough uint16_t tsap = (uint16_t)_assocTable.translateAsap(asap); - _transportLayer->dataGroupRequest(ack, hopType, priority, tsap, apdu); - dataGroupIndication(hopType, priority, tsap, apdu); + dataGroupRequest(ack, hopType, priority, tsap, apdu, secCtrl); + dataGroupIndication(hopType, priority, tsap, apdu, secCtrl); } -void ApplicationLayer::memorySend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, +void ApplicationLayer::memorySend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t * memoryData) { CemiFrame frame(3 + number); @@ -770,10 +960,10 @@ void ApplicationLayer::memorySend(ApduType type, AckType ack, Priority priority, if (number > 0) memcpy(data + 3, memoryData, number); - individualSend(ack, hopType, priority, asap, apdu); + individualSend(ack, hopType, priority, asap, apdu, secCtrl); } -void ApplicationLayer::userMemorySend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, +void ApplicationLayer::userMemorySend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t * memoryData) { CemiFrame frame(4 + number); @@ -785,30 +975,46 @@ void ApplicationLayer::userMemorySend(ApduType type, AckType ack, Priority prior pushWord(memoryAddress & 0xffff, data + 2); if (number > 0) memcpy(data + 4, memoryData, number); - individualSend(ack, hopType, priority, asap, apdu); + individualSend(ack, hopType, priority, asap, apdu, secCtrl); } -void ApplicationLayer::individualIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU & apdu) +void ApplicationLayer::individualIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU & apdu, const SecurityControl& secCtrl) { uint8_t* data = apdu.data(); switch (apdu.type()) { case DeviceDescriptorRead: - _bau.deviceDescriptorReadIndication(priority, hopType, tsap, *data & 0x3f); + _bau.deviceDescriptorReadIndication(priority, hopType, tsap, secCtrl, *data & 0x3f); break; case DeviceDescriptorResponse: - _bau.deviceDescriptorReadAppLayerConfirm(priority, hopType, tsap, *data & 0x3f, data + 1); + _bau.deviceDescriptorReadAppLayerConfirm(priority, hopType, tsap, secCtrl, *data & 0x3f, data + 1); break; case Restart: - if ((*data & 0x3f) == 0) - _bau.restartRequestIndication(priority, hopType, tsap); + case RestartMasterReset: + { + // These reserved bits must be 0 + uint8_t reservedBits = data[0] & 0x1e; + if (reservedBits != 0) + return; + + // handle erase code for factory reset (setting FDSK again as toolkey, etc.) + RestartType restartType = (RestartType) (data[0] & 0x3f); + EraseCode eraseCode = EraseCode::Void; + uint8_t channel = 0; + if (restartType == RestartType::MasterReset) + { + eraseCode = (EraseCode) data[1]; + channel = data[2]; + } + _bau.restartRequestIndication(priority, hopType, tsap, secCtrl, restartType, eraseCode, channel); break; + } case PropertyValueRead: { uint16_t startIndex; popWord(startIndex, data + 3); startIndex &= 0xfff; - _bau.propertyValueReadIndication(priority, hopType, tsap, data[1], data[2], data[3] >> 4, startIndex); + _bau.propertyValueReadIndication(priority, hopType, tsap, secCtrl, data[1], data[2], data[3] >> 4, startIndex); break; } case PropertyValueResponse: @@ -816,7 +1022,7 @@ void ApplicationLayer::individualIndication(HopCountType hopType, Priority prior uint16_t startIndex; popWord(startIndex, data + 3); startIndex &= 0xfff; - _bau.propertyValueReadAppLayerConfirm(priority, hopType, tsap, data[1], data[2], data[3] >> 4, + _bau.propertyValueReadAppLayerConfirm(priority, hopType, tsap, secCtrl, data[1], data[2], data[3] >> 4, startIndex, data + 5, apdu.length() - 5); break; } @@ -825,67 +1031,128 @@ void ApplicationLayer::individualIndication(HopCountType hopType, Priority prior uint16_t startIndex; popWord(startIndex, data + 3); startIndex &= 0xfff; - _bau.propertyValueWriteIndication(priority, hopType, tsap, data[1], data[2], data[3] >> 4, + _bau.propertyValueWriteIndication(priority, hopType, tsap, secCtrl, data[1], data[2], data[3] >> 4, startIndex, data + 5, apdu.length() - 5); break; } + case PropertyValueExtRead: + { + ObjectType objectType = (ObjectType)(((data[1] & 0xff) << 8) | (data[2] & 0xff)); + uint8_t objectInstance = ((data[3] & 0xff) << 4) | ((data[4] & 0xff) >> 4); + uint16_t propertyId = ((data[4] & 0xf) << 8) | (data[5] & 0xff); + uint8_t numberOfElements = data[6]; + uint16_t startIndex = ((data[7] & 0xf) << 8) | (data[8] & 0xff); + _bau.propertyValueExtReadIndication(priority, hopType, tsap, secCtrl, objectType, objectInstance, propertyId, numberOfElements, startIndex); + break; + } + case PropertyValueExtWriteCon: + case PropertyValueExtWriteUnCon: + { + ObjectType objectType = (ObjectType)(((data[1] & 0xff) << 8) | (data[2] & 0xff)); + uint8_t objectInstance = ((data[3] & 0xff) << 4) | ((data[4] & 0xff) >> 4); + uint16_t propertyId = ((data[4] & 0xf) << 8) | (data[5] & 0xff); + uint8_t numberOfElements = data[6]; + uint16_t startIndex = ((data[7] & 0xf) << 8) | (data[8] & 0xff); + bool confirmed = (apdu.type() == PropertyValueExtWriteCon); + _bau.propertyValueExtWriteIndication(priority, hopType, tsap, secCtrl, objectType, objectInstance, propertyId, numberOfElements, startIndex, + data + 9, apdu.length() - 9, confirmed); + break; + } case FunctionPropertyCommand: - _bau.functionPropertyCommandIndication(priority, hopType, tsap, data[1], data[2], &data[3], apdu.length() - 4); //TODO: check length + _bau.functionPropertyCommandIndication(priority, hopType, tsap, secCtrl, data[1], data[2], &data[3], apdu.length() - 4); //TODO: check length break; case FunctionPropertyState: - _bau.functionPropertyStateIndication(priority, hopType, tsap, data[1], data[2], &data[3], apdu.length() - 4); //TODO: check length + _bau.functionPropertyStateIndication(priority, hopType, tsap, secCtrl, data[1], data[2], &data[3], apdu.length() - 4); //TODO: check length break; + case FunctionPropertyExtCommand: + { + ObjectType objectType = (ObjectType)(((data[1] & 0xff) << 8) | (data[2] & 0xff)); + uint8_t objectInstance = ((data[3] & 0xff) << 4) | ((data[4] & 0xff) >> 4); + uint16_t propertyId = ((data[4] & 0xf) << 8) | (data[5] & 0xff); + uint8_t* functionInput = &data[6]; + _bau.functionPropertyExtCommandIndication(priority, hopType, tsap, secCtrl, objectType, objectInstance, propertyId, functionInput, apdu.length() - 6); + break; + } + case FunctionPropertyExtState: + { + ObjectType objectType = (ObjectType)(((data[1] & 0xff) << 8) | (data[2] & 0xff)); + uint8_t objectInstance = ((data[3] & 0xff) << 4) | ((data[4] & 0xff) >> 4); + uint16_t propertyId = ((data[4] & 0xf) << 8) | (data[5] & 0xff); + uint8_t* functionInput = &data[6]; + _bau.functionPropertyExtStateIndication(priority, hopType, tsap, secCtrl, objectType, objectInstance, propertyId, functionInput, apdu.length() - 6); + break; + } case PropertyDescriptionRead: - _bau.propertyDescriptionReadIndication(priority, hopType, tsap, data[1], data[2], data[3]); + _bau.propertyDescriptionReadIndication(priority, hopType, tsap, secCtrl, data[1], data[2], data[3]); break; case PropertyDescriptionResponse: - _bau.propertyDescriptionReadAppLayerConfirm(priority, hopType, tsap, data[1], data[2], data[3], + _bau.propertyDescriptionReadAppLayerConfirm(priority, hopType, tsap, secCtrl, data[1], data[2], data[3], (data[4] & 0x80) > 0, data[4] & 0x3f, getWord(data + 5) & 0xfff, data[7]); break; case MemoryRead: - _bau.memoryReadIndication(priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1)); + _bau.memoryReadIndication(priority, hopType, tsap, secCtrl, data[0] & 0x3f, getWord(data + 1)); break; case MemoryResponse: - _bau.memoryReadAppLayerConfirm(priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1), data + 3); + _bau.memoryReadAppLayerConfirm(priority, hopType, tsap, secCtrl, data[0] & 0x3f, getWord(data + 1), data + 3); break; case MemoryWrite: - _bau.memoryWriteIndication(priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1), data + 3); + _bau.memoryWriteIndication(priority, hopType, tsap, secCtrl, data[0] & 0x3f, getWord(data + 1), data + 3); break; + case MemoryExtRead: + { + uint8_t number = data[1]; + uint32_t memoryAddress = ((data[2] & 0xff) << 16) | ((data[3] & 0xff) << 8) | (data[4] & 0xff); + _bau.memoryExtReadIndication(priority, hopType, tsap, secCtrl, number, memoryAddress); + break; + } + //case MemoryExtReadResponse: + // _bau.memoryExtReadAppLayerConfirm(priority, hopType, tsap, secCtrl, data[0], getInt(data + 1), data + 4); // TODO return code + // break; + case MemoryExtWrite: + { + uint8_t number = data[1]; + uint32_t memoryAddress = ((data[2] & 0xff) << 16) | ((data[3] & 0xff) << 8) | (data[4] & 0xff); + _bau.memoryExtWriteIndication(priority, hopType, tsap, secCtrl, number, memoryAddress, data + 5); + break; + } + //case MemoryExtWriteResponse: + // _bau.memoryExtWriteAppLayerConfirm(priority, hopType, tsap, secCtrl, data[0] & 0x3f, getWord(data + 1), data + 3); // TODO return code + // break; case UserMemoryRead: { uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3]; - _bau.userMemoryReadIndication(priority, hopType, tsap, data[1] & 0xf, address); + _bau.userMemoryReadIndication(priority, hopType, tsap, secCtrl, data[1] & 0xf, address); break; } case UserMemoryResponse: { uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3]; - _bau.userMemoryReadAppLayerConfirm(priority, hopType, tsap, data[1] & 0xf, address, data + 4); + _bau.userMemoryReadAppLayerConfirm(priority, hopType, tsap, secCtrl, data[1] & 0xf, address, data + 4); break; } case UserMemoryWrite: { uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3]; - _bau.userMemoryWriteIndication(priority, hopType, tsap, data[1] & 0xf, address, data + 4); + _bau.userMemoryWriteIndication(priority, hopType, tsap, secCtrl, data[1] & 0xf, address, data + 4); break; } case UserManufacturerInfoRead: - _bau.userManufacturerInfoIndication(priority, hopType, tsap); + _bau.userManufacturerInfoIndication(priority, hopType, tsap, secCtrl); break; case UserManufacturerInfoResponse: - _bau.userManufacturerInfoAppLayerConfirm(priority, hopType, tsap, data + 1); + _bau.userManufacturerInfoAppLayerConfirm(priority, hopType, tsap, secCtrl, data + 1); break; case AuthorizeRequest: - _bau.authorizeIndication(priority, hopType, tsap, getInt(data + 2)); + _bau.authorizeIndication(priority, hopType, tsap, secCtrl, getInt(data + 2)); break; case AuthorizeResponse: - _bau.authorizeAppLayerConfirm(priority, hopType, tsap, data[1]); + _bau.authorizeAppLayerConfirm(priority, hopType, tsap, secCtrl, data[1]); break; case KeyWrite: - _bau.keyWriteIndication(priority, hopType, tsap, data[1], getInt(data + 2)); + _bau.keyWriteIndication(priority, hopType, tsap, secCtrl, data[1], getInt(data + 2)); break; case KeyResponse: - _bau.keyWriteAppLayerConfirm(priority, hopType, tsap, data[1]); + _bau.keyWriteAppLayerConfirm(priority, hopType, tsap, secCtrl, data[1]); break; default: print("Indiviual-indication: unhandled APDU-Type: "); @@ -893,26 +1160,26 @@ void ApplicationLayer::individualIndication(HopCountType hopType, Priority prior } } -void ApplicationLayer::individualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU & apdu, bool status) +void ApplicationLayer::individualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU & apdu, const SecurityControl &secCtrl, bool status) { uint8_t* data = apdu.data(); switch (apdu.type()) { case DeviceDescriptorRead: - _bau.deviceDescriptorReadLocalConfirm(ack, priority, hopType, tsap, *data & 0x3f, status); + _bau.deviceDescriptorReadLocalConfirm(ack, priority, hopType, tsap, secCtrl, *data & 0x3f, status); break; case DeviceDescriptorResponse: - _bau.deviceDescriptorReadResponseConfirm(ack, priority, hopType, tsap, *data & 0x3f, data + 1, status); + _bau.deviceDescriptorReadResponseConfirm(ack, priority, hopType, tsap, secCtrl, *data & 0x3f, data + 1, status); break; case Restart: - _bau.restartRequestLocalConfirm(ack, priority, hopType, tsap, status); + _bau.restartRequestLocalConfirm(ack, priority, hopType, tsap, secCtrl, status); break; case PropertyValueRead: { uint16_t startIndex; popWord(startIndex, data + 3); startIndex &= 0xfff; - _bau.propertyValueReadLocalConfirm(ack, priority, hopType, tsap, data[1], data[2], data[3] >> 4, + _bau.propertyValueReadLocalConfirm(ack, priority, hopType, tsap, secCtrl, data[1], data[2], data[3] >> 4, startIndex, status); break; } @@ -921,7 +1188,7 @@ void ApplicationLayer::individualConfirm(AckType ack, HopCountType hopType, Prio uint16_t startIndex; popWord(startIndex, data + 3); startIndex &= 0xfff; - _bau.propertyValueReadResponseConfirm(ack, priority, hopType, tsap, data[1], data[2], data[3] >> 4, + _bau.propertyValueReadResponseConfirm(ack, priority, hopType, tsap, secCtrl, data[1], data[2], data[3] >> 4, startIndex, data + 5, apdu.length() - 5, status); break; } @@ -930,61 +1197,73 @@ void ApplicationLayer::individualConfirm(AckType ack, HopCountType hopType, Prio uint16_t startIndex; popWord(startIndex, data + 3); startIndex &= 0xfff; - _bau.propertyValueWriteLocalConfirm(ack, priority, hopType, tsap, data[1], data[2], data[3] >> 4, + _bau.propertyValueWriteLocalConfirm(ack, priority, hopType, tsap, secCtrl, data[1], data[2], data[3] >> 4, startIndex, data + 5, apdu.length() - 5, status); break; } case PropertyDescriptionRead: - _bau.propertyDescriptionReadLocalConfirm(ack, priority, hopType, tsap, data[1], data[2], data[3], status); + _bau.propertyDescriptionReadLocalConfirm(ack, priority, hopType, tsap, secCtrl, data[1], data[2], data[3], status); break; case PropertyDescriptionResponse: - _bau.propertyDescriptionReadResponseConfirm(ack, priority, hopType, tsap, data[1], data[2], data[3], + _bau.propertyDescriptionReadResponseConfirm(ack, priority, hopType, tsap, secCtrl, data[1], data[2], data[3], (data[4] & 0x80) > 0, data[4] & 0x3f, getWord(data + 5) & 0xfff, data[7], status); break; case MemoryRead: - _bau.memoryReadLocalConfirm(ack, priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1), status); + _bau.memoryReadLocalConfirm(ack, priority, hopType, tsap, secCtrl, data[0] & 0x3f, getWord(data + 1), status); break; case MemoryResponse: - _bau.memoryReadResponseConfirm(ack, priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1), data + 3, status); + _bau.memoryReadResponseConfirm(ack, priority, hopType, tsap, secCtrl, data[0] & 0x3f, getWord(data + 1), data + 3, status); break; case MemoryWrite: - _bau.memoryWriteLocalConfirm(ack, priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1), data + 3, status); + _bau.memoryWriteLocalConfirm(ack, priority, hopType, tsap, secCtrl, data[0] & 0x3f, getWord(data + 1), data + 3, status); + break; + case MemoryExtRead: + _bau.memoryExtReadLocalConfirm(ack, priority, hopType, tsap, secCtrl, data[0] & 0x3f, getWord(data + 1), status); + break; + case MemoryExtReadResponse: + _bau.memoryExtReadResponseConfirm(ack, priority, hopType, tsap, secCtrl, data[0] & 0x3f, getWord(data + 1), data + 3, status); + break; + case MemoryExtWrite: + _bau.memoryExtWriteLocalConfirm(ack, priority, hopType, tsap, secCtrl, data[0] & 0x3f, getWord(data + 1), data + 3, status); + break; + case MemoryExtWriteResponse: + _bau.memoryExtWriteResponseConfirm(ack, priority, hopType, tsap, secCtrl, data[0] & 0x3f, getWord(data + 1), data + 3, status); break; case UserMemoryRead: { uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3]; - _bau.memoryReadLocalConfirm(ack, priority, hopType, tsap, data[1] & 0xf, address, status); + _bau.memoryReadLocalConfirm(ack, priority, hopType, tsap, secCtrl, data[1] & 0xf, address, status); break; } case UserMemoryResponse: { uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3]; - _bau.memoryReadResponseConfirm(ack, priority, hopType, tsap, data[1] & 0xf, address, data + 4, status); + _bau.memoryReadResponseConfirm(ack, priority, hopType, tsap, secCtrl, data[1] & 0xf, address, data + 4, status); break; } case UserMemoryWrite: { uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3]; - _bau.memoryWriteLocalConfirm(ack, priority, hopType, tsap, data[1] & 0xf, address, data + 4, status); + _bau.memoryWriteLocalConfirm(ack, priority, hopType, tsap, secCtrl, data[1] & 0xf, address, data + 4, status); break; } case UserManufacturerInfoRead: - _bau.userManufacturerInfoLocalConfirm(ack, priority, hopType, tsap, status); + _bau.userManufacturerInfoLocalConfirm(ack, priority, hopType, tsap, secCtrl, status); break; case UserManufacturerInfoResponse: - _bau.userManufacturerInfoResponseConfirm(ack, priority, hopType, tsap, data + 1, status); + _bau.userManufacturerInfoResponseConfirm(ack, priority, hopType, tsap, secCtrl, data + 1, status); break; case AuthorizeRequest: - _bau.authorizeLocalConfirm(ack, priority, hopType, tsap, getInt(data + 2), status); + _bau.authorizeLocalConfirm(ack, priority, hopType, tsap, secCtrl, getInt(data + 2), status); break; case AuthorizeResponse: - _bau.authorizeResponseConfirm(ack, priority, hopType, tsap, data[1], status); + _bau.authorizeResponseConfirm(ack, priority, hopType, tsap, secCtrl, data[1], status); break; case KeyWrite: - _bau.keyWriteLocalConfirm(ack, priority, hopType, tsap, data[1], getInt(data + 2), status); + _bau.keyWriteLocalConfirm(ack, priority, hopType, tsap, secCtrl, data[1], getInt(data + 2), status); break; case KeyResponse: - _bau.keyWriteResponseConfirm(ack, priority, hopType, tsap, data[1], status); + _bau.keyWriteResponseConfirm(ack, priority, hopType, tsap, secCtrl, data[1], status); break; default: print("Indiviual-confirm: unhandled APDU-Type: "); @@ -992,15 +1271,42 @@ void ApplicationLayer::individualConfirm(AckType ack, HopCountType hopType, Prio } } -void ApplicationLayer::individualSend(AckType ack, HopCountType hopType, Priority priority, uint16_t asap, APDU& apdu) +void ApplicationLayer::individualSend(AckType ack, HopCountType hopType, Priority priority, uint16_t asap, APDU& apdu, const SecurityControl& secCtrl) { if (asap == _connectedTsap) - _transportLayer->dataConnectedRequest(asap, priority, apdu); + dataConnectedRequest(asap, priority, apdu, secCtrl); else - _transportLayer->dataIndividualRequest(ack, hopType, priority, asap, apdu); + dataIndividualRequest(ack, hopType, priority, asap, apdu, secCtrl); } bool ApplicationLayer::isConnected() { return (_connectedTsap >= 0); } + +void ApplicationLayer::dataGroupRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl& secCtrl) +{ + (void)secCtrl; // We do not need security related information in the plain application layer + _transportLayer->dataGroupRequest(ack, hopType, priority, tsap, apdu); +} +void ApplicationLayer::dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl &secCtrl) +{ + (void)secCtrl; // We do not need security related information in the plain application layer + _transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu); +} +void ApplicationLayer::dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl& secCtrl) +{ + (void)secCtrl; // We do not need security related information in the plain application layer + _transportLayer->dataSystemBroadcastRequest(ack, hopType, SystemPriority, apdu); +} +void ApplicationLayer::dataIndividualRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t destination, APDU& apdu, const SecurityControl& secCtrl) +{ + (void)secCtrl; // We do not need security related information in the plain application layer + _transportLayer->dataIndividualRequest(ack, hopType, priority, destination, apdu); +} +void ApplicationLayer::dataConnectedRequest(uint16_t tsap, Priority priority, APDU& apdu, const SecurityControl &secCtrl) +{ + (void)secCtrl; // We do not need security related information in the plain application layer + // apdu must be valid until it was confirmed + _transportLayer->dataConnectedRequest(tsap, priority, apdu); +} diff --git a/src/knx/application_layer.h b/src/knx/application_layer.h index e05e3c9..4234716 100644 --- a/src/knx/application_layer.h +++ b/src/knx/application_layer.h @@ -29,6 +29,9 @@ class ApplicationLayer void transportLayer(TransportLayer& layer); // from transport layer + // Note: without data secure feature, the application layer is just used with SecurtyControl.dataSecurity = none + // hooks that can be implemented by derived class (e.g. SecureApplicationLayer) + #pragma region Transport - Layer - Callbacks /** * Somebody send us an APDU via multicast communiation. See 3.2 of @cite knx:3/3/4. @@ -39,12 +42,12 @@ class ApplicationLayer * See 3.1.1 of @cite knx:3/3/7 * * @param apdu The submitted APDU. - * + * * @param priority The ::Priority of the received request. * * @param hopType Should routing be endless or should the NetworkLayer::hopCount be used? See also ::HopCountType. */ - void dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu); + virtual void dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu); /** * Report the status of an APDU that we sent via multicast communiation back to us. See 3.2 of @cite knx:3/3/4. * See also ApplicationLayer::dataGroupConfirm and TransportLayer::dataGroupRequest. This method is called by @@ -63,102 +66,146 @@ class ApplicationLayer * * @param ack Did we want a DataLinkLayer acknowledgement? See ::AckType. */ - void dataGroupConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, - APDU& apdu, bool status); - void dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu); - void dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, bool status); - void dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu); - void dataSystemBroadcastConfirm(HopCountType hopType, Priority priority, APDU& apdu, bool status); - void dataIndividualIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu); - void dataIndividualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status); + virtual void dataGroupConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status); + virtual void dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu); + virtual void dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, bool status); + virtual void dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu); + virtual void dataSystemBroadcastConfirm(HopCountType hopType, Priority priority, APDU& apdu, bool status); + virtual void dataIndividualIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu); + virtual void dataIndividualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status); + virtual void dataConnectedIndication(Priority priority, uint16_t tsap, APDU& apdu); + virtual void dataConnectedConfirm(uint16_t tsap); void connectIndication(uint16_t tsap); void connectConfirm(uint16_t destination, uint16_t tsap, bool status); void disconnectIndication(uint16_t tsap); void disconnectConfirm(Priority priority, uint16_t tsap, bool status); - void dataConnectedIndication(Priority priority, uint16_t tsap, APDU& apdu); - void dataConnectedConfirm(uint16_t tsap); #pragma endregion #pragma region from bau - void groupValueReadRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType); - void groupValueReadResponse(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t* data, uint8_t dataLength); - void groupValueWriteRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t* data, uint8_t dataLength); - void individualAddressWriteRequest(AckType ack, HopCountType hopType, uint16_t newaddress); - void individualAddressReadRequest(AckType ack, HopCountType hopType); - void individualAddressReadResponse(AckType ack, HopCountType hopType); - void individualAddressSerialNumberReadRequest(AckType ack, HopCountType hopType, uint8_t* serialNumber); - void individualAddressSerialNumberReadResponse(AckType ack, HopCountType hopType, uint8_t* serialNumber, + void groupValueReadRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl); + void groupValueReadResponse(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* data, uint8_t dataLength); + void groupValueWriteRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* data, uint8_t dataLength); + void individualAddressWriteRequest(AckType ack, HopCountType hopType, const SecurityControl &secCtrl, uint16_t newaddress); + void individualAddressReadRequest(AckType ack, HopCountType hopType, const SecurityControl& secCtrl); + void individualAddressReadResponse(AckType ack, HopCountType hopType, const SecurityControl& secCtrl); + void individualAddressSerialNumberReadRequest(AckType ack, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* serialNumber); + void individualAddressSerialNumberReadResponse(AckType ack, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* serialNumber, uint16_t domainAddress); - void individualAddressSerialNumberWriteRequest(AckType ack, HopCountType hopType, uint8_t* serialNumber, + void individualAddressSerialNumberWriteRequest(AckType ack, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* serialNumber, uint16_t newaddress); - void deviceDescriptorReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + void deviceDescriptorReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t descriptorType); - void deviceDescriptorReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + void deviceDescriptorReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t descriptorType, uint8_t* deviceDescriptor); void connectRequest(uint16_t destination, Priority priority); void disconnectRequest(Priority priority); bool isConnected(); - void restartRequest(AckType ack, Priority priority, HopCountType hopType); - void propertyValueReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + void restartRequest(AckType ack, Priority priority, HopCountType hopType, const SecurityControl& secCtrl); + void restartResponse(AckType ack, Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint8_t errorCode, uint16_t processTime); + void propertyValueReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex); - void propertyValueReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + void propertyValueReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length); - void propertyValueWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + void propertyValueExtReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, + uint16_t objectType, uint8_t objectInstance, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length); + void propertyValueExtWriteConResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, + uint16_t objectType, uint8_t objectInstance, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t returnCode); + void propertyValueWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length); - void functionPropertyStateResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + void functionPropertyStateResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t *resultData, uint8_t resultLength); - void propertyDescriptionReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + void functionPropertyExtStateResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, + uint16_t objectType, uint8_t objectInstance, uint16_t propertyId, uint8_t* resultData, uint8_t resultLength); + void propertyDescriptionReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex); - void propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + void propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type, uint16_t maxNumberOfElements, uint8_t access); - void memoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + void memoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint16_t memoryAddress); - void memoryReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + void memoryReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t* data); - void memoryWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + void memoryExtReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ReturnCodes code, uint8_t number, + uint32_t memoryAddress, uint8_t* data); + void memoryExtWriteResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ReturnCodes code, uint8_t number, + uint32_t memoryAddress, uint8_t *memoryData); + void memoryWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t* data); - void userMemoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + void userMemoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress); - void userMemoryReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + void userMemoryReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* memoryData); - void userMemoryWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + void userMemoryWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* memoryData); - void userManufacturerInfoReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap); - void userManufacturerInfoReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + void userManufacturerInfoReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl); + void userManufacturerInfoReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t* info); - void authorizeRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint32_t key); - void authorizeResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level); - void keyWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, uint32_t key); - void keyWriteResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level); + void authorizeRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint32_t key); + void authorizeResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t level); + void keyWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t level, uint32_t key); + void keyWriteResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t level); - void systemNetworkParameterReadResponse(Priority priority, HopCountType hopType, uint16_t objectType, + void systemNetworkParameterReadResponse(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint16_t objectType, uint16_t propertyId, uint8_t* testInfo, uint16_t testInfoLength, uint8_t* testResult, uint16_t testResultLength); - void domainAddressSerialNumberReadResponse(Priority priority, HopCountType hopType, const uint8_t* rfDoA, + void domainAddressSerialNumberReadResponse(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, const uint8_t* rfDoA, const uint8_t* knxSerialNumber); - void IndividualAddressSerialNumberReadResponse(Priority priority, HopCountType hopType, const uint8_t* rfDoA, + void IndividualAddressSerialNumberReadResponse(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, const uint8_t* rfDoA, const uint8_t* knxSerialNumber); #pragma endregion + protected: + +#pragma region hooks + void dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl &secCtrl); + void dataGroupConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, + APDU& apdu, const SecurityControl& secCtrl, bool status); + void dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu, const SecurityControl& secCtrl); + void dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl& secCtrl, bool status); + void dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu, const SecurityControl& secCtrl); + void dataSystemBroadcastConfirm(HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl& secCtrl, bool status); + void dataIndividualIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu, const SecurityControl& secCtrl); + void dataIndividualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl& secCtrl, bool status); + void dataConnectedIndication(Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl& secCtrl); + void dataConnectedConfirm(uint16_t tsap, const SecurityControl& secCtrl); +#pragma endregion + + // to transport layer + virtual void dataGroupRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl &secCtrl); + virtual void dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl& secCtrl); + virtual void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl& secCtrl); + virtual void dataIndividualRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t destination, APDU& apdu, const SecurityControl& secCtrl); + virtual void dataConnectedRequest(uint16_t tsap, Priority priority, APDU& apdu, const SecurityControl& secCtrl); // apdu must be valid until it was confirmed + + uint16_t getConnectedTsasp() {return _connectedTsap;} + + // Protected: we need to access it in derived class SecureApplicationLayer + TransportLayer* _transportLayer = 0; + + static const SecurityControl noSecurity; + private: - void propertyDataSend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + void propertyDataSend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length); - void memorySend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + void propertyExtDataSend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, + uint16_t objectType, uint8_t objectInstance, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length); + void memorySend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t* memoryData); - void userMemorySend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + void userMemorySend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* memoryData); - void groupValueSend(ApduType type, AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t* data, uint8_t& dataLength); - void individualIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu); - void individualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status); - void individualSend(AckType ack, HopCountType hopType, Priority priority, uint16_t asap, APDU& apdu); + void groupValueSend(ApduType type, AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* data, uint8_t& dataLength); + void individualIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl &secCtrl); + void individualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl& secCtrl, bool status); + void individualSend(AckType ack, HopCountType hopType, Priority priority, uint16_t asap, APDU& apdu, const SecurityControl& secCtrl); + + uint16_t crc16Ccitt(uint8_t* input, uint16_t length); uint16_t _savedAsapReadRequest; uint16_t _savedAsapWriteRequest; uint16_t _savedAsapResponse; AssociationTableObject& _assocTable; BusAccessUnit& _bau; - TransportLayer* _transportLayer = 0; + int32_t _connectedTsap = -1; }; diff --git a/src/knx/bau.cpp b/src/knx/bau.cpp index b09f513..a74e7bf 100644 --- a/src/knx/bau.cpp +++ b/src/knx/bau.cpp @@ -1,248 +1,297 @@ #include "bau.h" -void BusAccessUnit::groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, bool status) +void BusAccessUnit::groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl, bool status) { } -void BusAccessUnit::groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType) +void BusAccessUnit::groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl) { } -void BusAccessUnit::groupValueReadResponseConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopTtype, uint8_t* data, uint8_t dataLength, bool status) +void BusAccessUnit::groupValueReadResponseConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopTtype, const SecurityControl &secCtrl, uint8_t* data, uint8_t dataLength, bool status) { } -void BusAccessUnit::groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, uint8_t* data, uint8_t dataLength) +void BusAccessUnit::groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* data, uint8_t dataLength) { } -void BusAccessUnit::groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t* data, uint8_t dataLength, bool status) +void BusAccessUnit::groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* data, uint8_t dataLength, bool status) { } -void BusAccessUnit::groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, uint8_t* data, uint8_t dataLength) +void BusAccessUnit::groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* data, uint8_t dataLength) { } -void BusAccessUnit::individualAddressWriteLocalConfirm(AckType ack, HopCountType hopType, uint16_t newaddress, bool status) +void BusAccessUnit::individualAddressWriteLocalConfirm(AckType ack, HopCountType hopType, const SecurityControl &secCtrl, uint16_t newaddress, bool status) { } -void BusAccessUnit::individualAddressWriteIndication(HopCountType hopType, uint16_t newaddress) +void BusAccessUnit::individualAddressWriteIndication(HopCountType hopType, const SecurityControl &secCtrl, uint16_t newaddress) { } -void BusAccessUnit::individualAddressReadLocalConfirm(AckType ack, HopCountType hopType, bool status) +void BusAccessUnit::individualAddressReadLocalConfirm(AckType ack, HopCountType hopType, const SecurityControl &secCtrl, bool status) { } -void BusAccessUnit::individualAddressReadIndication(HopCountType hopType) +void BusAccessUnit::individualAddressReadIndication(HopCountType hopType, const SecurityControl &secCtrl) { } -void BusAccessUnit::individualAddressReadResponseConfirm(AckType ack, HopCountType hopType, bool status) +void BusAccessUnit::individualAddressReadResponseConfirm(AckType ack, HopCountType hopType, const SecurityControl &secCtrl, bool status) { } -void BusAccessUnit::individualAddressReadAppLayerConfirm(HopCountType hopType, uint16_t individualAddress) +void BusAccessUnit::individualAddressReadAppLayerConfirm(HopCountType hopType, const SecurityControl &secCtrl, uint16_t individualAddress) { } -void BusAccessUnit::individualAddressSerialNumberReadLocalConfirm(AckType ack, HopCountType hopType, uint8_t* serialNumber, bool status) +void BusAccessUnit::individualAddressSerialNumberReadLocalConfirm(AckType ack, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* serialNumber, bool status) { } -void BusAccessUnit::individualAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, uint8_t* knxSerialNumber) +void BusAccessUnit::individualAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* knxSerialNumber) { } -void BusAccessUnit::individualAddressSerialNumberReadResponseConfirm(AckType ack, HopCountType hopType, uint8_t* serialNumber, uint16_t domainAddress, bool status) +void BusAccessUnit::individualAddressSerialNumberReadResponseConfirm(AckType ack, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* serialNumber, uint16_t domainAddress, bool status) { } -void BusAccessUnit::individualAddressSerialNumberReadAppLayerConfirm(HopCountType hopType, uint8_t* serialNumber, uint16_t individualAddress, uint16_t domainAddress) +void BusAccessUnit::individualAddressSerialNumberReadAppLayerConfirm(HopCountType hopType, const SecurityControl &secCtrl, uint8_t* serialNumber, uint16_t individualAddress, uint16_t domainAddress) { } -void BusAccessUnit::individualAddressSerialNumberWriteLocalConfirm(AckType ack, HopCountType hopType, uint8_t* serialNumber, uint16_t newaddress, bool status) +void BusAccessUnit::individualAddressSerialNumberWriteLocalConfirm(AckType ack, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* serialNumber, uint16_t newaddress, bool status) { } -void BusAccessUnit::individualAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, uint16_t newIndividualAddress, +void BusAccessUnit::individualAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint16_t newIndividualAddress, uint8_t* knxSerialNumber) { } -void BusAccessUnit::deviceDescriptorReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptorType, bool status) +void BusAccessUnit::deviceDescriptorReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t descriptorType, bool status) { } -void BusAccessUnit::deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptorType) +void BusAccessUnit::deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t descriptorType) { } -void BusAccessUnit::deviceDescriptorReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptor_type, +void BusAccessUnit::deviceDescriptorReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t descriptor_type, uint8_t* device_descriptor, bool status) { } -void BusAccessUnit::deviceDescriptorReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptortype, uint8_t* deviceDescriptor) +void BusAccessUnit::deviceDescriptorReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t descriptortype, uint8_t* deviceDescriptor) { } -void BusAccessUnit::restartRequestLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, bool status) +void BusAccessUnit::restartRequestLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, bool status) { } -void BusAccessUnit::restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap) +void BusAccessUnit::restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, RestartType restartType, EraseCode eraseCode, uint8_t channel) { } -void BusAccessUnit::propertyValueReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, bool status) +void BusAccessUnit::propertyValueReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, bool status) { } -void BusAccessUnit::propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex) +void BusAccessUnit::propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex) { } -void BusAccessUnit::functionPropertyCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t* data, uint8_t length) +void BusAccessUnit::propertyValueExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance, + uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex) { } -void BusAccessUnit::functionPropertyStateIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t* data, uint8_t length) +void BusAccessUnit::functionPropertyCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t* data, uint8_t length) { } -void BusAccessUnit::propertyValueReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length, bool status) +void BusAccessUnit::functionPropertyStateIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t* data, uint8_t length) { } -void BusAccessUnit::propertyValueReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length) +void BusAccessUnit::functionPropertyExtCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance, uint8_t propertyId, uint8_t* data, uint8_t length) { } -void BusAccessUnit::propertyValueWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length, bool status) +void BusAccessUnit::functionPropertyExtStateIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance, uint8_t propertyId, uint8_t* data, uint8_t length) { } -void BusAccessUnit::propertyValueWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length) +void BusAccessUnit::propertyValueReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length, bool status) { } -void BusAccessUnit::propertyDescriptionReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool status) +void BusAccessUnit::propertyValueReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length) { } -void BusAccessUnit::propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex) +void BusAccessUnit::propertyValueWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length, bool status) { } -void BusAccessUnit::propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type, uint16_t maxNumberOfElements, uint8_t access) +void BusAccessUnit::propertyValueWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length) { } -void BusAccessUnit::propertyDescriptionReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type, uint16_t maxNumberOfElements, uint8_t access, bool status) +void BusAccessUnit::propertyValueExtWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length, bool confirmed) { } -void BusAccessUnit::propertyDescriptionReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type, uint16_t maxNumberOfElements, uint8_t access) +void BusAccessUnit::propertyDescriptionReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool status) { } -void BusAccessUnit::memoryReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress, bool status) +void BusAccessUnit::propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex) { } -void BusAccessUnit::memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress) +void BusAccessUnit::propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type, uint16_t maxNumberOfElements, uint8_t access) { } -void BusAccessUnit::memoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress, uint8_t* data, bool status) +void BusAccessUnit::propertyDescriptionReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type, uint16_t maxNumberOfElements, uint8_t access, bool status) { } -void BusAccessUnit::memoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress, uint8_t* data) +void BusAccessUnit::propertyDescriptionReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type, uint16_t maxNumberOfElements, uint8_t access) { } -void BusAccessUnit::memoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress, uint8_t* data, bool status) +void BusAccessUnit::memoryReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, bool status) { } -void BusAccessUnit::memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress, uint8_t* data) +void BusAccessUnit::memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress) { } -void BusAccessUnit::userMemoryReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, bool status) +void BusAccessUnit::memoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t* data, bool status) { } -void BusAccessUnit::userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress) +void BusAccessUnit::memoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t* data) { } -void BusAccessUnit::userMemoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, uint8_t* memoryData, bool status) +void BusAccessUnit::memoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t* data, bool status) { } -void BusAccessUnit::userMemoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, uint8_t* memoryData) +void BusAccessUnit::memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t* data) { } -void BusAccessUnit::userMemoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, uint8_t* memoryData, bool status) +void BusAccessUnit::memoryExtReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, bool status) { } -void BusAccessUnit::userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, uint8_t* memoryData) +void BusAccessUnit::memoryExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress) { } -void BusAccessUnit::userManufacturerInfoLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, bool status) +void BusAccessUnit::memoryExtReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* data, bool status) { } -void BusAccessUnit::userManufacturerInfoIndication(Priority priority, HopCountType hopType, uint16_t asap) +void BusAccessUnit::memoryExtReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* data) { } -void BusAccessUnit::userManufacturerInfoResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t* info, bool status) +void BusAccessUnit::memoryExtWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* data, bool status) { } -void BusAccessUnit::userManufacturerInfoAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t* info) +void BusAccessUnit::memoryExtWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* data) { } -void BusAccessUnit::authorizeLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint32_t key, bool status) +void BusAccessUnit::memoryExtWriteResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* data, bool status) { } -void BusAccessUnit::authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, uint32_t key) +void BusAccessUnit::memoryExtWriteAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* data) { } -void BusAccessUnit::authorizeResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, bool status) +void BusAccessUnit::userMemoryReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, bool status) { } -void BusAccessUnit::authorizeAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level) +void BusAccessUnit::userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress) { } -void BusAccessUnit::keyWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, uint32_t key, bool status) +void BusAccessUnit::userMemoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* memoryData, bool status) { } -void BusAccessUnit::keyWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, uint32_t key) +void BusAccessUnit::userMemoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* memoryData) { } -void BusAccessUnit::keyWriteResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, bool status) +void BusAccessUnit::userMemoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* memoryData, bool status) { } -void BusAccessUnit::keyWriteAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level) +void BusAccessUnit::userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* memoryData) +{ +} + +void BusAccessUnit::userManufacturerInfoLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, bool status) +{ +} + +void BusAccessUnit::userManufacturerInfoIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl) +{ +} + +void BusAccessUnit::userManufacturerInfoResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t* info, bool status) +{ +} + +void BusAccessUnit::userManufacturerInfoAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t* info) +{ +} + +void BusAccessUnit::authorizeLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint32_t key, bool status) +{ +} + +void BusAccessUnit::authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint32_t key) +{ +} + +void BusAccessUnit::authorizeResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t level, bool status) +{ +} + +void BusAccessUnit::authorizeAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t level) +{ +} + +void BusAccessUnit::keyWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t level, uint32_t key, bool status) +{ +} + +void BusAccessUnit::keyWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t level, uint32_t key) +{ +} + +void BusAccessUnit::keyWriteResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t level, bool status) +{ +} + +void BusAccessUnit::keyWriteAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t level) { } @@ -250,31 +299,31 @@ void BusAccessUnit::connectConfirm(uint16_t destination) { } -void BusAccessUnit::systemNetworkParameterReadIndication(Priority priority, HopCountType hopType, uint16_t objectType, +void BusAccessUnit::systemNetworkParameterReadIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint16_t objectType, uint16_t propertyId, uint8_t* testInfo, uint16_t testInfoLength) { } -void BusAccessUnit::domainAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, const uint8_t* rfDoA, +void BusAccessUnit::domainAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, const uint8_t* rfDoA, const uint8_t* knxSerialNumber) { } -void BusAccessUnit::domainAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const uint8_t* knxSerialNumber) +void BusAccessUnit::domainAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, const uint8_t* knxSerialNumber) { } -void BusAccessUnit::systemNetworkParameterReadLocalConfirm(Priority priority, HopCountType hopType, uint16_t objectType, +void BusAccessUnit::systemNetworkParameterReadLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint16_t objectType, uint16_t propertyId, uint8_t* testInfo, uint16_t testInfoLength, bool status) { } -void BusAccessUnit::domainAddressSerialNumberWriteLocalConfirm(Priority priority, HopCountType hopType, const uint8_t* rfDoA, +void BusAccessUnit::domainAddressSerialNumberWriteLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, const uint8_t* rfDoA, const uint8_t* knxSerialNumber, bool status) { } -void BusAccessUnit::domainAddressSerialNumberReadLocalConfirm(Priority priority, HopCountType hopType, const uint8_t* knxSerialNumber, bool status) +void BusAccessUnit::domainAddressSerialNumberReadLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, const uint8_t* knxSerialNumber, bool status) { } diff --git a/src/knx/bau.h b/src/knx/bau.h index d2d9c80..78b3580 100644 --- a/src/knx/bau.h +++ b/src/knx/bau.h @@ -7,129 +7,152 @@ class BusAccessUnit { public: virtual ~BusAccessUnit() {} - virtual void groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, bool status); - virtual void groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType); - virtual void groupValueReadResponseConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopTtype, + virtual void groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, bool status); + virtual void groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl); + virtual void groupValueReadResponseConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopTtype, const SecurityControl &secCtrl, uint8_t* data, uint8_t dataLength, bool status); - virtual void groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, + virtual void groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* data, uint8_t dataLength); - virtual void groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, + virtual void groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* data, uint8_t dataLength, bool status); - virtual void groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, + virtual void groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* data, uint8_t dataLength); - virtual void individualAddressWriteLocalConfirm(AckType ack, HopCountType hopType, + virtual void individualAddressWriteLocalConfirm(AckType ack, HopCountType hopType, const SecurityControl &secCtrl, uint16_t newaddress, bool status); - virtual void individualAddressWriteIndication(HopCountType hopType, uint16_t newaddress); - virtual void individualAddressReadLocalConfirm(AckType ack, HopCountType hopType, bool status); - virtual void individualAddressReadIndication(HopCountType hopType); - virtual void individualAddressReadResponseConfirm(AckType ack, HopCountType hopType, bool status); - virtual void individualAddressReadAppLayerConfirm(HopCountType hopType, uint16_t individualAddress); - virtual void individualAddressSerialNumberReadLocalConfirm(AckType ack, HopCountType hopType, + virtual void individualAddressWriteIndication(HopCountType hopType, const SecurityControl &secCtrl, uint16_t newaddress); + virtual void individualAddressReadLocalConfirm(AckType ack, HopCountType hopType, const SecurityControl &secCtrl, bool status); + virtual void individualAddressReadIndication(HopCountType hopType, const SecurityControl &secCtrl); + virtual void individualAddressReadResponseConfirm(AckType ack, HopCountType hopType, const SecurityControl &secCtrl, bool status); + virtual void individualAddressReadAppLayerConfirm(HopCountType hopType, const SecurityControl &secCtrl, uint16_t individualAddress); + virtual void individualAddressSerialNumberReadLocalConfirm(AckType ack, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* serialNumber, bool status); - virtual void individualAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, uint8_t* knxSerialNumber); - virtual void individualAddressSerialNumberReadResponseConfirm(AckType ack, HopCountType hopType, + virtual void individualAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* knxSerialNumber); + virtual void individualAddressSerialNumberReadResponseConfirm(AckType ack, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* serialNumber, uint16_t domainAddress, bool status); - virtual void individualAddressSerialNumberReadAppLayerConfirm(HopCountType hopType, uint8_t* serialNumber, + virtual void individualAddressSerialNumberReadAppLayerConfirm(HopCountType hopType, const SecurityControl &secCtrl, uint8_t* serialNumber, uint16_t individualAddress, uint16_t domainAddress); - virtual void individualAddressSerialNumberWriteLocalConfirm(AckType ack, HopCountType hopType, uint8_t* serialNumber, + virtual void individualAddressSerialNumberWriteLocalConfirm(AckType ack, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* serialNumber, uint16_t newaddress, bool status); - virtual void individualAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, uint16_t newIndividualAddress, + virtual void individualAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint16_t newIndividualAddress, uint8_t* knxSerialNumber); - virtual void deviceDescriptorReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + virtual void deviceDescriptorReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t descriptorType, bool status); - virtual void deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptorType); - virtual void deviceDescriptorReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + virtual void deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t descriptorType); + virtual void deviceDescriptorReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t descriptor_type, uint8_t* device_descriptor, bool status); - virtual void deviceDescriptorReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, + virtual void deviceDescriptorReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t descriptortype, uint8_t* deviceDescriptor); - virtual void restartRequestLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, bool status); - virtual void restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap); - virtual void propertyValueReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + virtual void restartRequestLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, bool status); + virtual void restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, RestartType restartType, EraseCode eraseCode, uint8_t channel); + virtual void propertyValueReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, bool status); - virtual void propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + virtual void propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex); - virtual void functionPropertyCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + virtual void propertyValueExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance, + uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex); + virtual void functionPropertyCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t* data, uint8_t length); - virtual void functionPropertyStateIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + virtual void functionPropertyStateIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t* data, uint8_t length); - virtual void propertyValueReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + virtual void functionPropertyExtCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance, + uint8_t propertyId, uint8_t* data, uint8_t length); + virtual void functionPropertyExtStateIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance, + uint8_t propertyId, uint8_t* data, uint8_t length); + virtual void propertyValueReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length, bool status); - virtual void propertyValueReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + virtual void propertyValueReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length); - virtual void propertyValueWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + virtual void propertyValueWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length, bool status); - virtual void propertyValueWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + virtual void propertyValueWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length); - virtual void propertyDescriptionReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + virtual void propertyValueExtWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance, + uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length, bool confirmed); + virtual void propertyDescriptionReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool status); - virtual void propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, + virtual void propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex); - virtual void propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + virtual void propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type, uint16_t maxNumberOfElements, uint8_t access); - virtual void propertyDescriptionReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + virtual void propertyDescriptionReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type, uint16_t maxNumberOfElements, uint8_t access, bool status); - virtual void propertyDescriptionReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, + virtual void propertyDescriptionReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type, uint16_t maxNumberOfElements, uint8_t access); - virtual void memoryReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + virtual void memoryReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, bool status); - virtual void memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress); - virtual void memoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + virtual void memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress); + virtual void memoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t* data, bool status); - virtual void memoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + virtual void memoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t* data); - virtual void memoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + virtual void memoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t* data, bool status); - virtual void memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + virtual void memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t* data); - virtual void userMemoryReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + virtual void memoryExtReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, + uint32_t memoryAddress, bool status); + virtual void memoryExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress); + virtual void memoryExtReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, + uint32_t memoryAddress, uint8_t* data, bool status); + virtual void memoryExtReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, + uint32_t memoryAddress, uint8_t* data); + virtual void memoryExtWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, + uint32_t memoryAddress, uint8_t* data, bool status); + virtual void memoryExtWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, + uint32_t memoryAddress, uint8_t* data); + virtual void memoryExtWriteResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, + uint32_t memoryAddress, uint8_t* data, bool status); + virtual void memoryExtWriteAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, + uint32_t memoryAddress, uint8_t* data); + virtual void userMemoryReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, bool status); - virtual void userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + virtual void userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress); - virtual void userMemoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + virtual void userMemoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* memoryData, bool status); - virtual void userMemoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + virtual void userMemoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* memoryData); - virtual void userMemoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + virtual void userMemoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* memoryData, bool status); - virtual void userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + virtual void userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* memoryData); - virtual void userManufacturerInfoLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, bool status); - virtual void userManufacturerInfoIndication(Priority priority, HopCountType hopType, uint16_t asap); - virtual void userManufacturerInfoResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + virtual void userManufacturerInfoLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, bool status); + virtual void userManufacturerInfoIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl); + virtual void userManufacturerInfoResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t* info, bool status); - virtual void userManufacturerInfoAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, + virtual void userManufacturerInfoAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t* info); - virtual void authorizeLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint32_t key, bool status); - virtual void authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, uint32_t key); - virtual void authorizeResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, + virtual void authorizeLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint32_t key, bool status); + virtual void authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint32_t key); + virtual void authorizeResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t level, bool status); - virtual void authorizeAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level); - virtual void keyWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, + virtual void authorizeAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t level); + virtual void keyWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t level, uint32_t key, bool status); - virtual void keyWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, + virtual void keyWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t level, uint32_t key); - virtual void keyWriteResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, + virtual void keyWriteResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t level, bool status); - virtual void keyWriteAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level); + virtual void keyWriteAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t level); virtual void connectConfirm(uint16_t destination); - virtual void systemNetworkParameterReadIndication(Priority priority, HopCountType hopType, uint16_t objectType, + virtual void systemNetworkParameterReadIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint16_t objectType, uint16_t propertyId, uint8_t* testInfo, uint16_t testInfoLength); - virtual void domainAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, const uint8_t* rfDoA, + virtual void domainAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, const uint8_t* rfDoA, const uint8_t* knxSerialNumber); - virtual void domainAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const uint8_t* knxSerialNumber); + virtual void domainAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, const uint8_t* knxSerialNumber); - virtual void systemNetworkParameterReadLocalConfirm(Priority priority, HopCountType hopType, uint16_t objectType, + virtual void systemNetworkParameterReadLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint16_t objectType, uint16_t propertyId, uint8_t* testInfo, uint16_t testInfoLength, bool status); - virtual void domainAddressSerialNumberWriteLocalConfirm(Priority priority, HopCountType hopType, const uint8_t* rfDoA, + virtual void domainAddressSerialNumberWriteLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, const uint8_t* rfDoA, const uint8_t* knxSerialNumber, bool status); - virtual void domainAddressSerialNumberReadLocalConfirm(Priority priority, HopCountType hopType, const uint8_t* knxSerialNumber, bool status); + virtual void domainAddressSerialNumberReadLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, const uint8_t* knxSerialNumber, bool status); virtual void propertyValueRead(ObjectType objectType, uint8_t objectInstance, uint8_t propertyId, uint8_t& numberOfElements, uint16_t startIndex, diff --git a/src/knx/bau07B0.cpp b/src/knx/bau07B0.cpp index 3d2f2a0..47c4f34 100644 --- a/src/knx/bau07B0.cpp +++ b/src/knx/bau07B0.cpp @@ -20,7 +20,6 @@ Bau07B0::Bau07B0(Platform& platform) _dlLayer.cemiServer(_cemiServer); _memory.addSaveRestore(&_cemiServerObject); #endif - // Set Mask Version in Device Object depending on the BAU _deviceObj.maskVersion(0x07B0); @@ -33,7 +32,12 @@ Bau07B0::Bau07B0(Platform& platform) prop->write(3, (uint16_t) OT_ASSOC_TABLE); prop->write(4, (uint16_t) OT_GRP_OBJ_TABLE); prop->write(5, (uint16_t) OT_APPLICATION_PROG); -#ifdef USE_CEMI_SERVER +#if defined(USE_DATASECURE) && defined(USE_CEMI_SERVER) + prop->write(6, (uint16_t) OT_SECURITY); + prop->write(7, (uint16_t) OT_CEMI_SERVER); +#elif defined(USE_DATASECURE) + prop->write(6, (uint16_t) OT_SECURITY); +#elif defined(USE_CEMI_SERVER) prop->write(6, (uint16_t) OT_CEMI_SERVER); #endif } @@ -54,10 +58,18 @@ InterfaceObject* Bau07B0::getInterfaceObject(uint8_t idx) return &_appProgram; case 5: // would be app_program 2 return nullptr; -#ifdef USE_CEMI_SERVER +#if defined(USE_DATASECURE) && defined(USE_CEMI_SERVER) + case 6: + return &_secIfObj; + case 7: + return &_cemiServerObject; +#elif defined(USE_CEMI_SERVER) case 6: return &_cemiServerObject; -#endif +#elif defined(USE_DATASECURE) + case 6: + return &_secIfObj; +#endif default: return nullptr; } @@ -81,6 +93,10 @@ InterfaceObject* Bau07B0::getInterfaceObject(ObjectType objectType, uint8_t obje return &_groupObjTable; case OT_APPLICATION_PROG: return &_appProgram; +#ifdef USE_DATASECURE + case OT_SECURITY: + return &_secIfObj; +#endif #ifdef USE_CEMI_SERVER case OT_CEMI_SERVER: return &_cemiServerObject; diff --git a/src/knx/bau27B0.cpp b/src/knx/bau27B0.cpp index 5161336..0ef567a 100644 --- a/src/knx/bau27B0.cpp +++ b/src/knx/bau27B0.cpp @@ -41,8 +41,13 @@ Bau27B0::Bau27B0(Platform& platform) prop->write(4, (uint16_t) OT_GRP_OBJ_TABLE); prop->write(5, (uint16_t) OT_APPLICATION_PROG); prop->write(6, (uint16_t) OT_RF_MEDIUM); -#ifdef USE_CEMI_SERVER - prop->write(7, (uint16_t) OT_CEMI_SERVER); +#if defined(USE_DATASECURE) && defined(USE_CEMI_SERVER) + prop->write(7, (uint16_t) OT_SECURITY); + prop->write(8, (uint16_t) OT_CEMI_SERVER); +#elif defined(USE_DATASECURE) + prop->write(7, (uint16_t) OT_SECURITY); +#elif defined(USE_CEMI_SERVER) + prop->write(7, (uint16_t)OT_CEMI_SERVER); #endif } @@ -65,10 +70,18 @@ InterfaceObject* Bau27B0::getInterfaceObject(uint8_t idx) return nullptr; case 6: return &_rfMediumObj; -#ifdef USE_CEMI_SERVER +#if defined(USE_DATASECURE) && defined(USE_CEMI_SERVER) + case 7: + return &_secIfObj; + case 8: + return &_cemiServerObject; +#elif defined(USE_CEMI_SERVER) case 7: return &_cemiServerObject; -#endif +#elif defined(USE_DATASECURE) + case 7: + return &_secIfObj; +#endif default: return nullptr; } @@ -94,6 +107,10 @@ InterfaceObject* Bau27B0::getInterfaceObject(ObjectType objectType, uint8_t obje return &_appProgram; case OT_RF_MEDIUM: return &_rfMediumObj; +#ifdef USE_DATASECURE + case OT_SECURITY: + return &_secIfObj; +#endif #ifdef USE_CEMI_SERVER case OT_CEMI_SERVER: return &_cemiServerObject; @@ -103,6 +120,14 @@ InterfaceObject* Bau27B0::getInterfaceObject(ObjectType objectType, uint8_t obje } } +void Bau27B0::doMasterReset(EraseCode eraseCode, uint8_t channel) +{ + // Common SystemB objects + BauSystemB::doMasterReset(eraseCode, channel); + + _rfMediumObj.masterReset(eraseCode, channel); +} + DataLinkLayer& Bau27B0::dataLinkLayer() { return _dlLayer; @@ -116,7 +141,7 @@ void Bau27B0::loop() #endif } -void Bau27B0::domainAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, const uint8_t* rfDoA, +void Bau27B0::domainAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, const uint8_t* rfDoA, const uint8_t* knxSerialNumber) { // If the received serial number matches our serial number @@ -125,37 +150,28 @@ void Bau27B0::domainAddressSerialNumberWriteIndication(Priority priority, HopCou _rfMediumObj.rfDomainAddress(rfDoA); } -void Bau27B0::domainAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const uint8_t* knxSerialNumber) +void Bau27B0::domainAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, const uint8_t* knxSerialNumber) { // If the received serial number matches our serial number // then send a response with the current RF domain address stored in the RF medium object if (!memcmp(knxSerialNumber, _deviceObj.propertyData(PID_SERIAL_NUMBER), 6)) - _appLayer.domainAddressSerialNumberReadResponse(priority, hopType, _rfMediumObj.rfDomainAddress(), knxSerialNumber); + _appLayer.domainAddressSerialNumberReadResponse(priority, hopType, secCtrl, _rfMediumObj.rfDomainAddress(), knxSerialNumber); } -void Bau27B0::individualAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, uint16_t newIndividualAddress, - uint8_t* knxSerialNumber) -{ - // If the received serial number matches our serial number - // then store the received new individual address in the device object - if (!memcmp(knxSerialNumber, _deviceObj.propertyData(PID_SERIAL_NUMBER), 6)) - _deviceObj.induvidualAddress(newIndividualAddress); -} - -void Bau27B0::individualAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, uint8_t* knxSerialNumber) +void Bau27B0::individualAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* knxSerialNumber) { // If the received serial number matches our serial number // then send a response with the current RF domain address stored in the RF medium object and the serial number if (!memcmp(knxSerialNumber, _deviceObj.propertyData(PID_SERIAL_NUMBER), 6)) - _appLayer.IndividualAddressSerialNumberReadResponse(priority, hopType, _rfMediumObj.rfDomainAddress(), knxSerialNumber); + _appLayer.IndividualAddressSerialNumberReadResponse(priority, hopType, secCtrl, _rfMediumObj.rfDomainAddress(), knxSerialNumber); } -void Bau27B0::domainAddressSerialNumberWriteLocalConfirm(Priority priority, HopCountType hopType, const uint8_t* rfDoA, +void Bau27B0::domainAddressSerialNumberWriteLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, const uint8_t* rfDoA, const uint8_t* knxSerialNumber, bool status) { } -void Bau27B0::domainAddressSerialNumberReadLocalConfirm(Priority priority, HopCountType hopType, const uint8_t* knxSerialNumber, bool status) +void Bau27B0::domainAddressSerialNumberReadLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, const uint8_t* knxSerialNumber, bool status) { } diff --git a/src/knx/bau27B0.h b/src/knx/bau27B0.h index 678a659..63f2844 100644 --- a/src/knx/bau27B0.h +++ b/src/knx/bau27B0.h @@ -20,6 +20,7 @@ class Bau27B0 : public BauSystemB InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance); DataLinkLayer& dataLinkLayer(); + virtual void doMasterReset(EraseCode eraseCode, uint8_t channel) override; private: RfDataLinkLayer _dlLayer; RfMediumObject _rfMediumObj; @@ -28,14 +29,12 @@ class Bau27B0 : public BauSystemB CemiServerObject _cemiServerObject; #endif - void domainAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, const uint8_t* rfDoA, + void domainAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, const uint8_t* rfDoA, const uint8_t* knxSerialNumber) override; - void domainAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const uint8_t* knxSerialNumber) override; - void individualAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, uint16_t newIndividualAddress, - uint8_t* knxSerialNumber) override; - void individualAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, uint8_t* knxSerialNumber) override; - void domainAddressSerialNumberWriteLocalConfirm(Priority priority, HopCountType hopType, const uint8_t* rfDoA, + void domainAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, const uint8_t* knxSerialNumber) override; + void individualAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* knxSerialNumber) override; + void domainAddressSerialNumberWriteLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, const uint8_t* rfDoA, const uint8_t* knxSerialNumber, bool status) override; - void domainAddressSerialNumberReadLocalConfirm(Priority priority, HopCountType hopType, const uint8_t* knxSerialNumber, bool status) override; + void domainAddressSerialNumberReadLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, const uint8_t* knxSerialNumber, bool status) override; }; #endif diff --git a/src/knx/bau57B0.cpp b/src/knx/bau57B0.cpp index 67b9a22..eee3cc2 100644 --- a/src/knx/bau57B0.cpp +++ b/src/knx/bau57B0.cpp @@ -38,7 +38,12 @@ Bau57B0::Bau57B0(Platform& platform) prop->write(4, (uint16_t) OT_GRP_OBJ_TABLE); prop->write(5, (uint16_t) OT_APPLICATION_PROG); prop->write(6, (uint16_t) OT_IP_PARAMETER); -#ifdef USE_CEMI_SERVER +#if defined(USE_DATASECURE) && defined(USE_CEMI_SERVER) + prop->write(7, (uint16_t) OT_SECURITY); + prop->write(8, (uint16_t) OT_CEMI_SERVER); +#elif defined(USE_DATASECURE) + prop->write(7, (uint16_t) OT_SECURITY); +#elif defined(USE_CEMI_SERVER) prop->write(7, (uint16_t) OT_CEMI_SERVER); #endif } @@ -61,6 +66,18 @@ InterfaceObject* Bau57B0::getInterfaceObject(uint8_t idx) return nullptr; case 6: return &_ipParameters; +#if defined(USE_DATASECURE) && defined(USE_CEMI_SERVER) + case 7: + return &_secIfObj; + case 8: + return &_cemiServerObject; +#elif defined(USE_CEMI_SERVER) + case 7: + return &_cemiServerObject; +#elif defined(USE_DATASECURE) + case 7: + return &_secIfObj; +#endif default: return nullptr; } @@ -86,11 +103,27 @@ InterfaceObject* Bau57B0::getInterfaceObject(ObjectType objectType, uint8_t obje return &_appProgram; case OT_IP_PARAMETER: return &_ipParameters; +#ifdef USE_DATASECURE + case OT_SECURITY: + return &_secIfObj; +#endif +#ifdef USE_CEMI_SERVER + case OT_CEMI_SERVER: + return &_cemiServerObject; +#endif default: return nullptr; } } +void Bau57B0::doMasterReset(EraseCode eraseCode, uint8_t channel) +{ + // Common SystemB objects + BauSystemB::doMasterReset(eraseCode, channel); + + _ipParameters.masterReset(eraseCode, channel); +} + DataLinkLayer& Bau57B0::dataLinkLayer() { return _dlLayer; diff --git a/src/knx/bau57B0.h b/src/knx/bau57B0.h index df0dc4b..85c5ca0 100644 --- a/src/knx/bau57B0.h +++ b/src/knx/bau57B0.h @@ -17,6 +17,7 @@ class Bau57B0 : public BauSystemB InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance); DataLinkLayer& dataLinkLayer(); + virtual void doMasterReset(EraseCode eraseCode, uint8_t channel) override; private: IpParameterObject _ipParameters; IpDataLinkLayer _dlLayer; @@ -25,4 +26,4 @@ class Bau57B0 : public BauSystemB CemiServerObject _cemiServerObject; #endif }; -#endif \ No newline at end of file +#endif diff --git a/src/knx/bau_systemB.cpp b/src/knx/bau_systemB.cpp index 0b2ed88..8d20f59 100644 --- a/src/knx/bau_systemB.cpp +++ b/src/knx/bau_systemB.cpp @@ -11,11 +11,22 @@ enum NmReadSerialNumberType NM_Read_SerialNumber_By_ManufacturerSpecific = 0xFE, }; +static constexpr auto kFunctionPropertyResultBufferMaxSize = 64; +static constexpr auto kRestartProcessTime = 3; + BauSystemB::BauSystemB(Platform& platform): _memory(platform, _deviceObj), _addrTable(_memory), _assocTable(_memory), _groupObjTable(_memory), _appProgram(_memory), - _platform(platform), _appLayer(_assocTable, *this), + _platform(platform), +#ifdef USE_DATASECURE + _appLayer(_deviceObj, _secIfObj, _assocTable, _addrTable, *this), +#else + _appLayer(_assocTable, *this), +#endif _transLayer(_appLayer, _addrTable), _netLayer(_transLayer) { +#ifdef USE_DATASECURE + _secIfObj.secureApplicationLayer(_appLayer); +#endif _appLayer.transportLayer(_transLayer); _transLayer.networkLayer(_netLayer); _memory.addSaveRestore(&_deviceObj); @@ -23,6 +34,10 @@ BauSystemB::BauSystemB(Platform& platform): _memory(platform, _deviceObj), _addr _memory.addSaveRestore(&_addrTable); _memory.addSaveRestore(&_assocTable); _memory.addSaveRestore(&_groupObjTable); +#ifdef USE_DATASECURE + _memory.addSaveRestore(&_secIfObj); +#endif + } void BauSystemB::loop() @@ -31,6 +46,9 @@ void BauSystemB::loop() _transLayer.loop(); sendNextGroupTelegram(); nextRestartState(); +#ifdef USE_DATASECURE + _appLayer.loop(); +#endif } bool BauSystemB::enabled() @@ -64,15 +82,25 @@ void BauSystemB::sendNextGroupTelegram() if (!go.communicationEnable()) continue; + SecurityControl goSecurity; + goSecurity.toolAccess = false; // Secured group communication never uses the toolkey. ETS knows all keys, also the group keys. + +#ifdef USE_DATASECURE + // Get security flags from Security Interface Object for this group object + goSecurity.dataSecurity = _secIfObj.getGroupObjectSecurity(asap); +#else + goSecurity.dataSecurity = DataSecurity::none; +#endif + if (flag == WriteRequest && go.transmitEnable()) { uint8_t* data = go.valueRef(); - _appLayer.groupValueWriteRequest(AckRequested, asap, go.priority(), NetworkLayerParameter, data, + _appLayer.groupValueWriteRequest(AckRequested, asap, go.priority(), NetworkLayerParameter, goSecurity, data, go.sizeInTelegram()); } else if (flag == ReadRequest) { - _appLayer.groupValueReadRequest(AckRequested, asap, go.priority(), NetworkLayerParameter); + _appLayer.groupValueReadRequest(AckRequested, asap, go.priority(), NetworkLayerParameter, goSecurity); } go.commFlag(Transmitting); @@ -141,59 +169,162 @@ bool BauSystemB::configured() return _configured; } -void BauSystemB::deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptorType) +uint8_t BauSystemB::checkmasterResetValidity(EraseCode eraseCode, uint8_t channel) +{ + static constexpr uint8_t successCode = 0x00; // Where does this come from? It is the code for "success". + static constexpr uint8_t invalidEraseCode = 0x02; // Where does this come from? It is the error code for "unspported erase code". + + switch (eraseCode) + { + case EraseCode::ConfirmedRestart: + { + println("Confirmed restart requested."); + return successCode; + } + case EraseCode::ResetAP: + { + // TODO: increase download counter except for confirmed restart (PID_DOWNLOAD_COUNTER) + println("ResetAP requested. Not implemented yet."); + return successCode; + } + case EraseCode::ResetIA: + { + // TODO: increase download counter except for confirmed restart (PID_DOWNLOAD_COUNTER) + println("ResetAP requested. Not implemented yet."); + return successCode; + } + case EraseCode::ResetLinks: + { + // TODO: increase download counter except for confirmed restart (PID_DOWNLOAD_COUNTER) + println("ResetLinks requested. Not implemented yet."); + return successCode; + } + case EraseCode::ResetParam: + { + // TODO: increase download counter except for confirmed restart (PID_DOWNLOAD_COUNTER) + println("ResetParam requested. Not implemented yet."); + return successCode; + } + case EraseCode::FactoryReset: + { + // TODO: increase download counter except for confirmed restart (PID_DOWNLOAD_COUNTER) + println("Factory reset requested. type: with IA"); + return successCode; + } + case EraseCode::FactoryResetWithoutIA: + { + // TODO: increase download counter except for confirmed restart (PID_DOWNLOAD_COUNTER) + println("Factory reset requested. type: without IA"); + return successCode; + } + default: + { + print("Unhandled erase code: "); + println(eraseCode, HEX); + return invalidEraseCode; + } + } +} + +void BauSystemB::deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t descriptorType) { if (descriptorType != 0) descriptorType = 0x3f; uint8_t data[2]; pushWord(_deviceObj.maskVersion(), data); - _appLayer.deviceDescriptorReadResponse(AckRequested, priority, hopType, asap, descriptorType, data); + _appLayer.deviceDescriptorReadResponse(AckRequested, priority, hopType, asap, secCtrl, descriptorType, data); } -void BauSystemB::memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, +void BauSystemB::memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t * data) { _memory.writeMemory(memoryAddress, number, data); if (_deviceObj.verifyMode()) - memoryReadIndication(priority, hopType, asap, number, memoryAddress); + memoryReadIndication(priority, hopType, asap, secCtrl, number, memoryAddress); } -void BauSystemB::memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, +void BauSystemB::memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress) { - _appLayer.memoryReadResponse(AckRequested, priority, hopType, asap, number, memoryAddress, + _appLayer.memoryReadResponse(AckRequested, priority, hopType, asap, secCtrl, number, memoryAddress, _memory.toAbsolute(memoryAddress)); } -void BauSystemB::restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap) +void BauSystemB::memoryExtWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t * data) { + _memory.writeMemory(memoryAddress, number, data); + + _appLayer.memoryExtWriteResponse(AckRequested, priority, hopType, asap, secCtrl, ReturnCodes::Success, number, memoryAddress, _memory.toAbsolute(memoryAddress)); +} + +void BauSystemB::memoryExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress) +{ + _appLayer.memoryExtReadResponse(AckRequested, priority, hopType, asap, secCtrl, ReturnCodes::Success, number, memoryAddress, _memory.toAbsolute(memoryAddress)); +} + +void BauSystemB::doMasterReset(EraseCode eraseCode, uint8_t channel) +{ + _addrTable.masterReset(eraseCode, channel); + _assocTable.masterReset(eraseCode, channel); + _groupObjTable.masterReset(eraseCode, channel); + _appProgram.masterReset(eraseCode, channel); +#ifdef USE_DATASECURE + // If erase code is FactoryReset or FactoryResetWithoutIA, set FDSK as toolkey again + // and disable security mode. + // FIXME: the A_RestartResponse PDU has still to be sent with the current toolkey. + // Idea: use local confirmation of sent A_RestartResponse PDU to trigger writing the FDSK afterwards + _secIfObj.masterReset(eraseCode, channel); +#endif +} + +void BauSystemB::restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, RestartType restartType, EraseCode eraseCode, uint8_t channel) +{ + if (restartType == RestartType::BasicRestart) + { + println("Basic restart requested"); + } + else if (restartType == RestartType::MasterReset) + { + uint8_t errorCode = checkmasterResetValidity(eraseCode, channel); + // We send the restart response now before actually applying the reset values + // Processing time is kRestartProcessTime (example 3 seconds) that we require for the applying the master reset with restart + _appLayer.restartResponse(AckRequested, priority, hopType, secCtrl, errorCode, (errorCode == 0) ? kRestartProcessTime : 0); + doMasterReset(eraseCode, channel); + } + else + { + // Cannot happen as restartType is just one bit + println("Unhandled restart type."); + _platform.fatalError(); + } + // Flush the EEPROM before resetting _memory.writeMemory(); _platform.restart(); } -void BauSystemB::authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, uint32_t key) +void BauSystemB::authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint32_t key) { - _appLayer.authorizeResponse(AckRequested, priority, hopType, asap, 0); + _appLayer.authorizeResponse(AckRequested, priority, hopType, asap, secCtrl, 0); } -void BauSystemB::userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress) +void BauSystemB::userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress) { - _appLayer.userMemoryReadResponse(AckRequested, priority, hopType, asap, number, memoryAddress, + _appLayer.userMemoryReadResponse(AckRequested, priority, hopType, asap, secCtrl, number, memoryAddress, _memory.toAbsolute(memoryAddress)); } -void BauSystemB::userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, uint8_t* data) +void BauSystemB::userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* data) { _memory.writeMemory(memoryAddress, number, data); if (_deviceObj.verifyMode()) - userMemoryReadIndication(priority, hopType, asap, number, memoryAddress); + userMemoryReadIndication(priority, hopType, asap, secCtrl, number, memoryAddress); } -void BauSystemB::propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, +void BauSystemB::propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex) { uint8_t pid = propertyId; @@ -205,20 +336,37 @@ void BauSystemB::propertyDescriptionReadIndication(Priority priority, HopCountTy if (obj) obj->readPropertyDescription(pid, propertyIndex, writeEnable, type, numberOfElements, access); - _appLayer.propertyDescriptionReadResponse(AckRequested, priority, hopType, asap, objectIndex, pid, propertyIndex, + _appLayer.propertyDescriptionReadResponse(AckRequested, priority, hopType, asap, secCtrl, objectIndex, pid, propertyIndex, writeEnable, type, numberOfElements, access); } -void BauSystemB::propertyValueWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, +void BauSystemB::propertyValueWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length) { InterfaceObject* obj = getInterfaceObject(objectIndex); if(obj) obj->writeProperty((PropertyID)propertyId, startIndex, data, numberOfElements); - propertyValueReadIndication(priority, hopType, asap, objectIndex, propertyId, numberOfElements, startIndex); + propertyValueReadIndication(priority, hopType, asap, secCtrl, objectIndex, propertyId, numberOfElements, startIndex); } -void BauSystemB::propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, +void BauSystemB::propertyValueExtWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance, + uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length, bool confirmed) +{ + uint8_t returnCode = ReturnCodes::Success; + + InterfaceObject* obj = getInterfaceObject(objectType, objectInstance); + if(obj) + obj->writeProperty((PropertyID)propertyId, startIndex, data, numberOfElements); + else + returnCode = ReturnCodes::AddressVoid; + + if (confirmed) + { + _appLayer.propertyValueExtWriteConResponse(AckRequested, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, numberOfElements, startIndex, returnCode); + } +} + +void BauSystemB::propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex) { uint8_t size = 0; @@ -242,49 +390,219 @@ void BauSystemB::propertyValueReadIndication(Priority priority, HopCountType hop if (elementCount == 0) size = 0; - _appLayer.propertyValueReadResponse(AckRequested, priority, hopType, asap, objectIndex, propertyId, elementCount, - startIndex, data, size); + _appLayer.propertyValueReadResponse(AckRequested, priority, hopType, asap, secCtrl, objectIndex, propertyId, elementCount, + startIndex, data, size); } -void BauSystemB::functionPropertyCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, +void BauSystemB::propertyValueExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance, + uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex) +{ + uint8_t size = 0; + uint8_t elementCount = numberOfElements; + InterfaceObject* obj = getInterfaceObject(objectType, objectInstance); + if (obj) + { + uint8_t elementSize = obj->propertySize((PropertyID)propertyId); + if (startIndex > 0) + size = elementSize * numberOfElements; + else + size = sizeof(uint16_t); // size of propert array entry 0 which is the size + } + else + elementCount = 0; + + uint8_t data[size]; + if(obj) + obj->readProperty((PropertyID)propertyId, startIndex, elementCount, data); + + if (elementCount == 0) + size = 0; + + _appLayer.propertyValueExtReadResponse(AckRequested, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, elementCount, + startIndex, data, size); +} + +void BauSystemB::functionPropertyCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t* data, uint8_t length) { - uint8_t resultLength = 0; - uint8_t resultData[32]; + uint8_t resultData[kFunctionPropertyResultBufferMaxSize]; + uint8_t resultLength = sizeof(resultData); // tell the callee the maximum size of the buffer InterfaceObject* obj = getInterfaceObject(objectIndex); if(obj) - obj->command((PropertyID)propertyId, data, length, resultData, resultLength); + { + if (obj->property((PropertyID)propertyId)->Type() == PDT_FUNCTION) + { + obj->command((PropertyID)propertyId, data, length, resultData, resultLength); + } + else + { + resultLength = 0; // We must not send a return code or any data fields + } + } - _appLayer.functionPropertyStateResponse(AckRequested, priority, hopType, asap, objectIndex, propertyId, resultData, resultLength); + _appLayer.functionPropertyStateResponse(AckRequested, priority, hopType, asap, secCtrl, objectIndex, propertyId, resultData, resultLength); } -void BauSystemB::functionPropertyStateIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, +void BauSystemB::functionPropertyStateIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t* data, uint8_t length) { - uint8_t resultLength = 0; - uint8_t resultData[32]; + uint8_t resultData[kFunctionPropertyResultBufferMaxSize]; + uint8_t resultLength = sizeof(resultData); // tell the callee the maximum size of the buffer InterfaceObject* obj = getInterfaceObject(objectIndex); if(obj) - obj->state((PropertyID)propertyId, data, length, resultData, resultLength); + { + if (obj->property((PropertyID)propertyId)->Type() == PDT_FUNCTION) + { + obj->state((PropertyID)propertyId, data, length, resultData, resultLength); + } + else + { + resultLength = 0; // We must not send a return code or any data fields + } + } - _appLayer.functionPropertyStateResponse(AckRequested, priority, hopType, asap, objectIndex, propertyId, resultData, resultLength); + _appLayer.functionPropertyStateResponse(AckRequested, priority, hopType, asap, secCtrl, objectIndex, propertyId, resultData, resultLength); } -void BauSystemB::individualAddressReadIndication(HopCountType hopType) +void BauSystemB::functionPropertyExtCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance, + uint8_t propertyId, uint8_t* data, uint8_t length) +{ + uint8_t resultData[kFunctionPropertyResultBufferMaxSize]; + uint8_t resultLength = 1; // we always have to include the return code at least + + InterfaceObject* obj = getInterfaceObject(objectType, objectInstance); + if(obj) + { + PropertyDataType propType = obj->property((PropertyID)propertyId)->Type(); + + if (propType == PDT_FUNCTION) + { + // The first byte is reserved and 0 for PDT_FUNCTION + uint8_t reservedByte = data[0]; + if (reservedByte != 0x00) + { + resultData[0] = ReturnCodes::DataVoid; + } + else + { + resultLength = sizeof(resultData); // tell the callee the maximum size of the buffer + obj->command((PropertyID)propertyId, data, length, resultData, resultLength); + // resultLength was modified by the callee + } + } + else if (propType == PDT_CONTROL) + { + uint8_t count = 1; + // write the event + obj->writeProperty((PropertyID)propertyId, 1, data, count); + if (count == 1) + { + // Read the current state (one byte only) for the response + obj->readProperty((PropertyID)propertyId, 1, count, &resultData[1]); + resultLength = count ? 2 : 1; + resultData[0] = count ? ReturnCodes::Success : ReturnCodes::DataVoid; + } + else + { + resultData[0] = ReturnCodes::AddressVoid; + } + } + else + { + resultData[0] = ReturnCodes::DataTypeConflict; + } + } + else + { + resultData[0] = ReturnCodes::GenericError; + } + + _appLayer.functionPropertyExtStateResponse(AckRequested, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, resultData, resultLength); +} + +void BauSystemB::functionPropertyExtStateIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance, + uint8_t propertyId, uint8_t* data, uint8_t length) +{ + uint8_t resultData[kFunctionPropertyResultBufferMaxSize]; + uint8_t resultLength = sizeof(resultData); // tell the callee the maximum size of the buffer + + InterfaceObject* obj = getInterfaceObject(objectType, objectInstance); + if(obj) + { + PropertyDataType propType = obj->property((PropertyID)propertyId)->Type(); + + if (propType == PDT_FUNCTION) + { + // The first byte is reserved and 0 for PDT_FUNCTION + uint8_t reservedByte = data[0]; + if (reservedByte != 0x00) + { + resultData[0] = ReturnCodes::DataVoid; + } + else + { + resultLength = sizeof(resultData); // tell the callee the maximum size of the buffer + obj->state((PropertyID)propertyId, data, length, resultData, resultLength); + // resultLength was modified by the callee + } + } + else if (propType == PDT_CONTROL) + { + uint8_t count = 1; + // Read the current state (one byte only) for the response + obj->readProperty((PropertyID)propertyId, 1, count, &resultData[1]); + resultLength = count ? 2 : 1; + resultData[0] = count ? ReturnCodes::Success : ReturnCodes::DataVoid; + } + else + { + resultData[0] = ReturnCodes::DataTypeConflict; + } + } + else + { + resultData[0] = ReturnCodes::GenericError; + } + + _appLayer.functionPropertyExtStateResponse(AckRequested, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, resultData, resultLength); +} + +void BauSystemB::individualAddressReadIndication(HopCountType hopType, const SecurityControl &secCtrl) { if (_deviceObj.progMode()) - _appLayer.individualAddressReadResponse(AckRequested, hopType); + _appLayer.individualAddressReadResponse(AckRequested, hopType, secCtrl); } -void BauSystemB::individualAddressWriteIndication(HopCountType hopType, uint16_t newaddress) +void BauSystemB::individualAddressWriteIndication(HopCountType hopType, const SecurityControl &secCtrl, uint16_t newaddress) { if (_deviceObj.progMode()) _deviceObj.induvidualAddress(newaddress); } -void BauSystemB::groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength, bool status) +void BauSystemB::individualAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint16_t newIndividualAddress, + uint8_t* knxSerialNumber) +{ + // If the received serial number matches our serial number + // then store the received new individual address in the device object + if (!memcmp(knxSerialNumber, _deviceObj.propertyData(PID_SERIAL_NUMBER), 6)) + _deviceObj.induvidualAddress(newIndividualAddress); +} + +void BauSystemB::individualAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* knxSerialNumber) +{ + // If the received serial number matches our serial number + // then send a response with the serial number. The domain address is set to 0 for closed media. + // An open medium BAU has to override this method and provide a proper domain address. + if (!memcmp(knxSerialNumber, _deviceObj.propertyData(PID_SERIAL_NUMBER), 6)) + { + uint8_t emptyDomainAddress[6] = {0x00}; + _appLayer.IndividualAddressSerialNumberReadResponse(priority, hopType, secCtrl, emptyDomainAddress, knxSerialNumber); + } +} + +void BauSystemB::groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t * data, uint8_t dataLength, bool status) { GroupObject& go = _groupObjTable.get(asap); if (status) @@ -293,7 +611,7 @@ void BauSystemB::groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priorit go.commFlag(Error); } -void BauSystemB::groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, bool status) +void BauSystemB::groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, bool status) { GroupObject& go = _groupObjTable.get(asap); if (status) @@ -302,18 +620,31 @@ void BauSystemB::groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority go.commFlag(Error); } -void BauSystemB::groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType) +void BauSystemB::groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl) { +#ifdef USE_DATASECURE + DataSecurity requiredGoSecurity; + + // Get security flags from Security Interface Object for this group object + requiredGoSecurity = _secIfObj.getGroupObjectSecurity(asap); + + if (secCtrl.dataSecurity != requiredGoSecurity) + { + println("GroupValueRead: access denied due to wrong security flags"); + return; + } +#endif + GroupObject& go = _groupObjTable.get(asap); if (!go.communicationEnable() || !go.readEnable()) return; uint8_t* data = go.valueRef(); - _appLayer.groupValueReadResponse(AckRequested, asap, priority, hopType, data, go.sizeInTelegram()); + _appLayer.groupValueReadResponse(AckRequested, asap, priority, hopType, secCtrl, data, go.sizeInTelegram()); } -void BauSystemB::groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, uint8_t* data, +void BauSystemB::groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* data, uint8_t dataLength) { GroupObject& go = _groupObjTable.get(asap); @@ -324,8 +655,20 @@ void BauSystemB::groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, updateGroupObject(go, data, dataLength); } -void BauSystemB::groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength) +void BauSystemB::groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t * data, uint8_t dataLength) { +#ifdef USE_DATASECURE + DataSecurity requiredGoSecurity; + + // Get security flags from Security Interface Object for this group object + requiredGoSecurity = _secIfObj.getGroupObjectSecurity(asap); + + if (secCtrl.dataSecurity != requiredGoSecurity) + { + println("GroupValueWrite: access denied due to wrong security flags"); + return; + } +#endif GroupObject& go = _groupObjTable.get(asap); if (!go.communicationEnable() || !go.writeEnable()) @@ -339,13 +682,14 @@ void BauSystemB::addSaveRestore(SaveRestore* obj) _memory.addSaveRestore(obj); } -bool BauSystemB::restartRequest(uint16_t asap) +bool BauSystemB::restartRequest(uint16_t asap, const SecurityControl secCtrl) { if (_appLayer.isConnected()) return false; _restartState = Connecting; // order important, has to be set BEFORE connectRequest + _restartSecurity = secCtrl; _appLayer.connectRequest(asap, SystemPriority); - _appLayer.deviceDescriptorReadRequest(AckRequested, SystemPriority, NetworkLayerParameter, asap, 0); + _appLayer.deviceDescriptorReadRequest(AckRequested, SystemPriority, NetworkLayerParameter, asap, secCtrl, 0); return true; } @@ -377,7 +721,7 @@ void BauSystemB::nextRestartState() /* connection confirmed, we send restartRequest, but we wait a moment (sending ACK etc)... */ if (millis() - _restartDelay > 30) { - _appLayer.restartRequest(AckRequested, SystemPriority, NetworkLayerParameter); + _appLayer.restartRequest(AckRequested, SystemPriority, NetworkLayerParameter, _restartSecurity); _restartState = Restarted; _restartDelay = millis(); } @@ -394,7 +738,7 @@ void BauSystemB::nextRestartState() } } -void BauSystemB::systemNetworkParameterReadIndication(Priority priority, HopCountType hopType, uint16_t objectType, +void BauSystemB::systemNetworkParameterReadIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint16_t objectType, uint16_t propertyId, uint8_t* testInfo, uint16_t testInfoLength) { uint8_t operand; @@ -409,7 +753,7 @@ void BauSystemB::systemNetworkParameterReadIndication(Priority priority, HopCoun if (_deviceObj.progMode() && (objectType == OT_DEVICE) && (propertyId == PID_SERIAL_NUMBER)) { // Send reply. testResult data is KNX serial number - _appLayer.systemNetworkParameterReadResponse(priority, hopType, objectType, propertyId, + _appLayer.systemNetworkParameterReadResponse(priority, hopType, secCtrl, objectType, propertyId, testInfo, testInfoLength, (uint8_t*)_deviceObj.propertyData(PID_SERIAL_NUMBER), 6); } break; @@ -425,7 +769,7 @@ void BauSystemB::systemNetworkParameterReadIndication(Priority priority, HopCoun } } -void BauSystemB::systemNetworkParameterReadLocalConfirm(Priority priority, HopCountType hopType, uint16_t objectType, +void BauSystemB::systemNetworkParameterReadLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint16_t objectType, uint16_t propertyId, uint8_t* testInfo, uint16_t testInfoLength, bool status) { } diff --git a/src/knx/bau_systemB.h b/src/knx/bau_systemB.h index b618fa6..b485942 100644 --- a/src/knx/bau_systemB.h +++ b/src/knx/bau_systemB.h @@ -6,8 +6,10 @@ #include "address_table_object.h" #include "association_table_object.h" #include "group_object_table_object.h" +#include "security_interface_object.h" #include "application_program_object.h" #include "application_layer.h" +#include "secure_application_layer.h" #include "transport_layer.h" #include "network_layer.h" #include "data_link_layer.h" @@ -29,7 +31,8 @@ class BauSystemB : protected BusAccessUnit void readMemory(); void writeMemory(); void addSaveRestore(SaveRestore* obj); - bool restartRequest(uint16_t asap); + bool restartRequest(uint16_t asap, const SecurityControl secCtrl); + uint8_t checkmasterResetValidity(EraseCode eraseCode, uint8_t channel); void propertyValueRead(ObjectType objectType, uint8_t objectInstance, uint8_t propertyId, uint8_t& numberOfElements, uint16_t startIndex, @@ -40,39 +43,54 @@ class BauSystemB : protected BusAccessUnit protected: virtual DataLinkLayer& dataLinkLayer() = 0; - void memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + void memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t* data) override; - void memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + void memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress) override; - void deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptorType) override; - void restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap) override; - void authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, uint32_t key) override; - void userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress) override; - void userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + void memoryExtWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, + uint32_t memoryAddress, uint8_t* data) override; + void memoryExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, + uint32_t memoryAddress) override; + void deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t descriptorType) override; + void restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, RestartType restartType, EraseCode eraseCode, uint8_t channel) override; + void authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint32_t key) override; + void userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress) override; + void userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* memoryData) override; - void propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + void propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex) override; - void propertyValueWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + void propertyValueWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length) override; - void propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + void propertyValueExtWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance, + uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length, bool confirmed); + void propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex) override; - void functionPropertyCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, - uint8_t propertyId, uint8_t* data, uint8_t length); - void functionPropertyStateIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, - uint8_t propertyId, uint8_t* data, uint8_t length); - void individualAddressReadIndication(HopCountType hopType) override; - void individualAddressWriteIndication(HopCountType hopType, uint16_t newaddress) override; - void groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, + void propertyValueExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance, + uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex) override; + void functionPropertyCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, + uint8_t propertyId, uint8_t* data, uint8_t length) override; + void functionPropertyStateIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, + uint8_t propertyId, uint8_t* data, uint8_t length) override; + void functionPropertyExtCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance, + uint8_t propertyId, uint8_t* data, uint8_t length) override; + void functionPropertyExtStateIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance, + uint8_t propertyId, uint8_t* data, uint8_t length) override; + void individualAddressReadIndication(HopCountType hopType, const SecurityControl &secCtrl) override; + void individualAddressWriteIndication(HopCountType hopType, const SecurityControl &secCtrl, uint16_t newaddress) override; + void individualAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint16_t newIndividualAddress, + uint8_t* knxSerialNumber) override; + void individualAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* knxSerialNumber) override; + void groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* data, uint8_t dataLength, bool status) override; - void groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, bool status) override; - void groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType) override; - void groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, + void groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, bool status) override; + void groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl) override; + void groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* data, uint8_t dataLength) override; - void groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, + void groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* data, uint8_t dataLength) override; - void systemNetworkParameterReadIndication(Priority priority, HopCountType hopType, uint16_t objectType, + void systemNetworkParameterReadIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint16_t objectType, uint16_t propertyId, uint8_t* testInfo, uint16_t testinfoLength) override; - void systemNetworkParameterReadLocalConfirm(Priority priority, HopCountType hopType, uint16_t objectType, + void systemNetworkParameterReadLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint16_t objectType, uint16_t propertyId, uint8_t* testInfo, uint16_t testInfoLength, bool status) override; void connectConfirm(uint16_t tsap) override; @@ -82,6 +100,8 @@ class BauSystemB : protected BusAccessUnit void updateGroupObject(GroupObject& go, uint8_t* data, uint8_t length); void nextRestartState(); + virtual void doMasterReset(EraseCode eraseCode, uint8_t channel); + enum RestartState { Idle, @@ -97,10 +117,16 @@ class BauSystemB : protected BusAccessUnit GroupObjectTableObject _groupObjTable; ApplicationProgramObject _appProgram; Platform& _platform; +#ifdef USE_DATASECURE + SecureApplicationLayer _appLayer; + SecurityInterfaceObject _secIfObj; +#else ApplicationLayer _appLayer; +#endif TransportLayer _transLayer; NetworkLayer _netLayer; bool _configured = true; RestartState _restartState = Idle; + SecurityControl _restartSecurity; uint32_t _restartDelay = 0; }; diff --git a/src/knx/bits.cpp b/src/knx/bits.cpp index 551fbd2..b47d65f 100644 --- a/src/knx/bits.cpp +++ b/src/knx/bits.cpp @@ -84,3 +84,24 @@ uint32_t getInt(const uint8_t * data) { return (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]; } + +void sixBytesFromUInt64(uint64_t num, uint8_t* toByteArray) +{ + toByteArray[0] = ((num >> 40) & 0xff); + toByteArray[1] = ((num >> 32) & 0xff); + toByteArray[2] = ((num >> 24) & 0xff); + toByteArray[3] = ((num >> 16) & 0xff); + toByteArray[4] = ((num >> 8) & 0xff); + toByteArray[5] = (num & 0xff); +} + +uint64_t sixBytesToUInt64(uint8_t* data) +{ + uint64_t l = 0; + + for (uint8_t i = 0; i < 6; i++) + { + l = (l << 8) + data[i]; + } + return l; +} diff --git a/src/knx/bits.h b/src/knx/bits.h index 61d0d45..637f494 100644 --- a/src/knx/bits.h +++ b/src/knx/bits.h @@ -58,6 +58,7 @@ void print(int, int = DEC); void print(unsigned int, int = DEC); void print(long, int = DEC); void print(unsigned long, int = DEC); +void print(unsigned long long, int = DEC); void print(double); void println(const char[]); @@ -67,6 +68,7 @@ void println(int, int = DEC); void println(unsigned int, int = DEC); void println(long, int = DEC); void println(unsigned long, int = DEC); +void println(unsigned long long, int = DEC); void println(double); void println(void); @@ -85,3 +87,6 @@ uint8_t* pushByteArray(const uint8_t* src, uint32_t size, uint8_t* data); uint16_t getWord(const uint8_t* data); uint32_t getInt(const uint8_t* data); void printHex(const char* suffix, const uint8_t *data, size_t length); + +void sixBytesFromUInt64(uint64_t num, uint8_t* toByteArray); +uint64_t sixBytesToUInt64(uint8_t* data); diff --git a/src/knx/config.h b/src/knx/config.h index 59ee9c4..1b03cd0 100644 --- a/src/knx/config.h +++ b/src/knx/config.h @@ -22,4 +22,6 @@ #define USE_CEMI_SERVER #endif +#define USE_DATASECURE + #endif diff --git a/src/knx/data_property.cpp b/src/knx/data_property.cpp index 00e1141..1b68565 100644 --- a/src/knx/data_property.cpp +++ b/src/knx/data_property.cpp @@ -151,3 +151,13 @@ const uint8_t* DataProperty::data() { return _data; } + +const uint8_t* DataProperty::data(uint16_t elementIndex) +{ + if ((elementIndex == 0) || (elementIndex > _currentElements)) + return nullptr; + + elementIndex -= 1; // Starting from 0 + uint16_t offset = elementIndex * ElementSize(); + return _data + offset; +} diff --git a/src/knx/data_property.h b/src/knx/data_property.h index e430eeb..12c202f 100644 --- a/src/knx/data_property.h +++ b/src/knx/data_property.h @@ -17,6 +17,7 @@ class DataProperty : public Property virtual const uint8_t* restore(const uint8_t* buffer) override; virtual uint16_t saveSize() override; const uint8_t* data(); + const uint8_t* data(uint16_t elementIndex); private: uint16_t _currentElements = 0; diff --git a/src/knx/device_object.cpp b/src/knx/device_object.cpp index 464ae4a..0bcb866 100644 --- a/src/knx/device_object.cpp +++ b/src/knx/device_object.cpp @@ -9,8 +9,10 @@ DeviceObject::DeviceObject() { - //Default to KNXA (0xFA) - uint8_t serialNumber[] = {0x00, 0xFA, 0x00, 0x00, 0x00, 0x00}; + // Default to KNXA (0xFA) + // Note: ETS does not accept a SN 00FA00000000 in data secure mode. + // ETS says that 00FA00000000 looks "suspicious" in the log file. + uint8_t serialNumber[] = {0x00, 0xFA, 0x01, 0x02, 0x03, 0x04}; uint8_t hardwareType[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; Property* properties[] = diff --git a/src/knx/interface_object.cpp b/src/knx/interface_object.cpp index 3949409..0789992 100644 --- a/src/knx/interface_object.cpp +++ b/src/knx/interface_object.cpp @@ -54,6 +54,12 @@ void InterfaceObject::readPropertyDescription(uint8_t& propertyId, uint8_t& prop } } +void InterfaceObject::masterReset(EraseCode eraseCode, uint8_t channel) +{ + // every interface object shall implement this + // However, for the time being we provide an empty default implementation +} + void InterfaceObject::readProperty(PropertyID id, uint16_t start, uint8_t& count, uint8_t* data) { Property* prop = property(id); @@ -101,7 +107,7 @@ void InterfaceObject::command(PropertyID id, uint8_t* data, uint8_t length, uint prop->command(data, length, resultData, resultLength); } -void InterfaceObject::state(PropertyID id, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t resultLength) +void InterfaceObject::state(PropertyID id, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) { Property* prop = property(id); if (prop == nullptr) @@ -190,3 +196,9 @@ const uint8_t* InterfaceObject::propertyData(PropertyID id) DataProperty* prop = (DataProperty*)property(id); return prop->data(); } + +const uint8_t* InterfaceObject::propertyData(PropertyID id, uint16_t elementIndex) +{ + DataProperty* prop = (DataProperty*)property(id); + return prop->data(elementIndex); +} diff --git a/src/knx/interface_object.h b/src/knx/interface_object.h index 63e6797..b287735 100644 --- a/src/knx/interface_object.h +++ b/src/knx/interface_object.h @@ -3,6 +3,7 @@ #include #include "property.h" #include "save_restore.h" +#include "knx_types.h" /** Enum for the type of an interface object. See Section 2.2 of knx:3/7/3 */ enum ObjectType @@ -49,6 +50,9 @@ enum ObjectType /** File Server Object */ OT_FILE_SERVER = 13, + /** Security Interface Object */ + OT_SECURITY = 17, + /** RF Medium Object */ OT_RF_MEDIUM = 19 }; @@ -125,7 +129,7 @@ class InterfaceObject : public SaveRestore * * @param[out] resultData The result data for the function */ - virtual void state(PropertyID id, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t resultLength); + virtual void state(PropertyID id, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t &resultLength); /** * Read the Description of a property of the interface object. The output parameters are only valid if nuberOfElements is not zero. * @@ -146,6 +150,10 @@ class InterfaceObject : public SaveRestore */ void readPropertyDescription(uint8_t& propertyId, uint8_t& propertyIndex, bool& writeEnable, uint8_t& type, uint16_t& numberOfElements, uint8_t& access); + // every interface object shall implement this + // However, for the time being we provide an empty default implementation + virtual void masterReset(EraseCode eraseCode, uint8_t channel); + /** * Gets property with PropertyID id if it exists and nullptr otherwise. */ @@ -169,6 +177,7 @@ class InterfaceObject : public SaveRestore } const uint8_t* propertyData(PropertyID id); + const uint8_t* propertyData(PropertyID id, uint16_t elementIndex); /** * Gets const property with PropertyID id if it exists and nullptr otherwise. */ diff --git a/src/knx/ip_data_link_layer.cpp b/src/knx/ip_data_link_layer.cpp index 7219508..13fbdfa 100644 --- a/src/knx/ip_data_link_layer.cpp +++ b/src/knx/ip_data_link_layer.cpp @@ -108,4 +108,4 @@ bool IpDataLinkLayer::sendBytes(uint8_t* bytes, uint16_t length) return _platform.sendBytesMultiCast(bytes, length); } -#endif \ No newline at end of file +#endif diff --git a/src/knx/knx_ip_frame.h b/src/knx/knx_ip_frame.h index 3f21a15..f486774 100644 --- a/src/knx/knx_ip_frame.h +++ b/src/knx/knx_ip_frame.h @@ -55,4 +55,4 @@ class KnxIpFrame uint8_t* _data = 0; uint16_t _dataLength; }; -#endif \ No newline at end of file +#endif diff --git a/src/knx/knx_ip_search_response.cpp b/src/knx/knx_ip_search_response.cpp index ba3968e..d051416 100644 --- a/src/knx/knx_ip_search_response.cpp +++ b/src/knx/knx_ip_search_response.cpp @@ -22,8 +22,8 @@ KnxIpSearchResponse::KnxIpSearchResponse(IpParameterObject& parameters, DeviceOb _deviceInfo.indiviudalAddress(parameters.propertyValue(PID_KNX_INDIVIDUAL_ADDRESS)); _deviceInfo.projectInstallationIdentifier(parameters.propertyValue(PID_PROJECT_INSTALLATION_ID)); _deviceInfo.serialNumber(deviceObject.propertyData(PID_SERIAL_NUMBER)); - //_deviceInfo.routingMulticastAddress(parameters.propertyValue(PID_ROUTING_MULTICAST_ADDRESS)); - _deviceInfo.routingMulticastAddress(0); + _deviceInfo.routingMulticastAddress(parameters.propertyValue(PID_ROUTING_MULTICAST_ADDRESS)); + //_deviceInfo.routingMulticastAddress(0); uint8_t mac_address[LEN_MAC_ADDRESS] = {0}; Property* prop = parameters.property(PID_MAC_ADDRESS); @@ -59,4 +59,4 @@ KnxIpSupportedServiceDIB& KnxIpSearchResponse::supportedServices() { return _supportedServices; } -#endif \ No newline at end of file +#endif diff --git a/src/knx/knx_types.h b/src/knx/knx_types.h index a84b224..f92b60c 100644 --- a/src/knx/knx_types.h +++ b/src/knx/knx_types.h @@ -66,6 +66,31 @@ enum cEmiErrorCode Value_temp_not_writeable = 0x0A, // The Property exists but can at this moment not be written with a new value (W) }; +// Unified return codes for KNX services and functions +// Note, that several older KNX services and functions do not use these return codes. +enum ReturnCodes +{ + // Generic positive return codes + Success = 0x00, // service, function or command executed sucessfully + SuccessWithCrc = 0x01, // positive message confirmation, CRC over original data + // Generic negative return codes + MemoryError = 0xF1, // memory cannot be accessed or only with fault(s) + InvalidCommand = 0xF2, // server does not support the requested command. ets: also non-existing or protected resource + ImpossibleCommand = 0xF3, // command cannot be executed because a dependency is not fulfilled + ExceedsMaxApduLength = 0xF4, // data will not fit into a frame supported by this server + DataOverflow = 0xF5, // attempt to write data beyond what is reserved for the addressed resource + OutOfMinRange = 0xF6, // write value below minimum supported value + OutOfMaxRange = 0xF7, // write value exceeds maximum supported value + DataVoid = 0xF8, // request contains invalid data + TemporarilyNotAvailable = 0xF9, // data access not possible at this time + AccessWriteOnly = 0xFA, // read access to write-only resource + AccessReadOnly = 0xFB, // write access to read-only resource + AccessDenied = 0xFC, // access to recource is not allowed because of authorization/security + AddressVoid = 0xFD, // resource is not present, address does not exist + DataTypeConflict = 0xFE, // write access with wrong datatype (datapoint length) + GenericError = 0xFF // service, function or command failed +}; + enum Repetition { NoRepitiion = 0, @@ -130,6 +155,20 @@ enum ApduType // Application Layer Services on Point-to-point Connection-Oriented Communication Mode (mandatory) // Application Layer Services on Point-to-point Connectionless Communication Mode (either optional or mandatory) + PropertyValueExtRead = 0x1CC, + PropertyValueExtResponse = 0x1CD, + PropertyValueExtWriteCon = 0x1CE, + PropertyValueExtWriteConResponse = 0x1CF, + PropertyValueExtWriteUnCon = 0x1D0, + PropertyExtDescriptionRead = 0x1D2, + PropertyExtDescriptionResponse = 0x1D3, + FunctionPropertyExtCommand = 0x1D4, + FunctionPropertyExtState = 0x1D5, + FunctionPropertyExtStateResponse = 0x1D6, + MemoryExtWrite = 0x1FB, + MemoryExtWriteResponse = 0x1FC, + MemoryExtRead = 0x1FD, + MemoryExtReadResponse = 0x1FE, MemoryRead = 0x200, MemoryResponse = 0x240, MemoryWrite = 0x280, @@ -144,6 +183,7 @@ enum ApduType DeviceDescriptorRead = 0x300, DeviceDescriptorResponse = 0x340, Restart = 0x380, + RestartMasterReset = 0x381, AuthorizeRequest = 0x3d1, AuthorizeResponse = 0x3d2, KeyWrite = 0x3d3, @@ -153,4 +193,38 @@ enum ApduType PropertyValueWrite = 0x3d7, PropertyDescriptionRead = 0x3d8, PropertyDescriptionResponse = 0x3d9, + + // Secure Service + SecureService = 0x3F1 +}; + +enum DataSecurity +{ + none, + auth, + authConf +}; + +struct SecurityControl +{ + bool toolAccess; + DataSecurity dataSecurity; +}; + +enum RestartType +{ + BasicRestart = 0x0, + MasterReset = 0x1 +}; + +enum EraseCode +{ + Void = 0x00, + ConfirmedRestart = 0x01, + FactoryReset = 0x02, + ResetIA = 0x03, + ResetAP = 0x04, + ResetParam = 0x05, + ResetLinks = 0x06, + FactoryResetWithoutIA = 0x07 }; diff --git a/src/knx/memory.cpp b/src/knx/memory.cpp index 058c3d1..6c4d04d 100644 --- a/src/knx/memory.cpp +++ b/src/knx/memory.cpp @@ -13,7 +13,7 @@ void Memory::readMemory() if (_data != nullptr) return; - uint16_t flashSize = 512; + uint16_t flashSize = 8192; _data = _platform.getEepromBuffer(flashSize); printHex("RESTORED ", _data, _metadataSize); @@ -428,4 +428,4 @@ void Memory::addNewUsedBlock(uint8_t* address, size_t size) MemoryBlock* newUsedBlock = new MemoryBlock(address, size); addToUsedList(newUsedBlock); -} \ No newline at end of file +} diff --git a/src/knx/property.h b/src/knx/property.h index 7c3ea93..17104c9 100644 --- a/src/knx/property.h +++ b/src/knx/property.h @@ -86,6 +86,8 @@ enum PropertyID PID_VERSION = 25, PID_MCB_TABLE = 27, PID_ERROR_CODE = 28, + PID_OBJECT_INDEX = 29, + PID_DOWNLOAD_COUNTER = 30, /** Properties in the Device Object */ PID_ROUTING_COUNT = 51, @@ -158,6 +160,20 @@ enum PropertyID PID_MAX_INTERFACE_APDU_LENGTH = 68, PID_MAX_LOCAL_APDU_LENGTH = 69, + /** Security Interface Object */ + PID_SECURITY_MODE = 51, // Enable and disable the Security Mode + PID_P2P_KEY_TABLE = 52, // Security keys used for securing point-to-point and broadcast communication + PID_GRP_KEY_TABLE = 53, // Security keys used for securing standard mode group communication + PID_SECURITY_INDIVIDUAL_ADDRESS_TABLE = 54, // IAs and last valid sequence numbers of communication partners with secure links + PID_SECURITY_FAILURES_LOG = 55, // Provides security failure information + PID_TOOL_KEY = 56, // Stores the security information for the central MaC in S-Mode and Ctrl-Mode + PID_SECURITY_REPORT = 57, // KNX Data Security-related status and diagnostic information + PID_SECURITY_REPORT_CONTROL = 58, // Control the spontaneous communication of the security report through DMP_InterfaceObject-InfoReport_RCl + PID_SEQUENCE_NUMBER_SENDING = 59, // Sequence Number used for the next outgoing secure communication + PID_ZONE_KEY_TABLE = 60, // Security keys used for securing zone addressing communication + PID_GO_SECURITY_FLAGS = 61, // Defines the required security requirements for each group object + PID_ROLE_TABLE = 62, // Role table + PID_TOOL_SEQUENCE_NUMBER_SENDING = 250, // Sequence Number used for the next outgoing secure communication (Tool Access only, non-standardized!) }; enum LoadState diff --git a/src/knx/secure_application_layer.cpp b/src/knx/secure_application_layer.cpp new file mode 100644 index 0000000..a5dfbc1 --- /dev/null +++ b/src/knx/secure_application_layer.cpp @@ -0,0 +1,1282 @@ +#include "config.h" +#ifdef USE_DATASECURE + +#include "secure_application_layer.h" +#include "transport_layer.h" +#include "cemi_frame.h" +#include "association_table_object.h" +#include "address_table_object.h" +#include "security_interface_object.h" +#include "device_object.h" +#include "apdu.h" +#include "bau.h" +#include "string.h" +#include "bits.h" + +// Select what cipher modes to include. We need AES128-CBC and AES128-CTR modes. +#define CBC 1 +#define CTR 1 +#define ECB 0 +#include "aes.hpp" + +static constexpr uint8_t kSecureDataPdu = 0; +static constexpr uint8_t kSecureSyncRequest = 2; +static constexpr uint8_t kSecureSyncResponse = 3; + +SecureApplicationLayer::SecureApplicationLayer(DeviceObject &deviceObj, SecurityInterfaceObject &secIfObj, AssociationTableObject& assocTable, AddressTableObject &addrTab, BusAccessUnit& bau): + ApplicationLayer(assocTable, bau), + _secIfObj(secIfObj), + _deviceObj(deviceObj), + _addrTab(addrTab) +{ +} + +/* from transport layer */ + +void SecureApplicationLayer::dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu) +{ + println("dataGroupIndication"); + + if (apdu.type() == SecureService) + { + // Will be filled in by decodeSecureApdu() + SecurityControl secCtrl; + + // Decrypt secure APDU + // Somehow ugly that we need to know the size in advance here at this point + uint16_t plainApduLength = apdu.length() - 3 - 6 - 4; // secureAdsuLength - sizeof(tpci,apci,scf) - sizeof(seqNum) - sizeof(mac) + CemiFrame plainFrame(plainApduLength); + // Decrypt secure APDU + if (decodeSecureApdu(apdu, plainFrame.apdu(), secCtrl)) + { + // Process decrypted inner APDU + ApplicationLayer::dataGroupIndication(hopType, priority, tsap, plainFrame.apdu(), secCtrl); + } + return; + } + + ApplicationLayer::dataGroupIndication(hopType, priority, tsap, apdu); +} + +void SecureApplicationLayer::dataGroupConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status) +{ + println("dataGroupConfirm"); + + if (apdu.type() == SecureService) + { + // We do not care about confirmations of our sync communication + if (isSyncService(apdu)) + { + return; + } + + // Will be filled in by decodeSecureApdu() + SecurityControl secCtrl; + + // Decrypt secure APDU + // Somehow ugly that we need to know the size in advance here at this point + uint16_t plainApduLength = apdu.length() - 3 - 6 - 4; // secureAdsuLength - sizeof(tpci,apci,scf) - sizeof(seqNum) - sizeof(mac) + CemiFrame plainFrame(plainApduLength); + // Decrypt secure APDU + if (decodeSecureApdu(apdu, plainFrame.apdu(), secCtrl)) + { + // Process decrypted inner APDU + ApplicationLayer::dataGroupConfirm(ack, hopType, priority, tsap, plainFrame.apdu(), secCtrl, status); + } + return; + } + + ApplicationLayer::dataGroupConfirm(ack, hopType, priority, tsap, apdu, status); +} + +void SecureApplicationLayer::dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) +{ + println("dataBroadcastIndication"); + + if (apdu.type() == SecureService) + { + // Will be filled in by decodeSecureApdu() + SecurityControl secCtrl; + + // Decrypt secure APDU + // Somehow ugly that we need to know the size in advance here at this point + uint16_t plainApduLength = apdu.length() - 3 - 6 - 4; // secureAdsuLength - sizeof(tpci,apci,scf) - sizeof(seqNum) - sizeof(mac) + CemiFrame plainFrame(plainApduLength); + // Decrypt secure APDU + if (decodeSecureApdu(apdu, plainFrame.apdu(), secCtrl)) + { + // Process decrypted inner APDU + ApplicationLayer::dataBroadcastIndication(hopType, priority, source, plainFrame.apdu(), secCtrl); + } + return; + } + + ApplicationLayer::dataBroadcastIndication(hopType, priority, source, apdu); +} + +void SecureApplicationLayer::dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, bool status) +{ + println("dataBroadcastConfirm"); + + if (apdu.type() == SecureService) + { + // We do not care about confirmations of our sync communication + if (isSyncService(apdu)) + { + return; + } + + // Will be filled in by decodeSecureApdu() + SecurityControl secCtrl; + + // Decrypt secure APDU + // Somehow ugly that we need to know the size in advance here at this point + uint16_t plainApduLength = apdu.length() - 3 - 6 - 4; // secureAdsuLength - sizeof(tpci,apci,scf) - sizeof(seqNum) - sizeof(mac) + CemiFrame plainFrame(plainApduLength); + // Decrypt secure APDU + if (decodeSecureApdu(apdu, plainFrame.apdu(), secCtrl)) + { + // Process decrypted inner APDU + ApplicationLayer::dataBroadcastConfirm(ack, hopType, priority, plainFrame.apdu(), secCtrl, status); + } + return; + } + ApplicationLayer::dataBroadcastConfirm(ack, hopType, priority, apdu, status); +} + +void SecureApplicationLayer::dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) +{ + println("dataSystemBroadcastIndication"); + + if (apdu.type() == SecureService) + { + // Will be filled in by decodeSecureApdu() + SecurityControl secCtrl; + + // Decrypt secure APDU + // Somehow ugly that we need to know the size in advance here at this point + uint16_t plainApduLength = apdu.length() - 3 - 6 - 4; // secureAdsuLength - sizeof(tpci,apci,scf) - sizeof(seqNum) - sizeof(mac) + CemiFrame plainFrame(plainApduLength); + // Decrypt secure APDU + if (decodeSecureApdu(apdu, plainFrame.apdu(), secCtrl)) + { + // Process decrypted inner APDU + ApplicationLayer::dataSystemBroadcastIndication(hopType, priority, source, plainFrame.apdu(), secCtrl); + } + return; + } + + ApplicationLayer::dataSystemBroadcastIndication(hopType, priority, source, apdu); +} + +void SecureApplicationLayer::dataSystemBroadcastConfirm(HopCountType hopType, Priority priority, APDU& apdu, bool status) +{ + println("dataSystemBroadcastConfirm"); + + if (apdu.type() == SecureService) + { + // We do not care about confirmations of our sync communication + if (isSyncService(apdu)) + { + return; + } + + // Will be filled in by decodeSecureApdu() + SecurityControl secCtrl; + + // Decrypt secure APDU + // Somehow ugly that we need to know the size in advance here at this point + uint16_t plainApduLength = apdu.length() - 3 - 6 - 4; // secureAdsuLength - sizeof(tpci,apci,scf) - sizeof(seqNum) - sizeof(mac) + CemiFrame plainFrame(plainApduLength); + // Decrypt secure APDU + if (decodeSecureApdu(apdu, plainFrame.apdu(), secCtrl)) + { + // Process decrypted inner APDU + ApplicationLayer::dataSystemBroadcastConfirm(hopType, priority, plainFrame.apdu(), secCtrl, status); + } + return; + } + + ApplicationLayer::dataSystemBroadcastConfirm(hopType, priority, apdu, status); +} + +void SecureApplicationLayer::dataIndividualIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) +{ + println("dataIndividualIndication"); + + if (apdu.type() == SecureService) + { + // Will be filled in by decodeSecureApdu() + SecurityControl secCtrl; + + // Decrypt secure APDU + // Somehow ugly that we need to know the size in advance here at this point + uint16_t plainApduLength = apdu.length() - 3 - 6 - 4; // secureAdsuLength - sizeof(tpci,apci,scf) - sizeof(seqNum) - sizeof(mac) + CemiFrame plainFrame(plainApduLength); + // Decrypt secure APDU + if (decodeSecureApdu(apdu, plainFrame.apdu(), secCtrl)) + { + // Process decrypted inner APDU + ApplicationLayer::dataIndividualIndication(hopType, priority, source, plainFrame.apdu(), secCtrl); + } + return; + } + + ApplicationLayer::dataIndividualIndication(hopType, priority, source, apdu); +} + +void SecureApplicationLayer::dataIndividualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t source, APDU& apdu, bool status) +{ + println("dataIndividualConfirm"); + + if (apdu.type() == SecureService) + { + // We do not care about confirmations of our sync communication + if (isSyncService(apdu)) + { + return; + } + + // Will be filled in by decodeSecureApdu() + SecurityControl secCtrl; + + // Decrypt secure APDU + // Somehow ugly that we need to know the size in advance here at this point + uint16_t plainApduLength = apdu.length() - 3 - 6 - 4; // secureAdsuLength - sizeof(tpci,apci,scf) - sizeof(seqNum) - sizeof(mac) + CemiFrame plainFrame(plainApduLength); + // Decrypt secure APDU + if (decodeSecureApdu(apdu, plainFrame.apdu(), secCtrl)) + { + // Process decrypted inner APDU + ApplicationLayer::dataIndividualConfirm(ack, hopType, priority, source, apdu, secCtrl, status); + } + return; + } + else + { + ApplicationLayer::dataIndividualConfirm(ack, hopType, priority, source, apdu, status); + } +} + +void SecureApplicationLayer::dataConnectedIndication(Priority priority, uint16_t tsap, APDU& apdu) +{ + println("dataConnectedIndication"); + + if (apdu.type() == SecureService) + { + // Will be filled in by decodeSecureApdu() + SecurityControl secCtrl; + + // Decrypt secure APDU + // Somehow ugly that we need to know the size in advance here at this point + uint16_t plainApduLength = apdu.length() - 3 - 6 - 4; // secureAdsuLength - sizeof(tpci,apci,scf) - sizeof(seqNum) - sizeof(mac) + CemiFrame plainFrame(plainApduLength); + // Decrypt secure APDU + if (decodeSecureApdu(apdu, plainFrame.apdu(), secCtrl)) + { + // Process decrypted inner APDU + ApplicationLayer::dataConnectedIndication(priority, tsap, plainFrame.apdu(), secCtrl); + } + return; + } + + ApplicationLayer::dataConnectedIndication(priority, tsap, apdu); +} + +void SecureApplicationLayer::dataConnectedConfirm(uint16_t tsap) +{ + // Just the confirmation issued by the transport layer in case of T_DATA_CONNECTED + ApplicationLayer::dataConnectedConfirm(tsap); +} + +/* to transport layer */ + +void SecureApplicationLayer::dataGroupRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl& secCtrl) +{ + println("dataGroupRequest"); + + if (secCtrl.dataSecurity != DataSecurity::none) + { + apdu.frame().sourceAddress(_deviceObj.induvidualAddress()); + apdu.frame().destinationAddress(_addrTab.getGroupAddress(tsap)); + apdu.frame().addressType(GroupAddress); + + uint16_t secureApduLength = apdu.length() + 3 + 6 + 4; // 3(TPCI,APCI,SCF) + sizeof(seqNum) + apdu.length() + 4 + CemiFrame secureFrame(secureApduLength); + // create secure APDU + if (createSecureApdu(apdu, secureFrame.apdu(), secCtrl)) + { + ApplicationLayer::dataGroupRequest(ack, hopType, priority, tsap, secureFrame.apdu(), secCtrl); + } + return; + } + + ApplicationLayer::dataGroupRequest(ack, hopType, priority, tsap, apdu, secCtrl); +} + +void SecureApplicationLayer::dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl& secCtrl) +{ + println("dataBroadcastRequest"); + + if (secCtrl.dataSecurity != DataSecurity::none) + { + apdu.frame().sourceAddress(_deviceObj.induvidualAddress()); + apdu.frame().destinationAddress(0x0000); + apdu.frame().addressType(GroupAddress); + apdu.frame().systemBroadcast(Broadcast); + + uint16_t secureApduLength = apdu.length() + 3 + 6 + 4; // 3(TPCI,APCI,SCF) + sizeof(seqNum) + apdu.length() + 4 + CemiFrame secureFrame(secureApduLength); + // create secure APDU + if (createSecureApdu(apdu, secureFrame.apdu(), secCtrl)) + { + ApplicationLayer::dataBroadcastRequest(ack, hopType, SystemPriority, secureFrame.apdu(), secCtrl); + } + return; + } + + ApplicationLayer::dataBroadcastRequest(ack, hopType, SystemPriority, apdu, secCtrl); +} + +void SecureApplicationLayer::dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl& secCtrl) +{ + println("dataSystemBroadcastRequest"); + + if (secCtrl.dataSecurity != DataSecurity::none) + { + apdu.frame().sourceAddress(_deviceObj.induvidualAddress()); + apdu.frame().destinationAddress(0x0000); + apdu.frame().addressType(GroupAddress); + apdu.frame().systemBroadcast(SysBroadcast); + + uint16_t secureApduLength = apdu.length() + 3 + 6 + 4; // 3(TPCI,APCI,SCF) + sizeof(seqNum) + apdu.length() + 4 + CemiFrame secureFrame(secureApduLength); + // create secure APDU + if (createSecureApdu(apdu, secureFrame.apdu(), secCtrl)) + { + ApplicationLayer::dataSystemBroadcastRequest(ack, hopType, SystemPriority, secureFrame.apdu(), secCtrl); + } + return; + } + + ApplicationLayer::dataSystemBroadcastRequest(ack, hopType, SystemPriority, apdu, secCtrl); +} + +void SecureApplicationLayer::dataIndividualRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t destination, APDU& apdu, const SecurityControl& secCtrl) +{ + println("dataIndividualRequest"); + + if (secCtrl.dataSecurity != DataSecurity::none) + { + apdu.frame().sourceAddress(_deviceObj.induvidualAddress()); + apdu.frame().destinationAddress(destination); + apdu.frame().addressType(InduvidualAddress); + + uint16_t secureApduLength = apdu.length() + 3 + 6 + 4; // 3(TPCI,APCI,SCF) + sizeof(seqNum) + apdu.length() + 4 + CemiFrame secureFrame(secureApduLength); + // create secure APDU + if (createSecureApdu(apdu, secureFrame.apdu(), secCtrl)) + { + ApplicationLayer::dataIndividualRequest(ack, hopType, priority, destination, secureFrame.apdu(), secCtrl); + } + return; + } + + ApplicationLayer::dataIndividualRequest(ack, hopType, priority, destination, apdu, secCtrl); +} + +void SecureApplicationLayer::dataConnectedRequest(uint16_t tsap, Priority priority, APDU& apdu, const SecurityControl &secCtrl) +{ + println("dataConnectedRequest"); + + if (secCtrl.dataSecurity != DataSecurity::none) + { + apdu.frame().sourceAddress(_deviceObj.induvidualAddress()); + apdu.frame().destinationAddress(_transportLayer->getConnectionAddress()); + apdu.frame().addressType(InduvidualAddress); + + uint16_t secureApduLength = apdu.length() + 3 + 6 + 4; // 3(TPCI,APCI,SCF) + sizeof(seqNum) + apdu.length() + 4 + CemiFrame secureFrame(secureApduLength); + // create secure APDU + if (createSecureApdu(apdu, secureFrame.apdu(), secCtrl)) + { + ApplicationLayer::dataConnectedRequest(tsap, priority, secureFrame.apdu(), secCtrl); + } + return; + } + + // apdu must be valid until it was confirmed + ApplicationLayer::dataConnectedRequest(tsap, priority, apdu, secCtrl); +} + +void SecureApplicationLayer::encryptAesCbc(uint8_t* buffer, uint16_t bufLen, const uint8_t* iv, const uint8_t* key) +{ + // Use zeroes as IV for first round + uint8_t zeroIv[16] = {0x00}; + + struct AES_ctx ctx; + AES_init_ctx_iv(&ctx, key, zeroIv); + + // Now encrypt first block B0. + AES_CBC_encrypt_buffer(&ctx, (uint8_t*) iv, 16); + + // Encrypt remaining buffer + AES_CBC_encrypt_buffer(&ctx, buffer, bufLen); +} + +void SecureApplicationLayer::xcryptAesCtr(uint8_t* buffer, uint16_t bufLen, const uint8_t* iv, const uint8_t* key) +{ + struct AES_ctx ctx; + + AES_init_ctx_iv(&ctx, key, iv); + + AES_CTR_xcrypt_buffer(&ctx, buffer, bufLen); +} + +uint32_t SecureApplicationLayer::calcAuthOnlyMac(uint8_t* apdu, uint8_t apduLength, const uint8_t* key, uint8_t* iv, uint8_t* ctr0) +{ + uint16_t bufLen = 2 + apduLength; // 2 bytes for the length field (uint16_t) + // AES-128 operates on blocks of 16 bytes, add padding + uint16_t bufLenPadded = (bufLen + 15) / 16 * 16; + uint8_t buffer[bufLenPadded]; + // Make sure to have zeroes everywhere, because of the padding + memset(buffer, 0x00, bufLenPadded); + + uint8_t* pBuf = buffer; + + pBuf = pushWord(apduLength, pBuf); + pBuf = pushByteArray(apdu, apduLength, pBuf); + + encryptAesCbc(buffer, bufLenPadded, iv, key); + xcryptAesCtr(buffer, 4, ctr0, key); // 4 bytes only for the MAC + + uint32_t mac; + popInt(mac, &buffer[0]); + + return mac; +} + +uint32_t SecureApplicationLayer::calcConfAuthMac(uint8_t* associatedData, uint16_t associatedDataLength, + uint8_t* apdu, uint8_t apduLength, + const uint8_t* key, uint8_t* iv) +{ + uint16_t bufLen = 2 + associatedDataLength + apduLength; // 2 bytes for the length field (uint16_t) + // AES-128 operates on blocks of 16 bytes, add padding + uint16_t bufLenPadded = (bufLen + 15) / 16 * 16; + uint8_t buffer[bufLenPadded]; + // Make sure to have zeroes everywhere, because of the padding + memset(buffer, 0x00, bufLenPadded); + + uint8_t* pBuf = buffer; + + pBuf = pushWord(associatedDataLength, pBuf); + pBuf = pushByteArray(associatedData, associatedDataLength, pBuf); + pBuf = pushByteArray(apdu, apduLength, pBuf); + + encryptAesCbc(buffer, bufLenPadded, iv, key); + + uint32_t mac; + popInt(mac, &buffer[bufLenPadded - 16]); // bufLenPadded has a guaranteed minimum size of 16 bytes + + return mac; +} + +void SecureApplicationLayer::block0(uint8_t* buffer, uint8_t* seqNum, uint16_t indSrcAddr, uint16_t dstAddr, bool dstAddrIsGroupAddr, uint8_t extFrameFormat, uint8_t tpci, uint8_t apci, uint8_t payloadLength) +{ + uint8_t* pBuf = buffer; + pBuf = pushByteArray(seqNum, 6, pBuf); + pBuf = pushWord(indSrcAddr, pBuf); + pBuf = pushWord(dstAddr, pBuf); + pBuf = pushByte(0x00, pBuf); // FT: frametype + pBuf = pushByte( (dstAddrIsGroupAddr ? 0x80 : 0x00) | (extFrameFormat & 0xf), pBuf); // AT: address type + pBuf = pushByte(tpci, pBuf); // TPCI + pBuf = pushByte(apci, pBuf); // APCI // draft spec shows something different! + pBuf = pushByte(0x00, pBuf); // Reserved: fixed 0x00 (really?) + pBuf = pushByte(payloadLength, pBuf); // Payload length +} + +void SecureApplicationLayer::blockCtr0(uint8_t* buffer, uint8_t* seqNum, uint16_t indSrcAddr, uint16_t dstAddr) +{ + uint8_t* pBuf = buffer; + pBuf = pushByteArray(seqNum, 6, pBuf); + pBuf = pushWord(indSrcAddr, pBuf); + pBuf = pushWord(dstAddr, pBuf); + pBuf = pushInt(0x00000000, pBuf); + pBuf = pushByte(0x01, pBuf); +} + +uint16_t SecureApplicationLayer::groupAddressIndex(uint16_t groupAddr) +{ + return _addrTab.getTsap(groupAddr); +} + +const uint8_t* SecureApplicationLayer::securityKey(uint16_t addr, bool isGroupAddress) +{ + if (isGroupAddress) + { + uint16_t gaIndex = groupAddressIndex(addr); + if (gaIndex > 0) + return _secIfObj.groupKey(gaIndex); + } + else + { + uint16_t iaIndex = _secIfObj.indAddressIndex(addr); + if (iaIndex > 0) + return _secIfObj.p2pKey(iaIndex); + } + + return nullptr; +} + +// returns next outgoing sequence number for secure communication +uint64_t SecureApplicationLayer::nextSequenceNumber(bool toolAccess) +{ + return toolAccess ? _sequenceNumberToolAccess : _sequenceNumber; +} + +// stores next outgoing sequence number for secure communication +void SecureApplicationLayer::updateSequenceNumber(bool toolAccess, uint64_t seqNum) +{ + if (toolAccess) + { + _sequenceNumberToolAccess = seqNum; + } + else + { + _sequenceNumber = seqNum; + } + + // Also update the properties accordingly + _secIfObj.setSequenceNumber(toolAccess, seqNum); +} + +uint64_t SecureApplicationLayer::lastValidSequenceNumber(bool toolAccess, uint16_t srcAddr) +{ + if (toolAccess) + { + // TODO: check if we really have to support multiple tools at the same time + if (srcAddr == _deviceObj.induvidualAddress()) + return _sequenceNumberToolAccess; + return _lastValidSequenceNumberTool; + } + else + { + return _secIfObj.getLastValidSequenceNumber(srcAddr); + } + + return 0; +} + +void SecureApplicationLayer::updateLastValidSequence(bool toolAccess, uint16_t remoteAddr, uint64_t seqNo) +{ + if (toolAccess) + // TODO: check if we really have to support multiple tools at the same time + _lastValidSequenceNumberTool = seqNo; + else + { + _secIfObj.setLastValidSequenceNumber(remoteAddr, seqNo); + } +} + +void SecureApplicationLayer::sendSyncRequest(uint16_t dstAddr, bool dstAddrIsGroupAddr, const SecurityControl &secCtrl, bool systemBcast) +{ + if (secCtrl.dataSecurity != DataSecurity::authConf) + { + println("sync.req is always sent with auth+conf!"); + return; + } + + _syncReqBroadcastOutgoing = (dstAddr == 0x0000) && dstAddrIsGroupAddr; + + // use random number in SyncResponse + uint64_t challenge = getRandomNumber(); + + uint8_t asdu[6]; + sixBytesFromUInt64(challenge, &asdu[0]); + + CemiFrame request(2 + 6 + sizeof(asdu) + 4); // 2 bytes (APCI, SCF) + 6 bytes (SeqNum) + 6 bytes (challenge) + 4 bytes (MAC) + // Note: additional TPCI byte is already handled internally! + + uint8_t tpci = 0; + if (!_syncReqBroadcastOutgoing) + { + if (isConnected()) + { + tpci |= 0x40 | _transportLayer->getTpciSeqNum(); // get next TPCI sequence number for MAC calculation from TL (T_DATA_CONNECTED) + } + } + print("sendSyncRequest: TPCI: "); + println(tpci, HEX); + + if(secure(request.data() + APDU_LPDU_DIFF, kSecureSyncRequest, _deviceObj.induvidualAddress(), dstAddr, dstAddrIsGroupAddr, tpci, asdu, sizeof(asdu), secCtrl, systemBcast)) + { + println("SyncRequest: "); + request.apdu().printPDU(); + + if (_syncReqBroadcastOutgoing) + { + _transportLayer->dataBroadcastRequest(AckType::AckDontCare, HopCountType::NetworkLayerParameter, Priority::SystemPriority, request.apdu()); + } + else + { + // either send on T_DATA_INDIVIDUAL or T_DATA_CONNECTED + if (isConnected()) + { + _transportLayer->dataConnectedRequest(getConnectedTsasp(), SystemPriority, request.apdu()); + } + else + { + // Send encrypted SyncResponse message using T_DATA_INDIVIDUAL + _transportLayer->dataIndividualRequest(AckType::AckDontCare, NetworkLayerParameter, SystemPriority, dstAddr, request.apdu()); + } + } + + Addr toAddr = _syncReqBroadcastOutgoing ? (Addr)GrpAddr(0) : (Addr)IndAddr(dstAddr); + _pendingOutgoingSyncRequests.insertOrAssign(toAddr, challenge); + } + else + { + println("SyncRequest: failure during encryption"); + } +} + +void SecureApplicationLayer::sendSyncResponse(uint16_t dstAddr, bool dstAddrIsGroupAddr, const SecurityControl &secCtrl, uint64_t remoteNextSeqNum, bool systemBcast) +{ + if (secCtrl.dataSecurity != DataSecurity::authConf) + { + println("sync.res is always sent with auth+conf!"); + return; + } + + uint64_t ourNextSeqNum = nextSequenceNumber(secCtrl.toolAccess); + + uint8_t asdu[12]; + sixBytesFromUInt64(ourNextSeqNum, &asdu[0]); + sixBytesFromUInt64(remoteNextSeqNum, &asdu[6]); + + CemiFrame response(2 + 6 + sizeof(asdu) + 4); // 2 bytes (APCI, SCF) + 6 bytes (SeqNum) + 12 bytes + 4 bytes (MAC) + // Note: additional TPCI byte is already handled internally! + + uint8_t tpci = 0; + if (!_syncReqBroadcastIncoming) + { + if (isConnected()) + { + tpci |= 0x40 | _transportLayer->getTpciSeqNum(); // get next TPCI sequence number for MAC calculation from TL (T_DATA_CONNECTED) + } + } + print("sendSyncResponse: TPCI: "); + println(tpci, HEX); + + if(secure(response.data() + APDU_LPDU_DIFF, kSecureSyncResponse, _deviceObj.induvidualAddress(), dstAddr, dstAddrIsGroupAddr, tpci, asdu, sizeof(asdu), secCtrl, systemBcast)) + { + _lastSyncRes = millis(); + + println("SyncResponse: "); + response.apdu().printPDU(); + + if (_syncReqBroadcastIncoming) + { + _transportLayer->dataBroadcastRequest(AckType::AckDontCare, HopCountType::NetworkLayerParameter, Priority::SystemPriority, response.apdu()); + } + else + { + // either send on T_DATA_INDIVIDUAL or T_DATA_CONNECTED + if (isConnected()) + { + _transportLayer->dataConnectedRequest(getConnectedTsasp(), SystemPriority, response.apdu()); + } + else + { + // Send encrypted SyncResponse message using T_DATA_INDIVIDUAL + _transportLayer->dataIndividualRequest(AckType::AckDontCare, NetworkLayerParameter, SystemPriority, dstAddr, response.apdu()); + } + } + } + else + { + println("SyncResponse: failure during encryption"); + } +} + +void SecureApplicationLayer::receivedSyncRequest(uint16_t srcAddr, uint16_t dstAddr, bool dstAddrIsGroupAddr, const SecurityControl &secCtrl, uint8_t* seqNum, uint64_t challenge, bool systemBcast) +{ + println("Received SyncRequest:"); + + uint64_t nextRemoteSeqNum = sixBytesToUInt64(seqNum); + uint64_t nextSeqNum = 1 + lastValidSequenceNumber(secCtrl.toolAccess, srcAddr); + + if (nextRemoteSeqNum > nextSeqNum) + { + updateLastValidSequence(secCtrl.toolAccess, srcAddr, nextRemoteSeqNum - 1); + nextSeqNum = nextRemoteSeqNum; + } + + _syncReqBroadcastIncoming = (dstAddr == 0x0000) && dstAddrIsGroupAddr; + + // Remember challenge for securing the sync.res later + _pendingIncomingSyncRequests.insertOrAssign(_syncReqBroadcastIncoming ? (Addr) GrpAddr(0) : (Addr) IndAddr(srcAddr), challenge); + + uint16_t toAddr = _syncReqBroadcastIncoming ? dstAddr : srcAddr; + bool toIsGroupAddress = _syncReqBroadcastIncoming; + sendSyncResponse(toAddr, toIsGroupAddress, secCtrl, nextSeqNum, systemBcast); +} + +void SecureApplicationLayer::receivedSyncResponse(uint16_t remote, const SecurityControl &secCtrl, uint8_t* plainApdu) +{ + println("Received SyncResponse:"); + + if (_syncReqBroadcastOutgoing) + { + if (_pendingOutgoingSyncRequests.get(GrpAddr(0)) == nullptr) + { + println("Cannot handle sync.res without pending sync.req! (broadcast/systembroadcast)"); + return; + } + } + else + { + if (_pendingOutgoingSyncRequests.get(IndAddr(remote)) == nullptr) + { + println("Cannot handle sync.res without pending sync.req!"); + return; + } + } + + // Bytes 0-5 in the "APDU" buffer contain the remote sequence number + // Bytes 6-11 in the "APDU" buffer contain the local sequence number + uint64_t remoteSeq = sixBytesToUInt64(plainApdu + 0); + uint64_t localSeq = sixBytesToUInt64(plainApdu + 6); + + uint64_t last = lastValidSequenceNumber(secCtrl.toolAccess, remote); + if (remoteSeq - 1 > last) + { + //logger.debug("sync.res update {} last valid {} seq -> {}", remote, toolAccess ? "tool access" : "p2p", remoteSeq -1); + updateLastValidSequence(secCtrl.toolAccess, remote, remoteSeq - 1); + } + + uint64_t next = nextSequenceNumber(secCtrl.toolAccess); + if (localSeq > next) { + //logger.debug("sync.res update local next {} seq -> {}", toolAccess ? "tool access" : "p2p", localSeq); + updateSequenceNumber(secCtrl.toolAccess, localSeq); + } + + Addr remoteAddr = _syncReqBroadcastOutgoing ? (Addr)GrpAddr(0) : (Addr)IndAddr(remote); + _pendingOutgoingSyncRequests.erase(remoteAddr); +} + +bool SecureApplicationLayer::decrypt(uint8_t* plainApdu, uint16_t plainApduLength, uint16_t srcAddr, uint16_t dstAddr, bool dstAddrIsGroupAddr, uint8_t tpci, uint8_t* secureAsdu, SecurityControl& secCtrl, bool systemBcast) +{ + const uint8_t* pBuf; + uint8_t scf; + + pBuf = popByte(scf, secureAsdu); + + bool toolAccess = ((scf & 0x80) == 0x80); + bool systemBroadcast = ((scf & 0x08) == 0x08); + uint8_t sai = (scf >> 4) & 0x07; // sai can only be 0x0 (CCM auth only) or 0x1 (CCM with auth+conf), other values are reserved + bool authOnly = ( sai == 0); + uint8_t service = (scf & 0x07); // only 0x0 (S-A_Data-PDU), 0x2 (S-A_Sync_Req-PDU) or 0x3 (S-A_Sync_Rsp-PDU) are valid values + + if (systemBroadcast != systemBcast) + { + println("SBC flag in SCF does not match actual communication mode!"); + } + + secCtrl.toolAccess = toolAccess; + secCtrl.dataSecurity = authOnly ? DataSecurity::auth : DataSecurity::authConf; + + bool syncReq = service == kSecureSyncRequest; + bool syncRes = service == kSecureSyncResponse; + + //const uint8_t* key = dstAddrIsGroupAddr ? securityKey(dstAddr, dstAddrIsGroupAddr) : toolAccess ? toolKey() : securityKey(srcAddr, false); + const uint8_t* key = dstAddrIsGroupAddr && (dstAddr != 0) ? securityKey(dstAddr, dstAddrIsGroupAddr) : toolAccess ? _secIfObj.toolKey() : securityKey(srcAddr, false); + if (key == nullptr) + { + print("Error: No key found. toolAccess: "); + println(toolAccess ? "true" : "false"); + return false; + } + + uint8_t seqNum[6]; + pBuf = popByteArray(seqNum, 6, pBuf); + uint64_t receivedSeqNumber = sixBytesToUInt64(seqNum); + + // Provide array for KNX serial number if it is a SyncRequest + // DataService and SyncResponse do not use this variable. + uint8_t knxSerialNumber[6]; + + uint16_t remainingPlainApduLength = plainApduLength; + + if (service == kSecureDataPdu) + { + if (srcAddr != _deviceObj.induvidualAddress()) + { + uint64_t expectedSeqNumber = lastValidSequenceNumber(toolAccess, srcAddr) + 1; + + if (receivedSeqNumber < expectedSeqNumber) + { + // security failure + print("security failure: received seqNum: "); + print(receivedSeqNumber, HEX); + print(" < expected seqNum: "); + print(expectedSeqNumber, HEX); + return false; + } + } + } + else if(syncReq) + { + pBuf = popByteArray(knxSerialNumber, 6, pBuf); + remainingPlainApduLength -= 6; + + // ignore sync.reqs not addressed to us + if (!memcmp(knxSerialNumber, _deviceObj.propertyData(PID_SERIAL_NUMBER), 6)) + { + uint8_t emptySerialNumber[6] = {0}; + if (systemBroadcast || dstAddr != _deviceObj.induvidualAddress() || !memcmp(knxSerialNumber, emptySerialNumber, 6)) + return false; + } + + // if responded to another request within the last 1 second, ignore + if ((millis() - _lastSyncRes) < 1000) + { + return false; + } + } + else if (syncRes) + { + // fetch challenge depending on srcAddr to handle multiple requests + uint64_t *challenge = _pendingOutgoingSyncRequests.get(IndAddr(srcAddr)); + if (challenge == nullptr) + { + println("Cannot find matching challenge for source address!"); + return false; + } + + uint8_t _challengeSixBytes[6]; + sixBytesFromUInt64(*challenge, _challengeSixBytes); + // in a sync.res, seq actually contains our challenge from sync.req xored with a random value + // extract the random value and store it in seqNum to use it for block0 and ctr0 + for (uint8_t i = 0; i < sizeof(seqNum); i++) + { + seqNum[i] ^= _challengeSixBytes[i]; + } + } + + pBuf = popByteArray(plainApdu, remainingPlainApduLength, pBuf); + + // No LTE-HEE for now + // Data Secure always uses extended frame format with APDU length > 15 octets (long messages), no standard frames + uint8_t extendedFrameFormat = 0; + // Clear IV buffer + uint8_t iv[16] = {0x00}; + // Create first block B0 for AES CBC MAC calculation, used as IV later + /* + printHex("seq: ", seqNum, 6); + printHex("src: ", (uint8_t*) &srcAddr, 2); + printHex("dst: ", (uint8_t*) &dstAddr, 2); + print("dstAddrisGroup: ");println(dstAddrIsGroupAddr ? "true" : "false"); +*/ + block0(iv, seqNum, srcAddr, dstAddr, dstAddrIsGroupAddr, extendedFrameFormat, tpci | (SecureService >> 8), SecureService & 0x00FF, remainingPlainApduLength); + + // Clear block counter0 buffer + uint8_t ctr0[16] = {0x00}; + // Create first block for block counter 0 + blockCtr0(ctr0, seqNum, srcAddr, dstAddr); + + uint32_t mac; + pBuf = popInt(mac, pBuf); + + if (authOnly) + { + // APDU is already plain, no decryption needed + + // Only check the MAC + uint32_t calculatedMac = calcAuthOnlyMac(plainApdu, remainingPlainApduLength, key, iv, ctr0); + if (calculatedMac != mac) + { + // security failure + print("security failure(auth): calculated MAC: "); + print(calculatedMac, HEX); + print(" != received MAC: "); + print(mac, HEX); + println("\n"); + + return false; + } + + memcpy(plainApdu, secureAsdu, remainingPlainApduLength); + } + else + { + // APDU is encrypted and needs decryption + + uint16_t bufLen = 4 + remainingPlainApduLength; + // AES-128 operates on blocks of 16 bytes, add padding + uint16_t bufLenPadded = (bufLen + 15) / 16 * 16; + uint8_t buffer[bufLenPadded]; + //uint8_t buffer[bufLen]; + // Make sure to have zeroes everywhere, because of the padding + memset(buffer, 0x00, bufLenPadded); + + pushInt(mac, &buffer[0]); + pushByteArray(plainApdu, remainingPlainApduLength, &buffer[4]); // apdu is still encrypted + + xcryptAesCtr(buffer, bufLenPadded, ctr0, key); + //xcryptAesCtr(buffer, bufLen, ctr0, key); + + uint32_t decryptedMac; + popInt(decryptedMac, &buffer[0]); + popByteArray(plainApdu, remainingPlainApduLength, &buffer[4]); // apdu is now decrypted (overwritten) + + // Do calculations for Auth+Conf + uint8_t associatedData[syncReq ? 7 : 1]; + associatedData[0] = scf; + if (syncReq) + { + memcpy(&associatedData[1], knxSerialNumber, 6); + } +/* + printHex("APDU--------->", plainApdu, remainingPlainApduLength); + printHex("Key---------->", key, 16); + printHex("ASSOC-------->", associatedData, sizeof(associatedData)); +*/ + uint32_t calculatedMac = calcConfAuthMac(associatedData, sizeof(associatedData), plainApdu, remainingPlainApduLength, key, iv); + if (calculatedMac != decryptedMac) + { + // security failure + print("security failure(conf+auth): calculated MAC: "); + print(calculatedMac, HEX); + print(" != decrypted MAC: "); + print(decryptedMac, HEX); + println("\n"); + + return false; + } + + // prevent a sync.req sent by us to trigger sync notification, this happens if we provide our own tool key + // for decryption above + if (syncReq && (srcAddr == _deviceObj.induvidualAddress())) + return false; + + if (syncReq) + { + uint64_t challenge = sixBytesToUInt64(&plainApdu[0]); + receivedSyncRequest(srcAddr, dstAddr, dstAddrIsGroupAddr, secCtrl, seqNum, challenge, systemBroadcast); + return false; + } + else if (syncRes) + { + receivedSyncResponse(srcAddr, secCtrl, plainApdu); + return false; + } + else + { + if (srcAddr == _deviceObj.induvidualAddress()) + { + print("Update our next "); + print(toolAccess ? "tool access" : ""); + print(" seq from "); + print(srcAddr, HEX); + print(" -> (+1) "); + println(receivedSeqNumber,HEX); + updateSequenceNumber(toolAccess, receivedSeqNumber + 1); + } + else + { + print("Update last valid "); + print(toolAccess ? "tool access" : ""); + print(" seq from "); + print(srcAddr, HEX); + print(" -> "); + println(receivedSeqNumber, HEX); + updateLastValidSequence(toolAccess, srcAddr, receivedSeqNumber); + } + } + } + + return true; +} + +bool SecureApplicationLayer::decodeSecureApdu(APDU& secureApdu, APDU& plainApdu, SecurityControl& secCtrl) +{ + // Decode secure APDU + + println("decodeSecureApdu: Secure APDU: "); + secureApdu.printPDU(); + + uint16_t srcAddress = secureApdu.frame().sourceAddress(); + uint16_t dstAddress = secureApdu.frame().destinationAddress(); + bool isDstAddrGroupAddr = secureApdu.frame().addressType() == GroupAddress; + bool isSystemBroadcast = secureApdu.frame().systemBroadcast(); + uint8_t tpci = secureApdu.frame().data()[TPDU_LPDU_DIFF]; // FIXME: when cEMI class is refactored, there might be additional info fields in cEMI [fixed TPDU_LPDU_DIFF] + print("decodeSecureApdu: TPCI: "); + println(tpci, HEX); + // Note: + // The TPCI is also included in the MAC calculation to provide authenticity for this field. + // However, a secure APDU (with a MAC) is only included in transport layer PDUs T_DATA_GROUP, T_DATA_TAG_GROUP, T_DATA_INDIVIDUAL, T_DATA_CONNECTED + // and not in T_CONNECT, T_DISCONNECT, T_ACK, T_NACK. + // This means the DATA/CONTROL flag is always 0(=DATA). The flag "NUMBERED" differentiates between T_DATA_INDIVIDUAL and T_DATA_CONNECTED. + // The seqNumber is only used in T_DATA_CONNECTED and 0 in case of T_DATA_GROUP and T_DATA_GROUP (leaving out T_DATA_TAG_GROUP). + // Summary: effectively only the "NUMBERED" flag (bit6) and the SeqNumber (bit5-2) are used from transport layer. + // In T_DATA_* services the bits1-0 of TPCI octet are used as bits9-8 for APCI type which is fixed to 0x03. SecureService APCI is 0x03F1. + + // FIXME: when cEMI class is refactored, there might be additional info fields in cEMI (fixed APDU_LPDU_DIFF) + // We are starting from TPCI octet (including): plainApdu.frame().data()+APDU_LPDU_DIFF + if (decrypt(plainApdu.frame().data()+APDU_LPDU_DIFF, plainApdu.length()+1, srcAddress, dstAddress, isDstAddrGroupAddr, tpci, secureApdu.data()+1, secCtrl, isSystemBroadcast)) + { + println("decodeSecureApdu: Plain APDU: "); + plainApdu.frame().apdu().printPDU(); + + return true; + } + + return false; +} + +bool SecureApplicationLayer::secure(uint8_t* buffer, uint16_t service, uint16_t srcAddr, uint16_t dstAddr, bool dstAddrIsGroupAddr, uint8_t tpci, + uint8_t* apdu, uint16_t apduLength, const SecurityControl& secCtrl, bool systemBcast) +{ + bool toolAccess = secCtrl.toolAccess; + bool confidentiality = secCtrl.dataSecurity == DataSecurity::authConf; + + if (toolAccess) + { + if (!confidentiality) + { + println("Error: tool access requires auth+conf security"); + return false; + } + if (dstAddrIsGroupAddr && dstAddr != 0) + { + println("Error: tool access requires individual address"); + return false; + } + } + + const uint8_t* key = toolAccess ? _secIfObj.toolKey() : securityKey(dstAddr, dstAddrIsGroupAddr); + if (key == nullptr) + { + print("Error: No key found. toolAccess: "); + println(toolAccess ? "true" : "false"); + return false; + } + + bool syncReq = service == kSecureSyncRequest; + bool syncRes = service == kSecureSyncResponse; + + tpci |= SecureService >> 8; // OR'ing upper two APCI bits + uint8_t apci = SecureService & 0x00FF; + uint8_t* pBuf = buffer; + pBuf = pushByte(tpci, pBuf); // TPCI + pBuf = pushByte(apci, pBuf); // APCI + + uint8_t scf; + scf = service; + scf |= toolAccess ? 0x80 : 0; + scf |= confidentiality ? 0x10 : 0; + scf |= systemBcast ? 0x8 : 0; + + pBuf = pushByte(scf, pBuf); // SCF + + uint64_t seqSend = nextSequenceNumber(toolAccess); + if (seqSend == 0) + println("0 is not a valid sequence number"); + + uint8_t seq[6]; + sixBytesFromUInt64(seqSend, seq); + if (!syncRes) + pBuf = pushByteArray(seq, 6, pBuf); // Sequence Number + + // Prepare associated data depending on service (SyncRequest, SyncResponse or just DataService) + uint8_t associatedData[syncReq ? 7 : 1]; + associatedData[0] = scf; + if (syncReq) + { + // TODO: NYI lookup serial number of target device for SBC sync.req + uint8_t remoteSerialNo[6] = {0}; + + uint8_t emptySerialNo[6] = {0}; + pBuf = pushByteArray(systemBcast ? remoteSerialNo : emptySerialNo, 6, pBuf); + pushByteArray(_deviceObj.propertyData(PID_SERIAL_NUMBER), 6, &associatedData[1]); + } + else if (syncRes) + { + // use random number in SyncResponse + uint64_t randomNumber = getRandomNumber(); + sixBytesFromUInt64(randomNumber, seq); + + Addr remote = _syncReqBroadcastIncoming ? (Addr)GrpAddr(0) : (Addr)IndAddr(dstAddr); + + // Get challenge from sync.req + uint64_t *challenge = _pendingIncomingSyncRequests.get(remote); + if (challenge == nullptr) + { + println("Cannot send sync.res without corresponding sync.req"); + return false; + } + else + { + _pendingIncomingSyncRequests.erase(remote); + } + uint8_t challengeSixBytes[6]; + sixBytesFromUInt64(*challenge, challengeSixBytes); + //printHex("Decrypted challenge: ", challengeSixBytes, 6); + + // Now XOR the new random SeqNum with the challenge from the SyncRequest + uint8_t rndXorChallenge[6]; + pushByteArray(seq, 6, rndXorChallenge); + for (uint8_t i = 0; i < sizeof(rndXorChallenge); i++) + { + rndXorChallenge[i] ^= challengeSixBytes[i]; + } + pBuf = pushByteArray(rndXorChallenge, 6, pBuf); + } + + // No LTE-HEE for now + // Data Secure always uses extended frame format with APDU length > 15 octets (long messages), no standard frames + uint8_t extendedFrameFormat = 0; + // Clear IV buffer + uint8_t iv[16] = {0x00}; + // Create first block B0 for AES CBC MAC calculation, used as IV later +/* + printHex("seq: ", seq, 6); + printHex("src: ", (uint8_t*) &srcAddr, 2); + printHex("dst: ", (uint8_t*) &dstAddr, 2); + print("dstAddrisGroup: ");println(dstAddrIsGroupAddr ? "true" : "false"); +*/ + block0(iv, seq, srcAddr, dstAddr, dstAddrIsGroupAddr, extendedFrameFormat, tpci, apci, apduLength); + + // Clear block counter0 buffer + uint8_t ctr0[16] = {0x00}; + // Create first block for block counter 0 + blockCtr0(ctr0, seq, srcAddr, dstAddr); + + if (confidentiality) + { + // Do calculations for Auth+Conf +/* + printHex("APDU--------->", apdu, apduLength); + printHex("Key---------->", key, 16); + printHex("ASSOC-------->", associatedData, sizeof(associatedData)); +*/ + uint32_t mac = calcConfAuthMac(associatedData, sizeof(associatedData), apdu, apduLength, key, iv); + + uint8_t tmpBuffer[4 + apduLength]; + pushInt(mac, tmpBuffer); + pushByteArray(apdu, apduLength, &tmpBuffer[4]); + + xcryptAesCtr(tmpBuffer, apduLength + 4, ctr0, key); // APDU + MAC (4 bytes) + + pBuf = pushByteArray(tmpBuffer + 4, apduLength, pBuf); // Encrypted APDU + pBuf = pushByteArray(tmpBuffer + 0, 4, pBuf); // Encrypted MAC + + //print("MAC(encrypted): "); + //println(*((uint32_t*)(tmpBuffer + 0)),HEX); + } + else + { + // Do calculations for AuthOnly + uint32_t tmpMac = calcAuthOnlyMac(apdu, apduLength, key, iv, ctr0); + + pBuf = pushByteArray(apdu, apduLength, pBuf); // Plain APDU + pBuf = pushInt(tmpMac, pBuf); // MAC + + print("MAC: "); + println(tmpMac, HEX); + } + + return true; +} + +bool SecureApplicationLayer::createSecureApdu(APDU& plainApdu, APDU& secureApdu, const SecurityControl& secCtrl) +{ + // Create secure APDU + + println("createSecureApdu: Plain APDU: "); + plainApdu.printPDU(); + + uint16_t srcAddress = plainApdu.frame().sourceAddress(); + uint16_t dstAddress = plainApdu.frame().destinationAddress(); + bool isDstAddrGroupAddr = plainApdu.frame().addressType() == GroupAddress; + bool isSystemBroadcast = plainApdu.frame().systemBroadcast(); + uint8_t tpci = 0x00; + if (isConnected()) + { + tpci |= 0x40 | _transportLayer->getTpciSeqNum(); // get next TPCI sequence number for MAC calculation from TL (T_DATA_CONNECTED) + } + print("createSecureApdu: TPCI: "); + println(tpci, HEX); + // Note: + // The TPCI is also included in the MAC calculation to provide authenticity for this field. + // However, a secure APDU (with a MAC) is only included in transport layer PDUs T_DATA_GROUP, T_DATA_TAG_GROUP, T_DATA_INDIVIDUAL, T_DATA_CONNECTED + // and not in T_CONNECT, T_DISCONNECT, T_ACK, T_NACK. + // This means the DATA/CONTROL flag is always 0(=DATA). The flag "NUMBERED" differentiates between T_DATA_INDIVIDUAL and T_DATA_CONNECTED. + // The seqNumber is only used in T_DATA_CONNECTED and 0 in case of T_DATA_GROUP and T_DATA_GROUP (leaving out T_DATA_TAG_GROUP). + // Summary: effectively only the "NUMBERED" flag (bit6) and the SeqNumber (bit5-2) are used from transport layer. + // In T_DATA_* services the bits1-0 of TPCI octet are used as bits9-8 for APCI type which is fixed to 0x03. SecureService APCI is 0x03F1. + + // FIXME: when cEMI class is refactored, there might be additional info fields in cEMI (fixed APDU_LPDU_DIFF) + // We are starting from TPCI octet (including): plainApdu.frame().data()+APDU_LPDU_DIFF + if(secure(secureApdu.frame().data()+APDU_LPDU_DIFF, kSecureDataPdu, srcAddress, dstAddress, isDstAddrGroupAddr, tpci, plainApdu.frame().data()+APDU_LPDU_DIFF, plainApdu.length()+1, secCtrl, isSystemBroadcast)) + { + print("Update our next "); + print(secCtrl.toolAccess ? "tool access" : ""); + print(" seq from "); + print(srcAddress, HEX); + print(" -> (+1) "); + println(nextSequenceNumber(secCtrl.toolAccess),HEX); + updateSequenceNumber(secCtrl.toolAccess, nextSequenceNumber(secCtrl.toolAccess) + 1); + + println("createSecureApdu: Secure APDU: "); + secureApdu.frame().apdu().printPDU(); + + return true; + } + + return false; +} + +void SecureApplicationLayer::clearFailureLog() +{ + println("clearFailureLog()"); +} + +void SecureApplicationLayer::getFailureCounters(uint8_t* data) +{ + memset(data, 0, 8); + println("getFailureCounters()"); +} + +uint8_t SecureApplicationLayer::getFromFailureLogByIndex(uint8_t index, uint8_t* data, uint8_t maxDataLen) +{ + print("getFromFailureLogByIndex(): Index: "); + println(index); + return 0; +} + +uint64_t SecureApplicationLayer::getRandomNumber() +{ + return 0x000102030405; // TODO: generate random number +} + +void SecureApplicationLayer::loop() +{ + // TODO: handle timeout of outgoing sync requests + //_pendingOutgoingSyncRequests +} + +bool SecureApplicationLayer::isSyncService(APDU& secureApdu) +{ + uint8_t scf = *(secureApdu.data()+1); + uint8_t service = (scf & 0x07); // only 0x0 (S-A_Data-PDU), 0x2 (S-A_Sync_Req-PDU) or 0x3 (S-A_Sync_Rsp-PDU) are valid values + + if ((service == kSecureSyncRequest) || (service == kSecureSyncResponse)) + { + return true; + } + + return false; +} +#endif diff --git a/src/knx/secure_application_layer.h b/src/knx/secure_application_layer.h new file mode 100644 index 0000000..856904e --- /dev/null +++ b/src/knx/secure_application_layer.h @@ -0,0 +1,154 @@ +#pragma once + +#include "application_layer.h" +#include +#include "knx_types.h" +#include "apdu.h" +#include "bits.h" +#include "simple_map.h" + +class DeviceObject; +class SecurityInterfaceObject; +class AssociationTableObject; +class AddressTableObject; +class BusAccessUnit; +/** + * This is an implementation of the application layer as specified in @cite knx:3/5/1. + * It provides methods for the BusAccessUnit to do different things and translates this + * call to an APDU and calls the correct method of the TransportLayer. + * It also takes calls from TransportLayer, decodes the submitted APDU and calls the coresponding + * methods of the BusAccessUnit class. + */ +class SecureApplicationLayer : public ApplicationLayer +{ + + public: + /** + * The constructor. + * @param assocTable The AssociationTable is used to translate between asap (i.e. group objects) and group addresses. + * @param bau methods are called here depending of the content of the APDU + */ + SecureApplicationLayer(DeviceObject& deviceObj, SecurityInterfaceObject& secIfObj, AssociationTableObject& assocTable, AddressTableObject& addrTab, BusAccessUnit& bau); + + void clearFailureLog(); + void getFailureCounters(uint8_t* data); + uint8_t getFromFailureLogByIndex(uint8_t index, uint8_t* data, uint8_t maxDataLen); + + // from transport layer + virtual void dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu) override; + virtual void dataGroupConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, + APDU& apdu, bool status) override; + virtual void dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) override; + virtual void dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, bool status) override; + virtual void dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) override; + virtual void dataSystemBroadcastConfirm(HopCountType hopType, Priority priority, APDU& apdu, bool status) override; + virtual void dataIndividualIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) override; + virtual void dataIndividualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status) override; + virtual void dataConnectedIndication(Priority priority, uint16_t tsap, APDU& apdu) override; + virtual void dataConnectedConfirm(uint16_t tsap) override; + + void loop(); + + protected: + // to transport layer + virtual void dataGroupRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl& secCtrl) override; + virtual void dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl& secCtrl) override; + virtual void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl& secCtrl) override; + virtual void dataIndividualRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t destination, APDU& apdu, const SecurityControl& secCtrl) override; + virtual void dataConnectedRequest(uint16_t tsap, Priority priority, APDU& apdu, const SecurityControl& secCtrl) override; // apdu must be valid until it was confirmed + + private: + + + enum class AddrType : uint8_t + { + group, + individual, + unknown + }; + + struct Addr + { + Addr() = default; + Addr(uint8_t addr) : addr{addr} {} + + uint16_t addr; + AddrType addrType{AddrType::unknown}; + + bool operator ==(const Addr &cmpAddr) const + { + if ((cmpAddr.addrType == AddrType::unknown) || (addrType == AddrType::unknown)) + { + println("Unknown address type detected!"); + return false; + } + return (cmpAddr.addr == addr) && (cmpAddr.addrType == addrType); + } + }; + + struct GrpAddr : Addr + { + GrpAddr() {addrType = AddrType::group;} + GrpAddr(uint8_t addr) : Addr{addr} {addrType = AddrType::group;} + }; + + struct IndAddr : Addr + { + IndAddr() { addrType = AddrType::individual; } + IndAddr(uint8_t addr) : Addr{addr} { addrType = AddrType::individual; } + }; + + uint32_t calcAuthOnlyMac(uint8_t* apdu, uint8_t apduLength, const uint8_t *key, uint8_t* iv, uint8_t* ctr0); + uint32_t calcConfAuthMac(uint8_t* associatedData, uint16_t associatedDataLength, uint8_t* apdu, uint8_t apduLength, const uint8_t* key, uint8_t* iv); + + void block0(uint8_t* buffer, uint8_t* seqNum, uint16_t indSrcAddr, uint16_t dstAddr, bool dstAddrIsGroupAddr, uint8_t extFrameFormat, uint8_t tpci, uint8_t apci, uint8_t payloadLength); + void blockCtr0(uint8_t* buffer, uint8_t* seqNum, uint16_t indSrcAddr, uint16_t dstAddr); + + const uint8_t* securityKey(uint16_t addr, bool isGroupAddress); + + uint16_t groupAddressIndex(uint16_t groupAddr); // returns 1-based index of address in group address table + uint16_t groupObjectIndex(uint16_t groupAddrIndex); // returns 1-based index of object in association table + + uint8_t groupObjectSecurity(uint16_t groupObjectIndex); + + uint64_t nextSequenceNumber(bool toolAccess); + void updateSequenceNumber(bool toolAccess, uint64_t seqNum); + + uint64_t lastValidSequenceNumber(bool toolAcces, uint16_t srcAddr); + void updateLastValidSequence(bool toolAccess, uint16_t remoteAddr, uint64_t seqNo); + + uint64_t getRandomNumber(); + + bool isSyncService(APDU& secureAsdu); + + void sendSyncRequest(uint16_t dstAddr, bool dstAddrIsGroupAddr, const SecurityControl &secCtrl, bool systemBcast); + void sendSyncResponse(uint16_t dstAddr, bool dstAddrIsGroupAddr, const SecurityControl &secCtrl, uint64_t remoteNextSeqNum, bool systemBcast); + void receivedSyncRequest(uint16_t srcAddr, uint16_t dstAddr, bool dstAddrIsGroupAddr, const SecurityControl &secCtrl, uint8_t* seq, uint64_t challenge, bool systemBcast); + void receivedSyncResponse(uint16_t remoteAddr, const SecurityControl &secCtrl, uint8_t* plainApdu); + + bool decrypt(uint8_t* plainApdu, uint16_t plainapduLength, uint16_t srcAddr, uint16_t dstAddr, bool dstAddrIsGroupAddr, uint8_t tpci, uint8_t* secureAsdu, SecurityControl &secCtrl, bool systemBcast); + bool secure(uint8_t* buffer, uint16_t service, uint16_t srcAddr, uint16_t dstAddr, bool dstAddrIsGroupAddr, uint8_t tpci, uint8_t* apdu, uint16_t apduLength, const SecurityControl &secCtrl, bool systemBcast); + + bool decodeSecureApdu(APDU& secureApdu, APDU& plainApdu, SecurityControl &secCtrl); + bool createSecureApdu(APDU& plainApdu, APDU& secureApdu, const SecurityControl &secCtrl); + + void encryptAesCbc(uint8_t* buffer, uint16_t bufLen, const uint8_t* iv, const uint8_t* key); + void xcryptAesCtr(uint8_t* buffer, uint16_t bufLen, const uint8_t* iv, const uint8_t* key); + + bool _syncReqBroadcastIncoming{false}; + bool _syncReqBroadcastOutgoing{false}; + uint32_t _lastSyncRes; + + Map _pendingOutgoingSyncRequests; // Store challenges for outgoing sync requests + Map _pendingIncomingSyncRequests; // Store challenges for incoming sync requests + + uint64_t _sequenceNumberToolAccess = 50; + uint64_t _sequenceNumber = 0; + + uint64_t _lastValidSequenceNumberTool = 0; + uint64_t _lastValidSequenceNumber = 0; + + SecurityInterfaceObject& _secIfObj; + DeviceObject& _deviceObj; + AddressTableObject& _addrTab; +}; diff --git a/src/knx/security_interface_object.cpp b/src/knx/security_interface_object.cpp new file mode 100644 index 0000000..92b67c4 --- /dev/null +++ b/src/knx/security_interface_object.cpp @@ -0,0 +1,542 @@ +#include "config.h" +#ifdef USE_DATASECURE + +#include +#include "security_interface_object.h" +#include "secure_application_layer.h" +#include "bits.h" +#include "data_property.h" +#include "callback_property.h" +#include "function_property.h" + +// Our FDSK. It is never changed from ETS. This is the permanent default tool key that is restored on every factory reset of the device. +const uint8_t SecurityInterfaceObject::_fdsk[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F }; +uint8_t SecurityInterfaceObject::_secReport[] = { 0x00, 0x00, 0x00 }; +uint8_t SecurityInterfaceObject::_secReportCtrl[] = { 0x00, 0x00, 0x00 }; + +SecurityInterfaceObject::SecurityInterfaceObject() +{ + Property* properties[] = + { + new DataProperty( PID_OBJECT_TYPE, false, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv0, (uint16_t)OT_SECURITY ), + new CallbackProperty(this, PID_LOAD_STATE_CONTROL, true, PDT_CONTROL, 1, ReadLv3 | WriteLv3, + // ReadCallback of PID_LOAD_STATE_CONTROL + [](SecurityInterfaceObject* obj, uint16_t start, uint8_t count, uint8_t* data) -> uint8_t { + if (start == 0) + return 1; + + data[0] = obj->_state; + return 1; + }, + // WriteCallback of PID_LOAD_STATE_CONTROL + [](SecurityInterfaceObject* obj, uint16_t start, uint8_t count, const uint8_t* data) -> uint8_t { + obj->loadEvent(data); + return 1; + }), + new FunctionProperty(this, PID_SECURITY_MODE, + // Command Callback of PID_SECURITY_MODE + [](SecurityInterfaceObject* obj, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) -> void { + uint8_t serviceId = data[1] & 0xff; + if (serviceId != 0) + { + resultData[0] = ReturnCodes::InvalidCommand; + resultLength = 1; + return; + } + if (length == 3) + { + uint8_t mode = data[2]; + if (mode > 1) + { + resultData[0] = ReturnCodes::DataVoid; + resultLength = 1; + return; + } + obj->setSecurityMode(mode == 1); + resultData[0] = ReturnCodes::Success; + resultData[1] = serviceId; + resultLength = 2; + return; + } + resultData[0] = ReturnCodes::GenericError; + resultLength = 1; + }, + // State Callback of PID_SECURITY_MODE + [](SecurityInterfaceObject* obj, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) -> void { + uint8_t serviceId = data[1] & 0xff; + if (serviceId != 0) + { + resultData[0] = ReturnCodes::InvalidCommand; + resultLength = 1; + return; + } + if (length == 2) + { + resultData[0] = ReturnCodes::Success; + resultData[1] = serviceId; + resultData[2] = obj->isSecurityModeEnabled() ? 1 : 0; + resultLength = 3; + return; + } + resultData[0] = ReturnCodes::GenericError; + resultLength = 1; + }), + new DataProperty( PID_P2P_KEY_TABLE, true, PDT_GENERIC_20, 1, ReadLv3 | WriteLv0 ), // written by ETS + new DataProperty( PID_GRP_KEY_TABLE, true, PDT_GENERIC_18, 50, ReadLv3 | WriteLv0 ), // written by ETS + new DataProperty( PID_SECURITY_INDIVIDUAL_ADDRESS_TABLE, true, PDT_GENERIC_08, 32, ReadLv3 | WriteLv0 ), // written by ETS + new FunctionProperty(this, PID_SECURITY_FAILURES_LOG, + // Command Callback of PID_SECURITY_FAILURES_LOG + [](SecurityInterfaceObject* obj, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) -> void { + if (length != 3) + { + resultData[0] = ReturnCodes::DataVoid; + resultLength = 1; + return; + } + uint8_t id = data[1]; + uint8_t info = data[2]; + if (id == 0 && info == 0) + { + obj->_secAppLayer->clearFailureLog(); + resultData[0] = ReturnCodes::Success; + resultData[1] = id; + resultLength = 2; + return; + } + resultData[0] = ReturnCodes::GenericError; + resultLength = 1; + }, + // State Callback of PID_SECURITY_FAILURES_LOG + [](SecurityInterfaceObject* obj, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) -> void { + if (length != 3) + { + resultData[0] = ReturnCodes::DataVoid; + resultLength = 1; + return; + } + uint8_t id = data[1]; + uint8_t info = data[2]; + + // failure counters + if (id == 0 && info == 0) + { + resultData[0] = ReturnCodes::Success; + resultData[1] = id; + resultData[2] = info; + obj->_secAppLayer->getFailureCounters(&resultData[3]); // Put 8 bytes in the buffer + resultLength = 3 + 8; + return; + } + // query latest failure by index + else if(id == 1) + { + uint8_t maxBufferSize = resultLength; // Remember the maximum buffer size of the buffer that is provided to us + uint8_t index = info; + uint8_t numBytes = obj->_secAppLayer->getFromFailureLogByIndex(index, &resultData[2], maxBufferSize); + if ( numBytes > 0) + { + resultData[0] = ReturnCodes::Success; + resultData[1] = id; + resultData[2] = index; + resultLength += numBytes; + resultLength = 3 + numBytes; + return; + } + resultData[0] = ReturnCodes::DataVoid; + resultData[1] = id; + resultLength = 2; + return; + } + resultData[0] = ReturnCodes::GenericError; + resultLength = 1; + }), + new DataProperty( PID_TOOL_KEY, true, PDT_GENERIC_16, 1, ReadLv3 | WriteLv0, (uint8_t*) _fdsk ), // default is FDSK // ETS changes this property during programming from FDSK to some random key! + new DataProperty( PID_SECURITY_REPORT, true, PDT_BITSET8, 1, ReadLv3 | WriteLv0, _secReport ), // Not implemented + new DataProperty( PID_SECURITY_REPORT_CONTROL, true, PDT_BINARY_INFORMATION, 1, ReadLv3 | WriteLv0, _secReportCtrl ), // Not implemented + new DataProperty( PID_SEQUENCE_NUMBER_SENDING, true, PDT_GENERIC_06, 1, ReadLv3 | WriteLv0 ), // Updated by our device accordingly + new DataProperty( PID_ZONE_KEY_TABLE, true, PDT_GENERIC_19, 1, ReadLv3 | WriteLv0 ), // written by ETS + new DataProperty( PID_GO_SECURITY_FLAGS, true, PDT_GENERIC_01, 256, ReadLv3 | WriteLv0 ), // written by ETS + new DataProperty( PID_ROLE_TABLE, true, PDT_GENERIC_01, 1, ReadLv3 | WriteLv0 ), // written by ETS + new DataProperty( PID_ERROR_CODE, false, PDT_ENUM8, 1, ReadLv3 | WriteLv0, (uint8_t)E_NO_FAULT), + new DataProperty( PID_TOOL_SEQUENCE_NUMBER_SENDING, true, PDT_GENERIC_06, 1, ReadLv3 | WriteLv0 ) // Updated by our device accordingly (non-standardized!) + }; + initializeProperties(sizeof(properties), properties); +} + +void SecurityInterfaceObject::secureApplicationLayer(SecureApplicationLayer& secAppLayer) +{ + _secAppLayer = &secAppLayer; +} + +uint8_t* SecurityInterfaceObject::save(uint8_t* buffer) +{ + buffer = pushByte(_state, buffer); + buffer = pushByte(_securityModeEnabled, buffer); + + return InterfaceObject::save(buffer); +} + +const uint8_t* SecurityInterfaceObject::restore(const uint8_t* buffer) +{ + uint8_t state = 0; + buffer = popByte(state, buffer); + _state = (LoadState)state; + + uint8_t securityModeEnabled = 0; + buffer = popByte(securityModeEnabled, buffer); + _securityModeEnabled = securityModeEnabled; + + return InterfaceObject::restore(buffer); +} + +uint16_t SecurityInterfaceObject::saveSize() +{ + return 2 + InterfaceObject::saveSize(); +} + +void SecurityInterfaceObject::setSecurityMode(bool enabled) +{ + print("Security mode set to: "); + println(enabled ? "enabled" : "disabled"); + _securityModeEnabled = enabled; +} + +bool SecurityInterfaceObject::isSecurityModeEnabled() +{ + return _securityModeEnabled; +} + +bool SecurityInterfaceObject::isLoaded() +{ + return _state == LS_LOADED; +} + +void SecurityInterfaceObject::loadEvent(const uint8_t* data) +{ + switch (_state) + { + case LS_UNLOADED: + loadEventUnloaded(data); + break; + case LS_LOADING: + loadEventLoading(data); + break; + case LS_LOADED: + loadEventLoaded(data); + break; + case LS_ERROR: + loadEventError(data); + break; + default: + /* do nothing */ + break; + } +} + +void SecurityInterfaceObject::loadEventUnloaded(const uint8_t* data) +{ + uint8_t event = data[0]; + switch (event) + { + case LE_NOOP: + case LE_LOAD_COMPLETED: + case LE_ADDITIONAL_LOAD_CONTROLS: + case LE_UNLOAD: + break; + case LE_START_LOADING: + loadState(LS_LOADING); + break; + default: + loadState(LS_ERROR); + errorCode(E_GOT_UNDEF_LOAD_CMD); + } +} + +void SecurityInterfaceObject::loadEventLoading(const uint8_t* data) +{ + uint8_t event = data[0]; + switch (event) + { + case LE_NOOP: + case LE_START_LOADING: + break; + case LE_LOAD_COMPLETED: + loadState(LS_LOADED); + break; + case LE_UNLOAD: + loadState(LS_UNLOADED); + break; + case LE_ADDITIONAL_LOAD_CONTROLS: // Not supported here + default: + loadState(LS_ERROR); + errorCode(E_GOT_UNDEF_LOAD_CMD); + } +} + +void SecurityInterfaceObject::loadEventLoaded(const uint8_t* data) +{ + uint8_t event = data[0]; + switch (event) + { + case LE_NOOP: + case LE_LOAD_COMPLETED: + break; + case LE_START_LOADING: + loadState(LS_LOADING); + break; + case LE_UNLOAD: + loadState(LS_UNLOADED); + break; + case LE_ADDITIONAL_LOAD_CONTROLS: + loadState(LS_ERROR); + errorCode(E_INVALID_OPCODE); + break; + default: + loadState(LS_ERROR); + errorCode(E_GOT_UNDEF_LOAD_CMD); + } +} + +void SecurityInterfaceObject::loadEventError(const uint8_t* data) +{ + uint8_t event = data[0]; + switch (event) + { + case LE_NOOP: + case LE_LOAD_COMPLETED: + case LE_ADDITIONAL_LOAD_CONTROLS: + case LE_START_LOADING: + break; + case LE_UNLOAD: + loadState(LS_UNLOADED); + break; + default: + loadState(LS_ERROR); + errorCode(E_GOT_UNDEF_LOAD_CMD); + } +} + +void SecurityInterfaceObject::loadState(LoadState newState) +{ + if (newState == _state) + return; + //beforeStateChange(newState); + _state = newState; +} + +void SecurityInterfaceObject::errorCode(ErrorCode errorCode) +{ + uint8_t data = errorCode; + Property* prop = property(PID_ERROR_CODE); + prop->write(data); +} + +void SecurityInterfaceObject::masterReset(EraseCode eraseCode, uint8_t channel) +{ + if (eraseCode == FactoryReset) + { + // TODO handle different erase codes + println("Factory reset of security interface object requested."); + setSecurityMode(false); + property(PID_TOOL_KEY)->write(1, 1, _fdsk); + } +} + +const uint8_t* SecurityInterfaceObject::toolKey() +{ + // There is only one tool key + const uint8_t* toolKey = propertyData(PID_TOOL_KEY); + return toolKey; +} + +const uint8_t* SecurityInterfaceObject::p2pKey(uint16_t addressIndex) +{ + if (!isLoaded()) + return nullptr; + + // Get number of entries for this property + uint16_t numElements = getNumberOfElements(PID_P2P_KEY_TABLE); + + if (numElements > 0) + { + uint8_t elementSize = propertySize(PID_P2P_KEY_TABLE); + + // Search for address index + uint8_t entry[elementSize]; // 2 bytes index + keysize (16 bytes) + 2 bytes(roles) = 20 bytes + for (int i = 1; i <= numElements; i++) + { + property(PID_P2P_KEY_TABLE)->read(i, 1, entry); + uint16_t index = (entry[0] << 8) | entry[1]; + if (index > addressIndex) + { + return nullptr; + } + if (index == addressIndex) + { + return propertyData(PID_P2P_KEY_TABLE, i) + sizeof(index); + } + } + } + + return nullptr; +} + +const uint8_t* SecurityInterfaceObject::groupKey(uint16_t addressIndex) +{ + if (!isLoaded()) + return nullptr; + + // Get number of entries for this property + uint16_t numElements = getNumberOfElements(PID_GRP_KEY_TABLE); + + if (numElements > 0) + { + uint8_t elementSize = propertySize(PID_GRP_KEY_TABLE); + + // Search for address index + uint8_t entry[elementSize]; // 2 bytes index + keysize (16 bytes) = 18 bytes + for (int i = 1; i <= numElements; i++) + { + property(PID_GRP_KEY_TABLE)->read(i, 1, entry); + uint16_t index = ((entry[0] << 8) | entry[1]); + if (index > addressIndex) + { + return nullptr; + } + if (index == addressIndex) + { + return propertyData(PID_GRP_KEY_TABLE, i) + sizeof(index); + } + } + } + + return nullptr; +} + +uint16_t SecurityInterfaceObject::indAddressIndex(uint16_t indAddr) +{ + // Get number of entries for this property + uint16_t numElements = getNumberOfElements(PID_SECURITY_INDIVIDUAL_ADDRESS_TABLE); + + if (numElements > 0) + { + uint8_t elementSize = propertySize(PID_SECURITY_INDIVIDUAL_ADDRESS_TABLE); + + // Search for individual address + uint8_t entry[elementSize]; // 2 bytes address + 6 bytes seqno = 8 bytes + for (int i = 1; i <= numElements; i++) + { + property(PID_SECURITY_INDIVIDUAL_ADDRESS_TABLE)->read(i, 1, entry); + uint16_t addr = (entry[0] << 8) | entry[1]; + if (addr == indAddr) + { + return i; + } + } + } + + // Not found + return 0; +} + +void SecurityInterfaceObject::setSequenceNumber(bool toolAccess, uint64_t seqNum) +{ + uint8_t seqBytes[6] = {0x00}; + sixBytesFromUInt64(seqNum, seqBytes); + + if (toolAccess) + { + property(PID_TOOL_SEQUENCE_NUMBER_SENDING)->write(1, 1, seqBytes); + } + else + { + property(PID_SEQUENCE_NUMBER_SENDING)->write(1, 1, seqBytes); + } +} + +uint16_t SecurityInterfaceObject::getNumberOfElements(PropertyID propId) +{ + // Get number of entries for this property + uint16_t numElements = 0; + + uint8_t data[sizeof(uint16_t)]; // is sizeof(_currentElements) which is uint16_t + uint8_t count = property(propId)->read(0, 1, data); + + if (count > 0) + { + popWord(numElements, data); + } + + return numElements; +} + +uint64_t SecurityInterfaceObject::getLastValidSequenceNumber(uint16_t deviceAddr) +{ + + // Get number of entries for this property + uint16_t numElements = getNumberOfElements(PID_SECURITY_INDIVIDUAL_ADDRESS_TABLE); + + if (numElements > 0) + { + uint8_t elementSize = propertySize(PID_SECURITY_INDIVIDUAL_ADDRESS_TABLE); + + // Search for individual address + uint8_t entry[elementSize]; // 2 bytes address + 6 bytes seqno = 8 bytes + for (int i = 1; i <= numElements; i++) + { + property(PID_SECURITY_INDIVIDUAL_ADDRESS_TABLE)->read(i, 1, entry); + uint16_t addr = (entry[0] << 8) | entry[1]; + if (addr == deviceAddr) + { + return sixBytesToUInt64(&entry[2]); + } + } + } + return 0; +} + +void SecurityInterfaceObject::setLastValidSequenceNumber(uint16_t deviceAddr, uint64_t seqNum) +{ + // Get number of entries for this property + uint16_t numElements = getNumberOfElements(PID_SECURITY_INDIVIDUAL_ADDRESS_TABLE); + + if (numElements > 0) + { + uint8_t elementSize = propertySize(PID_SECURITY_INDIVIDUAL_ADDRESS_TABLE); + + // Search for individual address + uint8_t entry[elementSize]; // 2 bytes address + 6 bytes seqno = 8 bytes + for (int i = 1; i <= numElements; i++) + { + property(PID_SECURITY_INDIVIDUAL_ADDRESS_TABLE)->read(i, 1, entry); + uint16_t addr = (entry[0] << 8) | entry[1]; + if (addr == deviceAddr) + { + sixBytesFromUInt64(seqNum, &entry[2]); + property(PID_SECURITY_INDIVIDUAL_ADDRESS_TABLE)->write(i, 1, entry); + } + } + } +} + +DataSecurity SecurityInterfaceObject::getGroupObjectSecurity(uint16_t index) +{ + // security table uses same index as group object table + + uint8_t data[propertySize(PID_GO_SECURITY_FLAGS)]; + + uint8_t count = property(PID_GO_SECURITY_FLAGS)->read(index, 1, data); + + if (count > 0) + { + // write access flags, approved spec. AN158, p.97 + bool conf = (data[0] & 2) == 2; + bool auth = (data[0] & 1) == 1; + return conf ? DataSecurity::authConf : auth ? DataSecurity::auth : DataSecurity::none; + } + + return DataSecurity::none; +} + +#endif + diff --git a/src/knx/security_interface_object.h b/src/knx/security_interface_object.h new file mode 100644 index 0000000..754f2ea --- /dev/null +++ b/src/knx/security_interface_object.h @@ -0,0 +1,66 @@ +#pragma once + +#include "config.h" +#ifdef USE_DATASECURE + +#include "interface_object.h" +#include "knx_types.h" + +class SecureApplicationLayer; + +class SecurityInterfaceObject: public InterfaceObject +{ +public: + SecurityInterfaceObject(); + + void secureApplicationLayer(SecureApplicationLayer& secAppLayer); + + virtual void masterReset(EraseCode eraseCode, uint8_t channel) override; + + bool isSecurityModeEnabled(); + + bool isLoaded(); + + const uint8_t* toolKey(); // returns single tool key (ETS) + const uint8_t* p2pKey(uint16_t addressIndex); // returns p2p key for IA index + const uint8_t* groupKey(uint16_t addressIndex); // returns group key for group address index + + uint16_t indAddressIndex(uint16_t indAddr); // returns 1-based index of address in security IA table + + void setSequenceNumber(bool toolAccess, uint64_t seqNum); + uint64_t getLastValidSequenceNumber(uint16_t deviceAddr); + void setLastValidSequenceNumber(uint16_t deviceAddr, uint64_t seqNum); + + DataSecurity getGroupObjectSecurity(uint16_t index); + + LoadState loadState(); + uint8_t* save(uint8_t* buffer) override; + const uint8_t* restore(const uint8_t* buffer) override; + uint16_t saveSize() override; + +private: + SecureApplicationLayer* _secAppLayer = nullptr; + + void setSecurityMode(bool enabled); + + void errorCode(ErrorCode errorCode); + + void loadEvent(const uint8_t* data); + void loadEventUnloaded(const uint8_t* data); + void loadEventLoading(const uint8_t* data); + void loadEventLoaded(const uint8_t* data); + void loadEventError(const uint8_t* data); + + void loadState(LoadState newState); + LoadState _state = LS_UNLOADED; + + bool _securityModeEnabled {false}; + + uint16_t getNumberOfElements(PropertyID propId); + + // Our FDSK + static const uint8_t _fdsk[]; + static uint8_t _secReport[]; + static uint8_t _secReportCtrl[]; +}; +#endif diff --git a/src/knx/simple_map.h b/src/knx/simple_map.h new file mode 100644 index 0000000..ff8df24 --- /dev/null +++ b/src/knx/simple_map.h @@ -0,0 +1,131 @@ +#pragma once + +// Provides a simple unordered map which is based on two arrays of different data types, namely K and V. +// One array is used for the keys, the other array is used for the values. +// Tracking of free/occupied slots in the arrays is realized by a bitmask of size uint64_t. +// As a result the maximum size of the map is 64 entries. +// If a non-primitive data type is required for the key by using a "class" or "struct", +// then the operator== has to be provided by that class/struct: +// bool operator ==(const K&) const { return true/false; } + +template +class Map +{ +public: + Map() + { + static_assert (SIZE <= 64, "Map is too big! Max. 64 elements."); + } + + void clear() + { + _validEntries = 0; + } + + bool empty() + { + return (_validEntries == 0); + } + + uint8_t size() + { + uint8_t size = 0; + + for (uint8_t i = 0; i < SIZE; i++) + { + size += (((_validEntries >> i) & 0x01) == 0x01) ? 1 : 0; + } + + return size; + } + + bool insert(K key, V value) + { + uint8_t index = getNextFreeIndex(); + if (index != noFreeEntryFoundIndex) + { + keys[index] = key; + values[index] = value; + + _validEntries |= 1 << index; + return true; + } + + // No free space + return false; + } + + bool insertOrAssign(K key, V value) + { + // Try to find the key + for (uint8_t i = 0; i < SIZE; i++) + { + // Check if this array slot is occupied + if ((_validEntries >> i) & 0x01) + { + // Key found? + if (keys[i] == key) + { + values[i] = value; + return true; + } + } + } + + // Key does not exist, add it if enough space + return insert(key, value); + } + + bool erase(K key) + { + for (uint8_t i = 0; i < SIZE; i++) + { + if ((_validEntries >> i) & 0x01) + { + if (keys[i] == key) + { + _validEntries &= ~(1 << i); + return true; + } + } + } + return false; + } + + V* get(K key) + { + // Try to find the key + for (uint8_t i = 0; i < SIZE; i++) + { + // Check if this array slot is occupied + if ((_validEntries >> i) & 0x01) + { + // Key found? + if (keys[i] == key) + { + return &values[i]; + } + } + } + return nullptr; + } + +private: + uint8_t getNextFreeIndex() + { + for (uint8_t i = 0; i < SIZE; i++) + { + if (((_validEntries >> i) & 0x01) == 0) + { + return i; + } + } + + return noFreeEntryFoundIndex; + } + + uint64_t _validEntries{0}; + K keys[SIZE]; + V values[SIZE]; + static constexpr uint8_t noFreeEntryFoundIndex = 255; +}; diff --git a/src/knx/transport_layer.cpp b/src/knx/transport_layer.cpp index 91cafc9..176ff74 100644 --- a/src/knx/transport_layer.cpp +++ b/src/knx/transport_layer.cpp @@ -530,7 +530,19 @@ void TransportLayer::ackTimeoutIndication() } } +// Note: we should probably also add the TSAP as argument if would support multiple concurrent connections +uint8_t TransportLayer::getTpciSeqNum() +{ + // Return seqNum that would be used for sending next frame + // together with the TPDU type. + return ((_seqNoSend & 0xF) << 2); +} +// Note: we should probably also add the TSAP as argument if would support multiple concurrent connections +uint16_t TransportLayer::getConnectionAddress() +{ + return _connectionAddress; +} void TransportLayer::loop() { diff --git a/src/knx/transport_layer.h b/src/knx/transport_layer.h index f2d6c5e..76bf861 100644 --- a/src/knx/transport_layer.h +++ b/src/knx/transport_layer.h @@ -56,6 +56,9 @@ public: void disconnectRequest(uint16_t tsap, Priority priority); // apdu must be valid until it was confirmed void dataConnectedRequest(uint16_t tsap, Priority priority, APDU& apdu); + + uint8_t getTpciSeqNum(); + uint16_t getConnectionAddress(); #pragma endregion #pragma region other diff --git a/src/knx_facade.h b/src/knx_facade.h index 548f36e..923195f 100644 --- a/src/knx_facade.h +++ b/src/knx_facade.h @@ -352,4 +352,4 @@ template class KnxFacade : private SaveRestore extern KnxFacade knx; #elif __linux__ // no predefined global instance -#endif \ No newline at end of file +#endif