diff --git a/CMakeLists.txt b/CMakeLists.txt index a538fb97..af292c0d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,10 +132,10 @@ set(USE_PREBUILD_MBEDTLS 1) set(MBEDTLS_INCLUDE_DIR ${CONFIG_EXTERNAL_MBEDTLS_INCLUDE_PATH}) endif(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB) -if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.16) +if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28) set(WITH_MBEDTLS 1) -set(MBEDTLS_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.16/include") -endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.16) +set(MBEDTLS_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28/include") +endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28) if(WITH_MBEDTLS) diff --git a/examples/goose_publisher/goose_publisher_example.c b/examples/goose_publisher/goose_publisher_example.c index 083cfa59..ec89fc90 100644 --- a/examples/goose_publisher/goose_publisher_example.c +++ b/examples/goose_publisher/goose_publisher_example.c @@ -62,7 +62,8 @@ main(int argc, char **argv) L2Security l2Sec = L2Security_create(); - L2Security_addKey(l2Sec, 0x12345678, (uint8_t*)key, 16, MC_SEC_SEC_ALGO_NONE, MC_SEC_SIG_ALGO_HMAC_SHA256_256); + //L2Security_addKey(l2Sec, 0x12345678, (uint8_t*)key, 16, MC_SEC_SEC_ALGO_NONE, MC_SEC_SIG_ALGO_HMAC_SHA256_256); + L2Security_addKey(l2Sec, 0x12345678, (uint8_t*)key, 16, MC_SEC_SEC_ALGO_NONE, MC_SEC_SIG_ALGO_AES_GMAC_128); L2Security_setActiveKey(l2Sec, 1); GoosePublisher_setL2Security(publisher, l2Sec); diff --git a/examples/goose_subscriber/goose_subscriber_example.c b/examples/goose_subscriber/goose_subscriber_example.c index 3edd3db0..ff767206 100644 --- a/examples/goose_subscriber/goose_subscriber_example.c +++ b/examples/goose_subscriber/goose_subscriber_example.c @@ -65,12 +65,13 @@ main(int argc, char** argv) GooseSubscriber_setDstMac(subscriber, dstMac); GooseSubscriber_setAppId(subscriber, 1000); - //char* key = "0123456789ABCDEF"; - char* key = "0123456789ABCDEG"; + char* key = "0123456789ABCDEF"; + //char* key = "0123456789ABCDEG"; L2Security l2Sec = L2Security_create(); - L2Security_addKey(l2Sec, 0x12345678, (uint8_t*)key, 16, MC_SEC_SEC_ALGO_NONE, MC_SEC_SIG_ALGO_HMAC_SHA256_256); + //L2Security_addKey(l2Sec, 0x12345678, (uint8_t*)key, 16, MC_SEC_SEC_ALGO_NONE, MC_SEC_SIG_ALGO_HMAC_SHA256_256); + L2Security_addKey(l2Sec, 0x12345678, (uint8_t*)key, 16, MC_SEC_SEC_ALGO_NONE, MC_SEC_SIG_ALGO_AES_GMAC_128); L2Security_setActiveKey(l2Sec, 1); GooseReceiver_setL2Security(receiver, l2Sec); diff --git a/hal/CMakeLists.txt b/hal/CMakeLists.txt index 33975c28..ed407d90 100644 --- a/hal/CMakeLists.txt +++ b/hal/CMakeLists.txt @@ -129,7 +129,7 @@ include_directories( if(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB) link_directories(${CONFIG_EXTERNAL_MBEDTLS_DYNLIB_PATH}) else() -file(GLOB tls_SRCS ${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls/mbedtls-2.16/library/*.c) +file(GLOB tls_SRCS ${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls/mbedtls-2.28/library/*.c) endif(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB) add_definitions(-DMBEDTLS_CONFIG_FILE="mbedtls_config.h") diff --git a/src/goose/goose_sec.c b/src/goose/goose_sec.c index 72569389..b340d738 100644 --- a/src/goose/goose_sec.c +++ b/src/goose/goose_sec.c @@ -1,3 +1,26 @@ +/* + * l2_security.c + * + * Copyright 2013-2025 Michael Zillgith + * + * This file is part of libIEC61850. + * + * libIEC61850 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libIEC61850 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libIEC61850. If not, see . + * + * See COPYING file for the complete license text. + */ + #include #include @@ -6,7 +29,8 @@ #include "ber_decode.h" #include "r_session_crypto.h" -struct sL2Security { +struct sL2Security +{ RSignatureAlgorithm currentSigAlgo; uint32_t timeOfCurrentKey; @@ -73,6 +97,16 @@ L2Security_calculateCRC16(uint8_t* data, int size) return calculateCRC(data, size); } +static void +printBuffer(uint8_t* buffer, int size) +{ + for (int i = 0; i < size; i++) + { + printf("%02x", buffer[i]); + } + printf("\n"); +} + /** * \brief Create the security extension * @@ -88,9 +122,9 @@ L2Security_addSecurityExtension(L2Security self, uint8_t* buffer, int start, int printf("L2Security_addSecurityExtension: start=%i, length=%i, maxBufSize=%i\n", start, length, maxBufSize); if (self->currentSigAlgo != MC_SEC_SIG_ALGO_NONE) { - bool hasIV = false; int ivSize = 0; int mACSize = 0; + uint8_t* ivBuf = NULL; /* determine length of the mAC */ if (self->currentSigAlgo == MC_SEC_SIG_ALGO_HMAC_SHA256_128) { @@ -99,8 +133,17 @@ L2Security_addSecurityExtension(L2Security self, uint8_t* buffer, int start, int else if (self->currentSigAlgo == MC_SEC_SIG_ALGO_HMAC_SHA256_256) { mACSize = 2 + 32; } + else if (self->currentSigAlgo == MC_SEC_SIG_ALGO_AES_GMAC_128) { + mACSize = 2 + 16; + ivSize = 12; /* IV size for AES GMAC (recommendation from NIST: https://web.cs.ucdavis.edu/~rogaway/ocb/gcm.pdf) */ + } + else if (self->currentSigAlgo == MC_SEC_SIG_ALGO_AES_GMAC_256) { + mACSize = 2 + 32; + ivSize = 12; /* IV size for AES GMAC (recommendation from NIST: https://web.cs.ucdavis.edu/~rogaway/ocb/gcm.pdf) */ + } else { /* signature algorithm not supported */ + printf("Signature algorithm not supported\n"); return 0; } @@ -119,7 +162,8 @@ L2Security_addSecurityExtension(L2Security self, uint8_t* buffer, int start, int authValueSize += (2 + BerEncoder_Int32determineEncodedSize(self->timeToNextKey)); /* IV */ - if (hasIV) { + if (ivSize > 0) + { authValueSize += (2 + ivSize); } @@ -157,14 +201,16 @@ L2Security_addSecurityExtension(L2Security self, uint8_t* buffer, int start, int bufPos = BerEncoder_encodeInt32WithTL(0x82, self->timeToNextKey, buffer, bufPos); /* IV */ - if (hasIV) { - //TODO encode IV + if (ivSize > 0) + { + bufPos = BerEncoder_encodeTL(0x83, ivSize, buffer, bufPos); + ivBuf = buffer + bufPos; + bufPos += ivSize; } /* KeyID */ bufPos = BerEncoder_encodeInt32WithTL(0x84, self->currentKeyId, buffer, bufPos); - int macEnd = bufPos; /* encode mAC */ @@ -178,11 +224,46 @@ L2Security_addSecurityExtension(L2Security self, uint8_t* buffer, int start, int RSessionCrypto_createHMAC(buffer + start, macEnd - start, self->currentKey, self->currentKeySize, buffer + bufPos, 32); bufPos += 32; } + else if (self->currentSigAlgo == MC_SEC_SIG_ALGO_AES_GMAC_128) + { + /* create IV */ + if (RSessionCrypto_createRandomData(ivBuf, ivSize) == false) { + printf("ERROR - Failed to create random IV\n"); + } + + if (RSessionCrypto_createAES_GMAC(self->currentKey, self->currentKeySize, ivBuf, ivSize, buffer + start, macEnd - start, buffer + bufPos, 16) == false) + { + printf("ERROR - Failed to create GMAC\n"); + } + + bufPos += 16; + } + else if (self->currentSigAlgo == MC_SEC_SIG_ALGO_AES_GMAC_256) + { + /* create IV */ + if (RSessionCrypto_createRandomData(ivBuf, ivSize) == false) { + printf("ERROR - Failed to create random IV\n"); + } + + if (RSessionCrypto_createAES_GMAC(self->currentKey, self->currentKeySize, ivBuf, ivSize, buffer + start, macEnd - start, buffer + bufPos, 32) == false) + { + printf("ERROR - Failed to create GMAC\n"); + } + + bufPos += 32; + } + else { + /* signature algorithm not supported */ + printf("Signature algorithm not supported\n"); + return 0; + } } return securityExtensionSize + 2; } - else { + else + { + printf("L2_SECURITY: no signature algorithm set\n"); return 0; } } @@ -204,6 +285,12 @@ checkSecurityExtension(L2Security self, uint8_t* buffer, int secExtLen, uint8_t* else if (self->currentSigAlgo == MC_SEC_SIG_ALGO_HMAC_SHA256_256) { mACSize = 2 + 32; } + else if (self->currentSigAlgo == MC_SEC_SIG_ALGO_AES_GMAC_128) { + mACSize = 2 + 16; + } + else if (self->currentSigAlgo == MC_SEC_SIG_ALGO_AES_GMAC_256) { + mACSize = 2 + 32; + } else { /* signature algorithm not supported */ printf("L2_SECURITY: signature algorithm not supported\n"); @@ -320,7 +407,7 @@ checkSecurityExtension(L2Security self, uint8_t* buffer, int secExtLen, uint8_t* } else if (self->currentSigAlgo == MC_SEC_SIG_ALGO_HMAC_SHA256_256) { - printf("Algo: HMAC_SHA256_256\n"); + printf("Algo: HMAC_SHA256_256\n"); uint8_t calculatedMac[32]; @@ -332,6 +419,54 @@ checkSecurityExtension(L2Security self, uint8_t* buffer, int secExtLen, uint8_t* return false; } } + else if (self->currentSigAlgo == MC_SEC_SIG_ALGO_AES_GMAC_128) + { + printf("Algo: AES_GMAC_128\n"); + + uint8_t calculatedMac[16]; + + printf("IV: "); + printBuffer(ivBuffer, ivSize); + + printf("MAC: "); + printBuffer(mACBuffer, mACSize); + + if (RSessionCrypto_createAES_GMAC(self->currentKey, self->currentKeySize, ivBuffer, ivSize, macStart, macEnd, calculatedMac, 16) == false) + { + printf("L2_SECURITY: GMAC calculation failed\n"); + return false; + } + + if (memcmp(calculatedMac, mACBuffer, 16) != 0) + { + printf("L2_SECURITY: GMAC mismatch\n"); + return false; + } + } + else if (self->currentSigAlgo == MC_SEC_SIG_ALGO_AES_GMAC_256) + { + printf("Algo: AES_GMAC_256\n"); + + printf("IV: "); + printBuffer(ivBuffer, ivSize); + + printf("MAC: "); + printBuffer(mACBuffer, mACSize); + + uint8_t calculatedMac[8]; + + if (RSessionCrypto_createAES_GMAC(self->currentKey, self->currentKeySize, ivBuffer, ivSize, macStart, macEnd, calculatedMac, 32) == false) + { + printf("L2_SECURITY: GMAC calculation failed\n"); + return false; + } + + if (memcmp(calculatedMac, mACBuffer, 8) != 0) + { + printf("L2_SECURITY: GMAC mismatch\n"); + return false; + } + } else { printf("L2_SECURITY: signature algorithm not supported\n"); diff --git a/src/goose/l2_security.h b/src/goose/l2_security.h index 4e0f8525..2f0533d5 100644 --- a/src/goose/l2_security.h +++ b/src/goose/l2_security.h @@ -1,7 +1,7 @@ /* * l2_security.h * - * Copyright 2022 Michael Zillgith + * Copyright 2022-2025 Michael Zillgith * * This file is part of libIEC61850. * @@ -53,8 +53,8 @@ typedef enum { MC_SEC_SIG_ALGO_HMAC_SHA256_80 = 1, MC_SEC_SIG_ALGO_HMAC_SHA256_128 = 2, MC_SEC_SIG_ALGO_HMAC_SHA256_256 = 3, - MC_SEC_SIG_ALGO_AES_GMAC_64 = 4, - MC_SEC_SIG_ALGO_AES_GMAC_128 = 5, + MC_SEC_SIG_ALGO_AES_GMAC_128 = 4, + MC_SEC_SIG_ALGO_AES_GMAC_256 = 5, MC_SEC_SIG_ALGO_HMAC_SHA3_80 = 6, MC_SEC_SIG_ALGO_HMAC_SHA3_128 = 7, MC_SEC_SIG_ALGO_HMAC_SHA3_256 = 8 diff --git a/src/r_session/r_session_crypto.h b/src/r_session/r_session_crypto.h index d7a76a48..86fa254d 100644 --- a/src/r_session/r_session_crypto.h +++ b/src/r_session/r_session_crypto.h @@ -32,6 +32,9 @@ LIB61850_INTERNAL bool RSessionCrypto_createHMAC(uint8_t* buffer, int bufSize, uint8_t* key, int keySize, uint8_t* hmac, int hmacMaxSize); +LIB61850_INTERNAL bool +RSessionCrypto_createAES_GMAC(uint8_t* key, int keySize, uint8_t* iv, int ivSize, uint8_t* addData, int addDataSize, uint8_t* tag, int tagSize); + LIB61850_INTERNAL bool RSessionCrypto_gcmEncryptAndTag(uint8_t* key, int keySize, uint8_t* iv, int ivSize, uint8_t* addData, int addDataSize, uint8_t* encryptData, int encryptDataSize, uint8_t* tag, int tagSize); diff --git a/src/r_session/r_session_crypto_mbedtls.c b/src/r_session/r_session_crypto_mbedtls.c index ae31b34e..f5e94fd4 100644 --- a/src/r_session/r_session_crypto_mbedtls.c +++ b/src/r_session/r_session_crypto_mbedtls.c @@ -72,6 +72,39 @@ RSessionCrypto_createHMAC(uint8_t* buffer, int bufSize, uint8_t* key, int keySiz return true; } +bool +RSessionCrypto_createAES_GMAC(uint8_t* key, int keySize, uint8_t* iv, int ivSize, uint8_t* addData, int addDataSize, uint8_t* tag, int tagSize) +{ + mbedtls_gcm_context gcmCtx; + + mbedtls_gcm_init(&gcmCtx); + + if (mbedtls_gcm_setkey(&gcmCtx, MBEDTLS_CIPHER_ID_AES , (const unsigned char*) key, keySize * 8)) + { + printf("AES-GCM: Failed to set key\n"); + mbedtls_gcm_free(&gcmCtx); + return false; + } + + if (mbedtls_gcm_starts(&gcmCtx, MBEDTLS_GCM_ENCRYPT, iv, ivSize, addData, addDataSize)) + { + printf("AES-GCM: Failed to start tag calculation\n"); + mbedtls_gcm_free(&gcmCtx); + return false; + } + + if (mbedtls_gcm_finish(&gcmCtx, tag, tagSize)) + { + printf("AES-GCM: Failed to finish tag calculation\n"); + mbedtls_gcm_free(&gcmCtx); + return false; + } + + mbedtls_gcm_free(&gcmCtx); + + return true; +} + bool RSessionCrypto_gcmEncryptAndTag(uint8_t* key, int keySize, uint8_t* iv, int ivSize, uint8_t* addData, int addDataSize, uint8_t* encryptData, int encryptDataSize, uint8_t* tag, int tagSize) {