mirror of
				https://github.com/thelsing/knx.git
				synced 2025-10-26 10:26:25 +01:00 
			
		
		
		
	save work
This commit is contained in:
		
							parent
							
								
									c08e85c956
								
							
						
					
					
						commit
						99918eb08a
					
				| @ -171,6 +171,7 @@ enum PropertyID | ||||
|     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 | ||||
|  | ||||
| @ -19,7 +19,7 @@ const uint8_t SecureDataPdu = 0; | ||||
| const uint8_t SecureSyncRequest = 2; | ||||
| const uint8_t SecureSyncResponse = 3; | ||||
| 
 | ||||
| uint64_t sequenceNumberToolAccess = 0; | ||||
| uint64_t sequenceNumberToolAccess = 50; | ||||
| uint64_t sequenceNumber = 0; | ||||
| 
 | ||||
| uint64_t lastValidSequenceNumberTool = 0; | ||||
| @ -38,6 +38,8 @@ SecureApplicationLayer::SecureApplicationLayer(DeviceObject &deviceObj, Security | ||||
| 
 | ||||
| void SecureApplicationLayer::dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu) | ||||
| { | ||||
|     println("dataGroupIndication"); | ||||
| 
 | ||||
|     if (apdu.type() == SecureService) | ||||
|     { | ||||
|         // Decrypt secure APDU
 | ||||
| @ -58,6 +60,8 @@ void SecureApplicationLayer::dataGroupIndication(HopCountType hopType, Priority | ||||
| 
 | ||||
| void SecureApplicationLayer::dataGroupConfirm(AckType ack, HopCountType hopType, Priority priority,  uint16_t tsap, APDU& apdu, bool status) | ||||
| { | ||||
|     println("dataGroupConfirm"); | ||||
| 
 | ||||
|     if (apdu.type() == SecureService) | ||||
|     { | ||||
|         // Decrypt secure APDU
 | ||||
| @ -78,6 +82,8 @@ void SecureApplicationLayer::dataGroupConfirm(AckType ack, HopCountType hopType, | ||||
| 
 | ||||
| void SecureApplicationLayer::dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) | ||||
| { | ||||
|     println("dataBroadcastIndication"); | ||||
| 
 | ||||
|     if (apdu.type() == SecureService) | ||||
|     { | ||||
|         // Decrypt secure APDU
 | ||||
| @ -98,8 +104,11 @@ void SecureApplicationLayer::dataBroadcastIndication(HopCountType hopType, Prior | ||||
| 
 | ||||
| void SecureApplicationLayer::dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, bool status) | ||||
| { | ||||
|     println("dataBroadcastConfirm"); | ||||
| 
 | ||||
|     if (apdu.type() == SecureService) | ||||
|     { | ||||
| /*
 | ||||
|         // Decrypt secure APDU
 | ||||
|         // Somehow ugly that we need to know the size in advance here at this point
 | ||||
|         uint16_t plainApduLength = apdu.length() - 1 - 6 - 4; // secureAdsuLength - sizeof(scf) - sizeof(seqNum) - sizeof(mac)
 | ||||
| @ -110,14 +119,16 @@ void SecureApplicationLayer::dataBroadcastConfirm(AckType ack, HopCountType hopT | ||||
|             // Process decrypted inner APDU
 | ||||
|             ApplicationLayer::dataBroadcastConfirm(ack, hopType, priority, plainFrame.apdu(), 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) | ||||
|     { | ||||
|         // Decrypt secure APDU
 | ||||
| @ -138,8 +149,11 @@ void SecureApplicationLayer::dataSystemBroadcastIndication(HopCountType hopType, | ||||
| 
 | ||||
| void SecureApplicationLayer::dataSystemBroadcastConfirm(HopCountType hopType, Priority priority, APDU& apdu, bool status) | ||||
| { | ||||
|     println("dataSystemBroadcastConfirm"); | ||||
| 
 | ||||
|     if (apdu.type() == SecureService) | ||||
|     { | ||||
| /*
 | ||||
|         // Decrypt secure APDU
 | ||||
|         // Somehow ugly that we need to know the size in advance here at this point
 | ||||
|         uint16_t plainApduLength = apdu.length() - 1 - 6 - 4; // secureAdsuLength - sizeof(scf) - sizeof(seqNum) - sizeof(mac)
 | ||||
| @ -150,6 +164,7 @@ void SecureApplicationLayer::dataSystemBroadcastConfirm(HopCountType hopType, Pr | ||||
|             // Process decrypted inner APDU
 | ||||
|             ApplicationLayer::dataSystemBroadcastConfirm(hopType, priority, plainFrame.apdu(), status); | ||||
|         } | ||||
| */ | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
| @ -158,6 +173,8 @@ void SecureApplicationLayer::dataSystemBroadcastConfirm(HopCountType hopType, Pr | ||||
| 
 | ||||
| void SecureApplicationLayer::dataIndividualIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) | ||||
| { | ||||
|     println("dataIndividualIndication"); | ||||
| 
 | ||||
|     if (apdu.type() == SecureService) | ||||
|     { | ||||
|         // Decrypt secure APDU
 | ||||
| @ -178,12 +195,17 @@ void SecureApplicationLayer::dataIndividualIndication(HopCountType hopType, Prio | ||||
| 
 | ||||
| void SecureApplicationLayer::dataIndividualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t source, APDU& apdu, bool status) | ||||
| { | ||||
|     println("dataIndividualConfirm"); | ||||
| 
 | ||||
|     if (apdu.type() == SecureService) | ||||
|     { | ||||
| /*
 | ||||
|         // Decrypt secure APDU
 | ||||
| 
 | ||||
|         // Process decrypted inner APDU
 | ||||
|         ApplicationLayer::dataIndividualConfirm(ack, hopType, priority, source, apdu, status); | ||||
| */ | ||||
|         return; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
| @ -193,6 +215,8 @@ void SecureApplicationLayer::dataIndividualConfirm(AckType ack, HopCountType hop | ||||
| 
 | ||||
| void SecureApplicationLayer::dataConnectedIndication(Priority priority, uint16_t tsap, APDU& apdu) | ||||
| { | ||||
|     println("dataConnectedIndication"); | ||||
| 
 | ||||
|     if (apdu.type() == SecureService) | ||||
|     { | ||||
|         // Somehow ugly that we need to know the size in advance here at this point
 | ||||
| @ -260,6 +284,30 @@ void SecureApplicationLayer::dataConnectedRequest(uint16_t tsap, Priority priori | ||||
|     ApplicationLayer::dataConnectedRequest(tsap, priority, apdu); | ||||
| } | ||||
| 
 | ||||
| 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)
 | ||||
| @ -274,19 +322,8 @@ uint32_t SecureApplicationLayer::calcAuthOnlyMac(uint8_t* apdu, uint8_t apduLeng | ||||
|     pBuf = pushWord(apduLength, pBuf); | ||||
|     pBuf = pushByteArray(apdu, apduLength, pBuf); | ||||
| 
 | ||||
|     // Use zeroes as IV for first round
 | ||||
|     uint8_t zeroIv[16] = {0x00}; | ||||
| 
 | ||||
|     struct AES_ctx ctx1; | ||||
|     AES_init_ctx_iv(&ctx1, key, zeroIv); | ||||
|     // Now encrypt first block B0
 | ||||
|     AES_CBC_encrypt_buffer(&ctx1, iv, 16); | ||||
|     // Encrypt remaining buffer
 | ||||
|     AES_CBC_encrypt_buffer(&ctx1, buffer, bufLenPadded); | ||||
| 
 | ||||
|     struct AES_ctx ctx2; | ||||
|     AES_init_ctx_iv(&ctx2, key, ctr0); | ||||
|     AES_CTR_xcrypt_buffer(&ctx2, buffer, 4); // 4 bytes only for the MAC
 | ||||
|     encryptAesCbc(buffer, bufLenPadded, iv, key); | ||||
|     xcryptAesCtr(buffer, 4, ctr0, key); // 4 bytes only for the MAC
 | ||||
| 
 | ||||
|     uint32_t mac; | ||||
|     popInt(mac, &buffer[0]); | ||||
| @ -311,15 +348,7 @@ uint32_t SecureApplicationLayer::calcConfAuthMac(uint8_t* associatedData, uint16 | ||||
|     pBuf = pushByteArray(associatedData, associatedDataLength, pBuf); | ||||
|     pBuf = pushByteArray(apdu, apduLength, pBuf); | ||||
| 
 | ||||
|     // 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, iv, 16); | ||||
|     // Encrypt remaining buffer
 | ||||
|     AES_CBC_encrypt_buffer(&ctx, buffer, bufLenPadded); | ||||
|     encryptAesCbc(buffer, bufLenPadded, iv, key); | ||||
| 
 | ||||
|     uint32_t mac; | ||||
|     popInt(mac, &buffer[bufLenPadded - 16]); // bufLenPadded has a guaranteed minimum size of 16 bytes
 | ||||
| @ -351,21 +380,29 @@ void SecureApplicationLayer::blockCtr0(uint8_t* buffer, uint8_t* seqNum, uint16_ | ||||
|     pBuf = pushByte(0x01, pBuf); | ||||
| } | ||||
| 
 | ||||
| void SecureApplicationLayer::sixBytes(uint64_t num, uint8_t* toByteArray) | ||||
| void SecureApplicationLayer::sixBytesFromUInt64(uint64_t num, uint8_t* toByteArray) | ||||
| { | ||||
|     *(toByteArray + 0) = (uint16_t)(num >> 32); | ||||
|     *(toByteArray + 2) = (uint32_t)(num); | ||||
|     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 SecureApplicationLayer::toUInt64(uint8_t* data, uint8_t dataLen) | ||||
| uint64_t SecureApplicationLayer::sixBytesToUInt64(uint8_t* data) | ||||
| { | ||||
|         uint64_t l = 0; | ||||
| /*
 | ||||
|     uint64_t l = 0; | ||||
| 
 | ||||
|         for (uint8_t i = 0; i< dataLen; i++) | ||||
|         { | ||||
|             l = (l << 8) + data[i]; | ||||
|         } | ||||
|         return l; | ||||
|     for (uint8_t i = 0; i < 6; i++) | ||||
|     { | ||||
|         l = (l << 8) + data[i]; | ||||
|     } | ||||
|     return l; | ||||
| */ | ||||
|     return  ((uint64_t)data[0] << 40) + ((uint64_t)data[1] << 32) + | ||||
|             (data[2] << 24) + (data[3] << 16) + (data[4] << 8) + data[5]; | ||||
| } | ||||
| 
 | ||||
| const uint8_t* SecureApplicationLayer::toolKey(uint16_t devAddr) | ||||
| @ -474,7 +511,7 @@ uint64_t SecureApplicationLayer::lastValidSequenceNumber(bool toolAcces, uint16_ | ||||
| void SecureApplicationLayer::updateLastValidSequence(bool toolAccess, uint16_t remoteAddr, uint64_t seqNo) | ||||
| { | ||||
|     if (toolAccess) | ||||
|         // TODO: add map to handle multiplpe lastValidSequenceNumberTool for each srcAddr
 | ||||
|         // TODO: add map to handle multiple lastValidSequenceNumberTool for each srcAddr
 | ||||
|         //lastValidSequenceToolAccess.put(remoteAddr, seqNo);
 | ||||
|         lastValidSequenceNumberTool = seqNo; | ||||
|     else | ||||
| @ -503,12 +540,16 @@ void SecureApplicationLayer::sendSyncResponse(uint16_t dstAddr, bool dstAddrIsGr | ||||
|     uint64_t ourNextSeqNum = nextSequenceNumber(toolAccess); | ||||
| 
 | ||||
|     uint8_t asdu[12]; | ||||
|     sixBytes(ourNextSeqNum, &asdu[0]); | ||||
|     sixBytes(remoteNextSeqNum, &asdu[6]); | ||||
|     sixBytesFromUInt64(ourNextSeqNum, &asdu[0]); | ||||
|     sixBytesFromUInt64(remoteNextSeqNum, &asdu[6]); | ||||
| 
 | ||||
|     CemiFrame response(3 + 6 + sizeof(asdu) + 4); // 3 bytes (TPCI, APCI, SCF) + 6 bytes (SeqNum) + 12 bytes + 4 bytes (MAC)
 | ||||
| 
 | ||||
|     uint8_t tpci = _transportLayer->getTPCI(dstAddr); // get next TPCI sequence number for MAC calculation from TL
 | ||||
|     uint8_t tpci = 0; | ||||
|     if (!_syncReqBroadcast) | ||||
|     { | ||||
|         tpci = _transportLayer->getTPCI(dstAddr); // get next TPCI sequence number for MAC calculation from TL
 | ||||
|     } | ||||
|     print("sendSyncResponse: TPCI: "); | ||||
|     println(tpci, HEX); | ||||
| 
 | ||||
| @ -519,16 +560,23 @@ void SecureApplicationLayer::sendSyncResponse(uint16_t dstAddr, bool dstAddrIsGr | ||||
|         println("SyncResponse: "); | ||||
|         response.apdu().printPDU(); | ||||
| 
 | ||||
|         // Send encrypted SyncResponse message using T_DATA_INDIVIDUAL
 | ||||
|         _transportLayer->dataIndividualRequest(AckType::AckDontCare, NetworkLayerParameter, SystemPriority, dstAddr, response.apdu()); | ||||
|         if (_syncReqBroadcast) | ||||
|         { | ||||
|             _transportLayer->dataBroadcastRequest(AckType::AckDontCare, HopCountType::NetworkLayerParameter, Priority::SystemPriority, response.apdu()); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             //TODO: either send on T_DATA_INDIVIDUAL or T_DATA_CONNECTED depending on reception
 | ||||
| 
 | ||||
|         //TODO: either send on T_DATA_INDIVIDUAL or T_DATA_CONNECTED depending on reception
 | ||||
|             // Send encrypted SyncResponse message using T_DATA_INDIVIDUAL
 | ||||
|             _transportLayer->dataIndividualRequest(AckType::AckDontCare, NetworkLayerParameter, SystemPriority, dstAddr, response.apdu()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void SecureApplicationLayer::receivedSyncRequest(uint16_t srcAddr, uint16_t dstAddr, bool dstAddrIsGroupAddr, bool toolAccess, uint8_t* seqNum, long challenge) | ||||
| { | ||||
|     uint64_t nextRemoteSeqNum = toUInt64(seqNum, 6); | ||||
|     uint64_t nextRemoteSeqNum = sixBytesToUInt64(seqNum); | ||||
|     uint64_t nextSeqNum = 1 + lastValidSequenceNumber(toolAccess, srcAddr); | ||||
| 
 | ||||
|     if (nextRemoteSeqNum > nextSeqNum) | ||||
| @ -539,13 +587,16 @@ void SecureApplicationLayer::receivedSyncRequest(uint16_t srcAddr, uint16_t dstA | ||||
| 
 | ||||
|     // Remember challenge for securing the sync.res later
 | ||||
|     //stashSyncRequest(srcAddr, challenge); // TODO: save challenge in a map to handle multiple requests
 | ||||
|     sixBytes(challenge, _challenge); | ||||
|     _challengeSrcAddr = srcAddr; | ||||
|     sixBytesFromUInt64(challenge, _challenge); | ||||
| 
 | ||||
|     _syncReqBroadcast = (dstAddr == 0x0000) && dstAddrIsGroupAddr; | ||||
| 
 | ||||
|     uint16_t toAddr = _syncReqBroadcast ? dstAddr : srcAddr; | ||||
|     bool toIsGroupAddress = _syncReqBroadcast; | ||||
| 
 | ||||
|     _challengeSrcAddr = toAddr; | ||||
|     _isChallengeValid = true; | ||||
| 
 | ||||
|     sendSyncResponse(toAddr, toIsGroupAddress, toolAccess, nextSeqNum); | ||||
| } | ||||
| 
 | ||||
| @ -562,8 +613,8 @@ void SecureApplicationLayer::receivedSyncResponse(uint16_t remoteAddr, bool tool | ||||
| 
 | ||||
|     // 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 = toUInt64(plainApdu + 0, 6); | ||||
|     uint64_t localSeq = toUInt64(plainApdu + 6, 6 + 6); | ||||
|     uint64_t remoteSeq = sixBytesToUInt64(plainApdu + 0); | ||||
|     uint64_t localSeq = sixBytesToUInt64(plainApdu + 6); | ||||
| 
 | ||||
|     uint64_t last = lastValidSequenceNumber(toolAccess, remoteAddr); | ||||
|     if (remoteSeq - 1 > last) | ||||
| @ -597,16 +648,25 @@ bool SecureApplicationLayer::decrypt(uint8_t* plainApdu, uint16_t plainApduLengt | ||||
|     bool syncReq = service == SecureSyncRequest; | ||||
|     bool syncRes = service == SecureSyncResponse; | ||||
| 
 | ||||
|     const uint8_t* key = dstAddrIsGroupAddr ? securityKey(dstAddr, dstAddrIsGroupAddr) : toolAccess ? toolKey(srcAddr == _deviceObj.induvidualAddress() ? dstAddr : srcAddr) : securityKey(srcAddr, false); | ||||
|     //const uint8_t* key = dstAddrIsGroupAddr ? securityKey(dstAddr, dstAddrIsGroupAddr) : toolAccess ? toolKey(srcAddr == _deviceObj.induvidualAddress() ? dstAddr : srcAddr) : securityKey(srcAddr, false);
 | ||||
|     const uint8_t* key = dstAddrIsGroupAddr && (dstAddr != 0) ? securityKey(dstAddr, dstAddrIsGroupAddr) : toolAccess ? toolKey(srcAddr == _deviceObj.induvidualAddress() ? dstAddr : srcAddr) : 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 = toUInt64(seqNum, 6); | ||||
|     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 == SecureDataPdu) | ||||
|     { | ||||
|         uint64_t expectedSeqNumber = lastValidSequenceNumber(toolAccess, srcAddr) + 1; | ||||
| @ -624,6 +684,7 @@ bool SecureApplicationLayer::decrypt(uint8_t* plainApdu, uint16_t plainApduLengt | ||||
|     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)) | ||||
| @ -659,7 +720,7 @@ bool SecureApplicationLayer::decrypt(uint8_t* plainApdu, uint16_t plainApduLengt | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pBuf = popByteArray(plainApdu, plainApduLength, pBuf); | ||||
|     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
 | ||||
| @ -667,7 +728,7 @@ bool SecureApplicationLayer::decrypt(uint8_t* plainApdu, uint16_t plainApduLengt | ||||
|     // Clear IV buffer
 | ||||
|     uint8_t iv[16] = {0x00}; | ||||
|     // Create first block B0 for AES CBC MAC calculation, used as IV later
 | ||||
|     block0(iv, seqNum, srcAddr, dstAddr, dstAddrIsGroupAddr, extendedFrameFormat, tpci | (SecureService >> 8), SecureService & 0x00FF, plainApduLength); | ||||
|     block0(iv, seqNum, srcAddr, dstAddr, dstAddrIsGroupAddr, extendedFrameFormat, tpci | (SecureService >> 8), SecureService & 0x00FF, remainingPlainApduLength); | ||||
| 
 | ||||
|     // Clear block counter0 buffer
 | ||||
|     uint8_t ctr0[16] = {0x00}; | ||||
| @ -682,43 +743,42 @@ bool SecureApplicationLayer::decrypt(uint8_t* plainApdu, uint16_t plainApduLengt | ||||
|         // APDU is already plain, no decryption needed
 | ||||
| 
 | ||||
|         // Only check the MAC
 | ||||
|         uint32_t calculatedMac = calcAuthOnlyMac(plainApdu, plainApduLength, key, iv, ctr0); | ||||
|         uint32_t calculatedMac = calcAuthOnlyMac(plainApdu, remainingPlainApduLength, key, iv, ctr0); | ||||
|         if (calculatedMac != mac) | ||||
|         { | ||||
|             // security failure
 | ||||
|             print("security failure: calculated MAC: "); | ||||
|             print("security failure(auth): calculated MAC: "); | ||||
|             print(calculatedMac, HEX); | ||||
|             print(" != received MAC: "); | ||||
|             print(mac, HEX); | ||||
|             println("\n"); | ||||
| 
 | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         memcpy(plainApdu, secureAsdu, plainApduLength); | ||||
|         memcpy(plainApdu, secureAsdu, remainingPlainApduLength); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         // APDU is encrypted and needs decryption
 | ||||
| 
 | ||||
|         uint16_t bufLen = 4 + plainApduLength; | ||||
|         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]; | ||||
|         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);
 | ||||
|         memset(buffer, 0x00, bufLenPadded); | ||||
| 
 | ||||
|         pushInt(mac, &buffer[0]); | ||||
|         pushByteArray(plainApdu, plainApduLength, &buffer[4]); // apdu is still encrypted
 | ||||
|         pushByteArray(plainApdu, remainingPlainApduLength, &buffer[4]); // apdu is still encrypted
 | ||||
| 
 | ||||
|         struct AES_ctx ctx; | ||||
|         AES_init_ctx_iv(&ctx, key, ctr0); | ||||
|         //AES_CTR_xcrypt_buffer(&ctx, buffer, bufLenPadded);
 | ||||
|         AES_CTR_xcrypt_buffer(&ctx, buffer, bufLen); | ||||
|         xcryptAesCtr(buffer, bufLenPadded, ctr0, key); | ||||
|         //xcryptAesCtr(buffer, bufLen, ctr0, key);
 | ||||
| 
 | ||||
|         uint32_t decryptedMac; | ||||
|         popInt(decryptedMac, &buffer[0]); | ||||
|         popByteArray(plainApdu, plainApduLength, &buffer[4]); // apdu is now decrypted (overwritten)
 | ||||
|         popByteArray(plainApdu, remainingPlainApduLength, &buffer[4]); // apdu is now decrypted (overwritten)
 | ||||
| 
 | ||||
|         // Do calculations for Auth+Conf
 | ||||
|         uint8_t associatedData[syncReq ? 7 : 1]; | ||||
| @ -727,25 +787,27 @@ bool SecureApplicationLayer::decrypt(uint8_t* plainApdu, uint16_t plainApduLengt | ||||
|         { | ||||
|             memcpy(&associatedData[1], knxSerialNumber, 6); | ||||
|         } | ||||
|         uint32_t calculatedMac = calcConfAuthMac(associatedData, sizeof(associatedData), plainApdu, plainApduLength, key, iv); | ||||
|         uint32_t calculatedMac = calcConfAuthMac(associatedData, sizeof(associatedData), plainApdu, remainingPlainApduLength, key, iv); | ||||
|         if (calculatedMac != decryptedMac) | ||||
|         { | ||||
|             // security failure
 | ||||
|             print("security failure: calculated MAC: "); | ||||
|             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()) | ||||
|         if (syncReq && (srcAddr == _deviceObj.induvidualAddress())) | ||||
|             return false; | ||||
| 
 | ||||
|         if (syncReq) | ||||
|         { | ||||
|             uint64_t challenge = toUInt64(&plainApdu[0], 6); | ||||
|             uint64_t challenge = sixBytesToUInt64(&plainApdu[0]); | ||||
|             receivedSyncRequest(srcAddr, dstAddr, dstAddrIsGroupAddr, toolAccess, seqNum, challenge); | ||||
|         } | ||||
|         else if (syncRes) | ||||
| @ -796,7 +858,7 @@ bool SecureApplicationLayer::decodeSecureApdu(APDU& secureApdu, APDU& plainApdu) | ||||
|     //          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)
 | ||||
|     if (decrypt(plainApdu.frame().data()+APDU_LPDU_DIFF, plainApdu.length(), srcAddress, dstAddress, isDstAddrGroupAddr, tpci, secureApdu.data())) | ||||
|     if (decrypt(plainApdu.frame().data()+APDU_LPDU_DIFF, plainApdu.length()-1, srcAddress, dstAddress, isDstAddrGroupAddr, tpci, secureApdu.data()+1)) | ||||
|     { | ||||
|         println("decodeSecureApdu: Plain APDU: "); | ||||
|         plainApdu.frame().apdu().printPDU(); | ||||
| @ -856,7 +918,7 @@ bool SecureApplicationLayer::secure(uint8_t* buffer, uint16_t service, uint16_t | ||||
|         println("0 is not a valid sequence number"); | ||||
| 
 | ||||
|     uint8_t seq[6]; | ||||
|     sixBytes(seqSend, seq); | ||||
|     sixBytesFromUInt64(seqSend, seq); | ||||
|     if (!syncRes) | ||||
|         pBuf = pushByteArray(seq, 6, pBuf);          // Sequence Number
 | ||||
| 
 | ||||
| @ -878,23 +940,28 @@ bool SecureApplicationLayer::secure(uint8_t* buffer, uint16_t service, uint16_t | ||||
|         if (testSeq) | ||||
|         { | ||||
|             // Do not use a random number, but a well-known one
 | ||||
|             sixBytes(0xaaaaaaaaaaaa, seq); | ||||
|             sixBytesFromUInt64(0xaaaaaaaaaaaa, seq); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // use random number in SyncResponse
 | ||||
|             uint64_t randomNumber = 0x000102030405; // TODO: generate random number
 | ||||
|             sixBytes(randomNumber, seq); | ||||
|             sixBytesFromUInt64(randomNumber, seq); | ||||
|         } | ||||
| 
 | ||||
|         // TODO: maybe implement something like std::map for pending SyncRequests?
 | ||||
|         //final var request = pendingSyncRequests.remove(dst);
 | ||||
|         //if (request == null)
 | ||||
|         //    throw new KnxSecureException("sending sync.res without corresponding .req");
 | ||||
|         if (_challengeSrcAddr != dstAddr) | ||||
|         if (_isChallengeValid && (_challengeSrcAddr == dstAddr)) | ||||
|         { | ||||
|             println("Did not find matching challenge for destination address!"); | ||||
|             return false; | ||||
|             // Invalidate _challengeSrcAddr
 | ||||
|             _challengeSrcAddr = 0; | ||||
|             _isChallengeValid = false; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             println("sending sync.res without corresponding .req"); | ||||
|         } | ||||
| 
 | ||||
|         // Now XOR the new random SeqNum with the challenge from the SyncRequest
 | ||||
| @ -930,9 +997,7 @@ bool SecureApplicationLayer::secure(uint8_t* buffer, uint16_t service, uint16_t | ||||
|         pushInt(mac, tmpBuffer); | ||||
|         pushByteArray(apdu, apduLength, &tmpBuffer[4]); | ||||
| 
 | ||||
|         struct AES_ctx ctx; | ||||
|         AES_init_ctx_iv(&ctx, key, ctr0); | ||||
|         AES_CTR_xcrypt_buffer(&ctx, tmpBuffer, apduLength + 4); // APDU + MAC (4 bytes)
 | ||||
|         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
 | ||||
| @ -972,7 +1037,7 @@ bool SecureApplicationLayer::createSecureApdu(APDU& plainApdu, APDU& secureApdu, | ||||
|     //          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)
 | ||||
|     if(secure(secureApdu.frame().data()+APDU_LPDU_DIFF, SecureDataPdu, srcAddress, dstAddress, isDstAddrGroupAddr, tpci, plainApdu.data(), plainApdu.length(), toolAccess, confidentialty)) | ||||
|     if(secure(secureApdu.frame().data()+APDU_LPDU_DIFF, SecureDataPdu, srcAddress, dstAddress, isDstAddrGroupAddr, tpci, plainApdu.data()+1, plainApdu.length()-1, toolAccess, confidentialty)) | ||||
|     { | ||||
|         updateSequenceNumber(toolAccess, nextSequenceNumber(toolAccess) + 1); | ||||
| 
 | ||||
|  | ||||
| @ -60,8 +60,8 @@ class SecureApplicationLayer :  public ApplicationLayer | ||||
|     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); | ||||
| 
 | ||||
|     void sixBytes(uint64_t num, uint8_t* toByteArray); | ||||
|     uint64_t toUInt64(uint8_t* data, uint8_t dataLen); | ||||
|     void sixBytesFromUInt64(uint64_t num, uint8_t* toByteArray); | ||||
|     uint64_t sixBytesToUInt64(uint8_t* data); | ||||
| 
 | ||||
|     const uint8_t *toolKey(uint16_t devAddr); | ||||
|     const uint8_t* securityKey(uint16_t addr, bool isGroupAddress); | ||||
| @ -90,12 +90,16 @@ class SecureApplicationLayer :  public ApplicationLayer | ||||
|     bool decodeSecureApdu(APDU& secureApdu, APDU& plainApdu); | ||||
|     bool createSecureApdu(APDU& plainApdu, APDU& secureApdu, bool toolAccess, bool confidentialty); | ||||
| 
 | ||||
|     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 _securityModeEnabled {false}; | ||||
| 
 | ||||
|     bool _syncReqBroadcast; | ||||
|     uint32_t _lastSyncRes; | ||||
|     uint8_t _challenge[6]; | ||||
|     uint16_t _challengeSrcAddr; | ||||
|     bool _isChallengeValid {false}; | ||||
| 
 | ||||
|     SecurityInterfaceObject& _secIfObj; | ||||
|     DeviceObject& _deviceObj; | ||||
|  | ||||
| @ -156,6 +156,7 @@ SecurityInterfaceObject::SecurityInterfaceObject() | ||||
|         new DataProperty( PID_ZONE_KEY_TABLE, true, PDT_GENERIC_19, 32, ReadLv3 | WriteLv0 ), // written by ETS
 | ||||
|         new DataProperty( PID_GO_SECURITY_FLAGS, true, PDT_GENERIC_01, 32, ReadLv3 | WriteLv0 ), // written by ETS
 | ||||
|         new DataProperty( PID_ROLE_TABLE, true, PDT_GENERIC_01, 32, ReadLv3 | WriteLv0 ), // written by ETS
 | ||||
|         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); | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user