/* * r_session.c * * Copyright 2013-2022 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 "r_session.h" #include "hal_socket.h" #include "hal_thread.h" #include "lib_memory.h" #include "string_utilities.h" #include "libiec61850_platform_includes.h" #include "r_session_crypto.h" #include "r_session_internal.h" #ifndef DEBUG_RSESSION #define DEBUG_RSESSION 0 #endif #if (DEBUG_RSESSION == 1) #include #define DEBUG_PRINTF(...) printf("RSESSION:"__VA_ARGS__);printf("\n"); #else #define DEBUG_PRINTF(...) #endif typedef struct sRSessionKeyMaterial* RSessionKeyMaterial; struct sRSessionKeyMaterial { uint32_t keyId; RSecurityAlgorithm secAlgo; RSignatureAlgorithm sigAlgo; uint8_t* key; int keyLength; }; struct sRSession { uint32_t spduNumber; int protocolVersion; /* default is 2 */ char* remoteAddress; int remotePort; char* localAddress; int localPort; UdpSocket socket; Semaphore socketLock; uint16_t bufferSize; /* maximum buffer size (range: 128 - 65535) */ uint8_t* sendBuffer; uint8_t* payloadBuffer; /* only required when secAlgo != NONE */ LinkedList keyList; /* list of RSessionKeyMaterial */ Semaphore keyListLock; RSecurityAlgorithm secAlgo; RSignatureAlgorithm sigAlgo; uint32_t timeOfCurrentKey; uint32_t currentKeyId; uint8_t* currentKey; int currentKeySize; bool updateBufferedKeyMaterial; RSecurityAlgorithm currentSecAlgo; RSignatureAlgorithm currentSigAlgo; int timeToNextKey; }; #ifdef DEBUG_RSESSION static void printBuffer(uint8_t* buffer, int bufSize) { int i; for (i = 0; i < bufSize; i++) { printf("%02x ", buffer[i]); if ((((i + 1) % 16) == 0) || (i + 1 == bufSize)) printf(" (%i)\n", i + 1); } } #endif /* DEBUG_RSESSION */ RSessionKeyMaterial RSessionKeyMaterial_create(uint32_t keyId, uint8_t* key, int keyLength, RSecurityAlgorithm secAlgo, RSignatureAlgorithm sigAlgo) { RSessionKeyMaterial self = (RSessionKeyMaterial) GLOBAL_CALLOC(1, sizeof(struct sRSessionKeyMaterial)); if (self) { self->keyId = keyId; self->keyLength = keyLength; self->secAlgo = secAlgo; self->sigAlgo = sigAlgo; self->key = (uint8_t*) GLOBAL_MALLOC(keyLength); if (self->key) { memcpy(self->key, key, keyLength); } else { GLOBAL_FREEMEM(self); self = NULL; } } return self; } void RSessionKeyMaterial_destroy(RSessionKeyMaterial self) { if (self) { GLOBAL_FREEMEM(self->key); GLOBAL_FREEMEM(self); } } RSession RSession_create() { UdpSocket udpSocket = UdpSocket_create(); if (udpSocket) { RSession self = (RSession) GLOBAL_CALLOC(1, sizeof(struct sRSession)); if (self) { self->socket = udpSocket; self->socketLock = Semaphore_create(1); self->secAlgo = R_SESSION_SEC_ALGO_NONE; self->sigAlgo = R_SESSION_SIG_ALGO_NONE; self->protocolVersion = 2; self->bufferSize = (uint16_t)65000; self->keyListLock = Semaphore_create(1); self->keyList = LinkedList_create(); self->localAddress = NULL; self->localPort = 102; } return self; } return NULL; } /* only for version 1 of the protocol! */ RSessionError RSession_setSecurity(RSession self, RSecurityAlgorithm secAlgo, RSignatureAlgorithm sigAlgo) { self->secAlgo = secAlgo; self->sigAlgo = sigAlgo; return R_SESSION_ERROR_OK; } RSessionError RSession_setLocalAddress(RSession self, const char* localAddress, int localPort) { if (self->localAddress) { GLOBAL_FREEMEM(self->localAddress); self->localAddress = NULL; } if (localAddress) self->localAddress = StringUtils_copyString(localAddress); self->localPort = localPort; return R_SESSION_ERROR_OK; } RSessionError RSession_addMulticastGroup(RSession self, const char* multicastAddress) { if (UdpSocket_addGroupMembership(self->socket, multicastAddress)) return R_SESSION_ERROR_OK; else return R_SESSION_ERROR_SET_FAILED; } RSessionError RSession_setMulticastTtl(RSession self, int ttl) { RSessionError err = R_SESSION_ERROR_OK; if (self->socket) { if (!UdpSocket_setMulticastTtl(self->socket, ttl)) { err = R_SESSION_ERROR_SET_FAILED; } } else { err = R_SESSION_ERROR_NO_SOCKET; } return err; } RSessionError RSession_setRemoteAddress(RSession self, const char* remoteAddress, int remotePort) { if (self->remoteAddress) { GLOBAL_FREEMEM(self->remoteAddress); self->remoteAddress = NULL; } self->remoteAddress = StringUtils_copyString(remoteAddress); self->remotePort = remotePort; return R_SESSION_ERROR_OK; } RSessionError RSession_startListening(RSession self) { if (self->socket) { bool success = false; if (self->localAddress) success = UdpSocket_bind(self->socket, self->localAddress, self->localPort); else success = UdpSocket_bind(self->socket, "0.0.0.0", self->localPort); if (success) return R_SESSION_ERROR_OK; else return R_SESSION_ERROR_SET_FAILED; } else { return R_SESSION_ERROR_NO_SOCKET; } } RSessionError RSession_stopListening(RSession self) { Semaphore_wait(self->socketLock); if (self->socket) { Socket_destroy((Socket)self->socket); self->socket = NULL; Semaphore_post(self->socketLock); return R_SESSION_ERROR_OK; } else { Semaphore_post(self->socketLock); return R_SESSION_ERROR_NO_SOCKET; } } #define PAYLOAD_TYPE_TUNNELED_GOOSE_OR_SV 0xa0 #define PAYLOAD_TYPE_GOOSE_APDU 0xa1 #define PAYLOAD_TYPE_SV_APDU 0xa2 #define PAYLOAD_TYPE_MNGT_APDU 0xa3 static int encodeUInt16FixedSize(uint16_t value, uint8_t* buffer, int bufPos) { uint8_t* valueArray = (uint8_t*) &value; #if (ORDER_LITTLE_ENDIAN == 1) buffer[bufPos++] = valueArray[1]; buffer[bufPos++] = valueArray[0]; #else buffer[bufPos++] = valueArray[0]; buffer[bufPos++] = valueArray[1]; #endif return bufPos; } static int encodeInt16FixedSize(int16_t value, uint8_t* buffer, int bufPos) { uint8_t* valueArray = (uint8_t*) &value; #if (ORDER_LITTLE_ENDIAN == 1) buffer[bufPos++] = valueArray[1]; buffer[bufPos++] = valueArray[0]; #else buffer[bufPos++] = valueArray[0]; buffer[bufPos++] = valueArray[1]; #endif return bufPos; } static int encodeUInt32FixedSize(uint32_t value, uint8_t* buffer, int bufPos) { uint8_t* valueArray = (uint8_t*) &value; #if (ORDER_LITTLE_ENDIAN == 1) buffer[bufPos++] = valueArray[3]; buffer[bufPos++] = valueArray[2]; buffer[bufPos++] = valueArray[1]; buffer[bufPos++] = valueArray[0]; #else buffer[bufPos++] = valueArray[0]; buffer[bufPos++] = valueArray[1]; buffer[bufPos++] = valueArray[2]; buffer[bufPos++] = valueArray[3]; #endif return bufPos; } static int decodeUInt32FixedSize(uint32_t* outValue, uint8_t* buffer, int bufPos) { uint32_t value = 0; value += buffer[bufPos++] * 0x1000000; value += buffer[bufPos++] * 0x10000; value += buffer[bufPos++] * 0x100; value += buffer[bufPos++]; *outValue = value; return bufPos; } static int decodeUInt16FixedSize(uint16_t* outValue, uint8_t* buffer, int bufPos) { uint16_t value = 0; value += buffer[bufPos++] * 0x100; value += buffer[bufPos++]; *outValue = value; return bufPos; } static int decodeInt16FixedSize(int16_t* outValue, uint8_t* buffer, int bufPos) { uint16_t value = 0; value += buffer[bufPos++] * 0x100; value += buffer[bufPos++]; *outValue = (int16_t) value; return bufPos; } static bool lookupKey(RSession self, uint32_t keyId, uint8_t** key, int* keySize, RSecurityAlgorithm* secAlgo, RSignatureAlgorithm* sigAlgo) { if (keyId == 0) { DEBUG_PRINTF("Invalid key ID"); return false; } if (self->currentKeyId != keyId) { if (RSession_setActiveKey(self, keyId) != R_SESSION_ERROR_OK) { DEBUG_PRINTF("unknown key-ID %u", keyId); /* TODO audit-log? */ return false; } } *key = self->currentKey; *keySize = self->currentKeySize; *secAlgo = self->currentSecAlgo; *sigAlgo = self->currentSigAlgo; return true; } static RSessionError parseSessionMessage(RSession self, uint8_t* buffer, int msgSize, RSessionPayloadElementHandler handler, void* handlerParam) { int bufPos = 0; if (msgSize < 4) goto exit_parse_error; if (buffer[bufPos++] != 0x01) goto exit_parse_error; if (buffer[bufPos++] != 0x40) goto exit_parse_error; /* SI */ uint8_t payloadType = buffer[bufPos++]; if ((payloadType == 0xa2) || (payloadType == 0xa1) || (payloadType == 0xa0)) { } else { DEBUG_PRINTF("unknown payload type %i", payloadType); goto exit_error; } int sessionHeaderLength = buffer[bufPos++]; if ((msgSize < (sessionHeaderLength + 4)) || (sessionHeaderLength < 10)) { DEBUG_PRINTF("message too small"); goto exit_error; } if (buffer[bufPos++] != 0x80) { DEBUG_PRINTF("protocol error"); goto exit_error; } int commonSessionHeaderLength = buffer[bufPos++]; if (commonSessionHeaderLength < 10) { DEBUG_PRINTF("common session header too small"); goto exit_error; } /* SPDU length */ uint32_t spduLength = 0; bufPos = decodeUInt32FixedSize(&spduLength, buffer, bufPos); /* SPDU number */ uint32_t spduNumber = 0; bufPos = decodeUInt32FixedSize(&spduNumber, buffer, bufPos); /* protocol version */ int16_t protocolVersion = 0; bufPos = decodeInt16FixedSize(&protocolVersion, buffer, bufPos); if (protocolVersion == 1) { /* parse version 1 common header parts */ /* TimeOfCurrentKey */ uint32_t timeOfCurrentKey; bufPos = decodeUInt32FixedSize(&timeOfCurrentKey, buffer, bufPos); /* TimeToNextKey */ int16_t timeToNextKey; bufPos = decodeInt16FixedSize(&timeToNextKey, buffer, bufPos); RSecurityAlgorithm secAlgo = (RSecurityAlgorithm) buffer[bufPos++]; RSignatureAlgorithm sigAlgo = (RSignatureAlgorithm) buffer[bufPos++]; /* Check if algorithms match the configured algorithms */ if (secAlgo != self->secAlgo) { DEBUG_PRINTF("encryption algorithm doesn't match with configuration"); goto exit_error; } if (sigAlgo != self->sigAlgo) { DEBUG_PRINTF("signature algorithm(%i) doesn't match with configuration(%i)", sigAlgo, self->sigAlgo); goto exit_error; } /* Key ID */ uint32_t keyId; bufPos = decodeUInt32FixedSize(&keyId, buffer, bufPos); uint8_t* key = NULL; int keySize = 0; if (sigAlgo != R_SESSION_SIG_ALGO_NONE) { if (lookupKey(self, keyId, &key, &keySize, &secAlgo, &sigAlgo) == false) { DEBUG_PRINTF("ERROR - key not found"); goto exit_error; } } else if (secAlgo != R_SESSION_SEC_ALGO_NONE) { if (lookupKey(self, keyId, &key, &keySize, &secAlgo, &sigAlgo) == false) { DEBUG_PRINTF("ERROR - key not found"); goto exit_error; } } uint32_t payloadLength; bufPos = decodeUInt32FixedSize(&payloadLength, buffer, bufPos); /* parse payload elements */ uint32_t payloadEnd = bufPos + payloadLength; if (payloadEnd > (uint32_t)msgSize) { DEBUG_PRINTF("ERROR - payload size field invalid"); goto exit_error; } uint8_t signatureBuffer[128]; /* Check signature */ if (sigAlgo != R_SESSION_SIG_ALGO_NONE) { if (key) { if (RSessionCrypto_createHMAC(buffer, payloadEnd, key, keySize, signatureBuffer, 32)) { if (buffer[payloadEnd] != 0x85) { DEBUG_PRINTF("ERROR - no signature found"); goto exit_error; } else { if (sigAlgo == R_SESSION_SIG_ALGO_HMAC_SHA256_128) { /* TODO is payloadEnd +2 correct? */ if (memcmp(signatureBuffer, buffer + payloadEnd + 1, 16)) { DEBUG_PRINTF("ERROR - signature not matching!"); goto exit_error; } } else if (sigAlgo == R_SESSION_SIG_ALGO_HMAC_SHA256_256) { /* TODO is payloadEnd +2 correct? */ if (memcmp(signatureBuffer, buffer + payloadEnd + 1, 32)) { DEBUG_PRINTF("ERROR - signature not matching!"); goto exit_error; } } } } else { DEBUG_PRINTF("ERROR - failed to calculate HMAC!"); goto exit_error; } } else { DEBUG_PRINTF("ERROR - key not found!"); goto exit_error; } } while ((uint32_t)bufPos < payloadEnd) { int payloadElementType = buffer[bufPos++]; bool simulation; if (buffer[bufPos++]) simulation = true; else simulation = false; uint16_t appId; bufPos = decodeUInt16FixedSize(&appId, buffer, bufPos); uint16_t asduLength; bufPos = decodeUInt16FixedSize(&asduLength, buffer, bufPos); DEBUG_PRINTF("ASDU %02x sim: %i APPID: %04x length: %i", payloadElementType, simulation, appId, asduLength); if (payloadElementType == 0x82) { /* user payload */ //TODO copy ASDU payload to ??? handler(handlerParam, appId, buffer + bufPos, asduLength); } else { DEBUG_PRINTF("unexpected payload type! (expect 82h)"); } bufPos += asduLength; } } else if (protocolVersion == 2) { /* parse version 2 common header parts */ /* TimeOfCurrentKey */ uint32_t timeOfCurrentKey; bufPos = decodeUInt32FixedSize(&timeOfCurrentKey, buffer, bufPos); /* TimeToNextKey */ int16_t timeToNextKey; bufPos = decodeInt16FixedSize(&timeToNextKey, buffer, bufPos); RSecurityAlgorithm secAlgo = R_SESSION_SEC_ALGO_NONE; RSignatureAlgorithm sigAlgo = R_SESSION_SIG_ALGO_NONE; uint8_t* key = NULL; int keySize = 0; /* Key ID */ uint32_t keyId; bufPos = decodeUInt32FixedSize(&keyId, buffer, bufPos); if (keyId != 0) { /* get key material associated with the key ID */ if (lookupKey(self, keyId, &key, &keySize, &secAlgo, &sigAlgo) == false) { DEBUG_PRINTF("ERROR - key not found"); goto exit_error; } } else { DEBUG_PRINTF("ERROR - invalid key ID"); goto exit_error; } DEBUG_PRINTF("PV: 2 sec-algo: %i sig-algo: %i", secAlgo, sigAlgo); uint8_t* iv = NULL; /* IV */ int ivLen = buffer[bufPos++]; DEBUG_PRINTF("IV: size = %i\n", ivLen); if (ivLen > 0) { iv = buffer + bufPos; bufPos += ivLen; } uint32_t payloadLength; bufPos = decodeUInt32FixedSize(&payloadLength, buffer, bufPos); uint8_t* payloadStart = buffer + bufPos; int payloadStartPos = bufPos; /* parse payload elements */ uint32_t payloadEnd = bufPos + payloadLength; if (payloadEnd > (uint32_t)msgSize) { DEBUG_PRINTF("ERROR - payload size field invalid"); goto exit_error; } uint8_t signatureBuffer[128]; /* Check signature */ if (sigAlgo != R_SESSION_SIG_ALGO_NONE) { if (key) { if (RSessionCrypto_createHMAC(buffer, payloadEnd, key, keySize, signatureBuffer, 32)) { if (buffer[payloadEnd] != 0x85) { DEBUG_PRINTF("ERROR - no signature found"); goto exit_error; } else { if (sigAlgo == R_SESSION_SIG_ALGO_HMAC_SHA256_128) { /* TODO is payloadEnd +2 correct? */ if (memcmp(signatureBuffer, buffer + payloadEnd + 1, 16)) { DEBUG_PRINTF("ERROR - signature not matching!"); goto exit_error; } } else if (sigAlgo == R_SESSION_SIG_ALGO_HMAC_SHA256_256) { /* TODO is payloadEnd +2 correct? */ if (memcmp(signatureBuffer, buffer + payloadEnd + 1, 32)) { DEBUG_PRINTF("ERROR - signature not matching!"); goto exit_error; } } } } else { DEBUG_PRINTF("ERROR - failed to calculate HMAC!"); goto exit_error; } } else { DEBUG_PRINTF("ERROR - key not found!"); goto exit_error; } } /* Check signature and decrypt application layer */ if (secAlgo != R_SESSION_SEC_ALGO_NONE) { /* Check for HMAC */ if (payloadEnd + 18 <= (uint32_t)msgSize) { if (self->payloadBuffer == NULL) self->payloadBuffer = (uint8_t*)GLOBAL_MALLOC(65000); if (self->payloadBuffer) { //TODO check MMAC tag uint8_t* mac = buffer + payloadEnd + 2; int macSize = buffer[payloadEnd + 1]; int payloadSize = payloadEnd - payloadStartPos; if (RSessionCrypto_gcmAuthAndDecrypt(key, keySize, iv, ivLen, buffer, payloadStartPos, payloadStart, payloadSize, self->payloadBuffer, mac, macSize)) { memcpy(buffer + bufPos, self->payloadBuffer, payloadSize); } else { DEBUG_PRINTF("ERROR - auth and decrypt failed!"); goto exit_error; } } } else { DEBUG_PRINTF("ERROR - sec algo - message too small!"); goto exit_error; } } while ((uint32_t)bufPos < payloadEnd) { int payloadElementType = buffer[bufPos++]; bool simulation; if (buffer[bufPos++]) simulation = true; else simulation = false; uint16_t appId; bufPos = decodeUInt16FixedSize(&appId, buffer, bufPos); uint16_t asduLength; bufPos = decodeUInt16FixedSize(&asduLength, buffer, bufPos); DEBUG_PRINTF("ASDU %02x sim: %i APPID: %04x length: %i", payloadElementType, simulation, appId, asduLength); if (payloadElementType == 0x82) { /* user payload */ //TODO copy ASDU payload to ??? handler(handlerParam, appId, buffer + bufPos, asduLength); } else { DEBUG_PRINTF("unexpected payload type! (expect 82h)"); } bufPos += asduLength; } } else { DEBUG_PRINTF("only protocol version 1 and 2 supported (received version %i)", protocolVersion); goto exit_error; } return R_SESSION_ERROR_OK; exit_parse_error: DEBUG_PRINTF("ERROR - Failed to parse message"); exit_error: return R_SESSION_ERROR_INVALID_MESSAGE; } int encodePacket(RSession self, uint8_t payloadType, uint8_t* buffer, int bufPos, RSessionPayloadElement elements) { /* calculate total payload length */ int payloadLength = 0; int startPos = bufPos; uint8_t* iv = NULL; int ivSize = 0; RSessionPayloadElement element = elements; while (element) { payloadLength += 6; /* payload type, simulation, APPID, length */ payloadLength += element->payloadSize; element = element->nextElement; } /* Connection less transport protocol */ buffer[bufPos++] = 0x01; buffer[bufPos++] = 0x40; /* SI */ buffer[bufPos++] = payloadType; /* Session header length */ buffer[bufPos++] = 24; /* Common session header tag */ buffer[bufPos++] = 0x80; /* Common session header length */ buffer[bufPos++] = 18; // 12 /* SPDU length */ int spduLength = 4 + 2 + 4 + 4 + 1 + 1 + 4 + payloadLength; DEBUG_PRINTF("payloadlength: %i spdu_len: %i", payloadLength, spduLength); bufPos = encodeUInt32FixedSize(spduLength, buffer, bufPos); /* SPDU number */ bufPos = encodeUInt32FixedSize(self->spduNumber, buffer, bufPos); /* version */ bufPos = encodeUInt16FixedSize(self->protocolVersion, buffer, bufPos); /* TimeOfCurrentKey */ bufPos = encodeUInt32FixedSize(self->timeOfCurrentKey, buffer, bufPos); /* TimeToNextKey */ bufPos = encodeInt16FixedSize((int16_t)self->timeToNextKey, buffer, bufPos); if (self->protocolVersion == 1) { /* encryption algorithm */ buffer[bufPos++] = (uint8_t) self->secAlgo; /* 0 = none */ /* signature algorithm */ buffer[bufPos++] = (uint8_t) self->sigAlgo; /* 0 = none */ /* Key ID */ bufPos = encodeUInt32FixedSize(self->currentKeyId, buffer, bufPos); } else { /* protocol version 2 */ /* Key ID */ bufPos = encodeUInt32FixedSize(self->currentKeyId, buffer, bufPos); self->secAlgo = self->currentSecAlgo; self->sigAlgo = self->currentSigAlgo; DEBUG_PRINTF("PV: 2 sec-algo: %i sig-algo: %i\n", self->secAlgo, self->sigAlgo); /* IV */ if (self->secAlgo != R_SESSION_SEC_ALGO_NONE) { /* TODO get and encode IV (initialization vector) */ buffer[bufPos++] = 12; iv = buffer + bufPos; ivSize = 12; if (RSessionCrypto_createRandomData(iv, ivSize) == false) { DEBUG_PRINTF("ERROR - Failed to create random IV"); } bufPos += ivSize; } else { buffer[bufPos++] = 0; /* empty initialization vector */ } } /* user payload length */ bufPos = encodeUInt32FixedSize(payloadLength, buffer, bufPos); int encryptedPartStartPos = bufPos; /* encode user payload elements */ element = elements; while (element) { /* payload type ? (according to example in annex G) */ buffer[bufPos++] = 0x82; /* simulation */ buffer[bufPos++] = element->simulation; /* APPID */ bufPos = encodeUInt16FixedSize(element->appId, buffer, bufPos); /* APDU length */ bufPos = encodeUInt16FixedSize(element->payloadSize + 2, buffer, bufPos); /* copy user payload */ memcpy(buffer + bufPos, element->payload, element->payloadSize); bufPos += element->payloadSize; element = element->nextElement; } if (self->sigAlgo != R_SESSION_SIG_ALGO_NONE) { int signatureCoveredLength = bufPos - startPos; DEBUG_PRINTF("Signature: %i", signatureCoveredLength); /* add signature */ if (self->sigAlgo == R_SESSION_SIG_ALGO_HMAC_SHA256_256) { buffer[bufPos++] = 0x85; buffer[bufPos++] = 16; RSessionCrypto_createHMAC(buffer + startPos, signatureCoveredLength, self->currentKey, self->currentKeySize, buffer + bufPos, 32); bufPos += 32; } else if (self->sigAlgo == R_SESSION_SIG_ALGO_HMAC_SHA256_128) { buffer[bufPos++] = 0x85; buffer[bufPos++] = 16; //buffer[bufPos++] = 0x20; /* 32 octets */ RSessionCrypto_createHMAC(buffer + startPos, signatureCoveredLength, self->currentKey, self->currentKeySize, buffer + bufPos, 16); bufPos += 16; } else { DEBUG_PRINTF("ERROR - unsupported signature type"); } } int payloadEndPos = bufPos; if (self->secAlgo != R_SESSION_SEC_ALGO_NONE) { /* create signature and encrypt payload */ buffer[bufPos++] = 0x85; buffer[bufPos++] = 16; int addPartSize = encryptedPartStartPos - startPos; int encryptedPartSize = payloadEndPos - encryptedPartStartPos; #ifdef DEBUG_RSESSION printBuffer(buffer + startPos, bufPos - startPos); #endif DEBUG_PRINTF("===> encrypt ===="); if (RSessionCrypto_gcmEncryptAndTag(self->currentKey, self->currentKeySize, iv, ivSize, buffer + startPos, addPartSize, buffer + encryptedPartStartPos, encryptedPartSize, buffer + bufPos, 16) == false) { DEBUG_PRINTF("ERROR - encryption failed"); } else { bufPos += 16; } } self->spduNumber++; return bufPos; } /* * NOTE: For library internal use! */ RSessionError RSession_sendMessage(RSession self, RSessionProtocol_SPDU_ID spduId, bool simulation, uint16_t appId, uint8_t* payload, int payloadSize) { if (self->socket == NULL) { self->socket = UdpSocket_create(); } if (self->sendBuffer == NULL) { self->sendBuffer = (uint8_t*) GLOBAL_MALLOC(self->bufferSize); if (self->sendBuffer == NULL) return R_SESSION_ERROR_OUT_OF_MEMORY; } if (self->socket) { struct sRSessionPayloadElement element; element.simulation = simulation; element.appId = appId; element.payload = payload; element.payloadSize = payloadSize; element.nextElement = NULL; int msgSize = encodePacket(self, (uint8_t) spduId, self->sendBuffer, 0, &element); #ifdef DEBUG_RSESSION printBuffer(self->sendBuffer, msgSize); #endif if (UdpSocket_sendTo(self->socket, self->remoteAddress, self->remotePort, self->sendBuffer, msgSize)) { return R_SESSION_ERROR_OK; } else { return R_SESSION_ERROR_FAILED_TO_SEND; } } else { return R_SESSION_ERROR_NO_SOCKET; } } void RSession_setBufferSize(RSession self, uint16_t bufferSize) { if (bufferSize > 127) { self->bufferSize = bufferSize; } else { self->bufferSize = 128; } } RSessionError RSession_receiveMessage(RSession self, RSessionPayloadElementHandler handler, void* parameter) { Semaphore_wait(self->socketLock); if (self->socket) { char ipAddrBuf[128]; if (self->sendBuffer == NULL) { self->sendBuffer = (uint8_t*) GLOBAL_MALLOC(self->bufferSize); if (self->sendBuffer == NULL) { Semaphore_post(self->socketLock); return R_SESSION_ERROR_OUT_OF_MEMORY; } } //TODO use handleset int msgSize = UdpSocket_receiveFrom(self->socket, ipAddrBuf, 128, self->sendBuffer, self->bufferSize); Semaphore_post(self->socketLock); if (msgSize < 1) { DEBUG_PRINTF("RESSSION: Failed to receive message"); return R_SESSION_ERROR_FAILED_TO_RECEIVE; } else { return parseSessionMessage(self, self->sendBuffer, msgSize, handler, parameter); } } else { Semaphore_post(self->socketLock); return R_SESSION_ERROR_NO_SOCKET; } } RSessionError RSession_addKey(RSession self, uint32_t keyId, uint8_t* key, int keyLength, RSecurityAlgorithm secAlgo, RSignatureAlgorithm sigAlgo) { RSessionKeyMaterial keyMaterial = RSessionKeyMaterial_create(keyId, key, keyLength, secAlgo, sigAlgo); if (keyMaterial) { Semaphore_wait(self->keyListLock); LinkedList_add(self->keyList, keyMaterial); Semaphore_post(self->keyListLock); return R_SESSION_ERROR_OK; } else { return R_SESSION_ERROR_OUT_OF_MEMORY; } } static RSessionKeyMaterial getKeyById(RSession self, uint32_t keyId) { LinkedList keyElem = LinkedList_getNext(self->keyList); while (keyElem) { RSessionKeyMaterial keyMaterial = (RSessionKeyMaterial) LinkedList_getData(keyElem); if (keyMaterial->keyId == keyId) { return keyMaterial; } keyElem = LinkedList_getNext(keyElem); } return NULL; } RSessionError RSession_removeKey(RSession self, uint32_t keyId) { RSessionError retVal = R_SESSION_ERROR_OK; Semaphore_wait(self->keyListLock); RSessionKeyMaterial keyMaterial = getKeyById(self, keyId); if (keyMaterial) { LinkedList_remove(self->keyList, keyMaterial); RSessionKeyMaterial_destroy(keyMaterial); } else { retVal = R_SESSION_ERROR_INVALID_KEY; } Semaphore_post(self->keyListLock); if (self->currentKeyId == keyId) { /* active key removed! */ self->currentKeyId = 0; } return retVal; } void RSession_removeAllKeys(RSession self) { Semaphore_wait(self->keyListLock); LinkedList_destroyDeep(self->keyList, (LinkedListValueDeleteFunction)RSessionKeyMaterial_destroy); self->keyList = LinkedList_create(); self->currentKeyId = 0; Semaphore_post(self->keyListLock); } /** * \brief Set the active key for the sender */ RSessionError RSession_setActiveKey(RSession self, uint32_t keyId) { RSessionError retVal = R_SESSION_ERROR_OK; Semaphore_wait(self->keyListLock); RSessionKeyMaterial keyMaterial = getKeyById(self, keyId); if (keyMaterial) { self->currentKeySize = keyMaterial->keyLength; self->currentKey = keyMaterial->key; self->currentKeyId = keyMaterial->keyId; self->currentSecAlgo = keyMaterial->secAlgo; self->currentSigAlgo = keyMaterial->sigAlgo; } else { retVal = R_SESSION_ERROR_INVALID_KEY; } Semaphore_post(self->keyListLock); return retVal; } void RSession_destroy(RSession self) { if (self) { if (self->socket) Socket_destroy((Socket)self->socket); if (self->sendBuffer) GLOBAL_FREEMEM(self->sendBuffer); if (self->remoteAddress) GLOBAL_FREEMEM(self->remoteAddress); if (self->localAddress) GLOBAL_FREEMEM(self->localAddress); if (self->payloadBuffer) GLOBAL_FREEMEM(self->payloadBuffer); if (self->keyList) LinkedList_destroyDeep(self->keyList, (LinkedListValueDeleteFunction)RSessionKeyMaterial_destroy); if (self->keyListLock) Semaphore_destroy(self->keyListLock); if (self->socketLock) Semaphore_destroy(self->socketLock); GLOBAL_FREEMEM(self); } } Socket RSession_getSocket(RSession self) { return (Socket)self->socket; }