mirror of
https://github.com/thelsing/knx.git
synced 2025-08-31 13:47:01 +02:00
save work
This commit is contained in:
parent
66b31a6dd7
commit
4c24836ebc
@ -124,8 +124,9 @@ void ApplicationLayer::dataBroadcastIndication(HopCountType hopType, Priority pr
|
|||||||
#else
|
#else
|
||||||
print("Broadcast-indication: unhandled APDU-Type: ");
|
print("Broadcast-indication: unhandled APDU-Type: ");
|
||||||
println(apdu.type());
|
println(apdu.type());
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,8 +172,9 @@ void ApplicationLayer::dataBroadcastConfirm(AckType ack, HopCountType hopType, P
|
|||||||
#else
|
#else
|
||||||
print("Broadcast-confirm: unhandled APDU-Type: ");
|
print("Broadcast-confirm: unhandled APDU-Type: ");
|
||||||
println(apdu.type());
|
println(apdu.type());
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,12 @@ enum NmReadSerialNumberType
|
|||||||
|
|
||||||
BauSystemB::BauSystemB(Platform& platform): _memory(platform, _deviceObj), _addrTable(_memory),
|
BauSystemB::BauSystemB(Platform& platform): _memory(platform, _deviceObj), _addrTable(_memory),
|
||||||
_assocTable(_memory), _groupObjTable(_memory), _appProgram(_memory),
|
_assocTable(_memory), _groupObjTable(_memory), _appProgram(_memory),
|
||||||
_platform(platform), _appLayer(_assocTable, *this),
|
_platform(platform),
|
||||||
|
#ifdef USE_DATASECURE
|
||||||
|
_appLayer(_deviceObj, _secIfObj, _assocTable, *this),
|
||||||
|
#else
|
||||||
|
_appLayer(_assocTable, *this),
|
||||||
|
#endif
|
||||||
_transLayer(_appLayer, _addrTable), _netLayer(_transLayer)
|
_transLayer(_appLayer, _addrTable), _netLayer(_transLayer)
|
||||||
{
|
{
|
||||||
_appLayer.transportLayer(_transLayer);
|
_appLayer.transportLayer(_transLayer);
|
||||||
|
@ -140,7 +140,12 @@ bool DataLinkLayer::sendTelegram(NPDU & npdu, AckType ack, uint16_t destinationA
|
|||||||
frame.addressType(addrType);
|
frame.addressType(addrType);
|
||||||
frame.priority(priority);
|
frame.priority(priority);
|
||||||
frame.repetition(RepititionAllowed);
|
frame.repetition(RepititionAllowed);
|
||||||
|
#if (MEDIUM_TYPE == 5)||(MEDIUM_TYPE == 0)
|
||||||
|
// Make sure to always send as normal Broadcast on closed media (TP and IP)
|
||||||
|
frame.systemBroadcast(Broadcast);
|
||||||
|
#else
|
||||||
frame.systemBroadcast(systemBroadcast);
|
frame.systemBroadcast(systemBroadcast);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (npdu.octetCount() <= 15)
|
if (npdu.octetCount() <= 15)
|
||||||
frame.frameType(StandardFrame);
|
frame.frameType(StandardFrame);
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
#include "transport_layer.h"
|
#include "transport_layer.h"
|
||||||
#include "cemi_frame.h"
|
#include "cemi_frame.h"
|
||||||
#include "association_table_object.h"
|
#include "association_table_object.h"
|
||||||
|
#include "security_interface_object.h"
|
||||||
|
#include "device_object.h"
|
||||||
#include "apdu.h"
|
#include "apdu.h"
|
||||||
#include "bau.h"
|
#include "bau.h"
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
@ -10,17 +12,22 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
const uint8_t SecureDataPdu = 0;
|
const uint8_t SecureDataPdu = 0;
|
||||||
const uint8_t SecureServiceRequest = 2;
|
const uint8_t SecureSyncRequest = 2;
|
||||||
const uint8_t SecureServiceResponse = 3;
|
const uint8_t SecureSyncResponse = 3;
|
||||||
|
|
||||||
uint8_t lastValidSequenceNumberTool = 0;
|
uint64_t sequenceNumberToolAccess = 0;
|
||||||
|
uint64_t sequenceNumber = 0;
|
||||||
|
|
||||||
// Our FDSK
|
uint64_t lastValidSequenceNumberTool = 0;
|
||||||
uint8_t SecureApplicationLayer::_key[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
|
uint64_t lastValidSequenceNumber = 0;
|
||||||
|
|
||||||
SecureApplicationLayer::SecureApplicationLayer(AssociationTableObject& assocTable, BusAccessUnit& bau):
|
|
||||||
ApplicationLayer(assocTable, bau)
|
SecureApplicationLayer::SecureApplicationLayer(DeviceObject &deviceObj, SecurityInterfaceObject &secIfObj, AssociationTableObject& assocTable, BusAccessUnit& bau):
|
||||||
|
ApplicationLayer(assocTable, bau),
|
||||||
|
_secIfObj(secIfObj),
|
||||||
|
_deviceObj(deviceObj)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* from transport layer */
|
/* from transport layer */
|
||||||
@ -150,6 +157,8 @@ void SecureApplicationLayer::dataConnectedIndication(Priority priority, uint16_t
|
|||||||
println("Secure APDU: ");
|
println("Secure APDU: ");
|
||||||
apdu.printPDU();
|
apdu.printPDU();
|
||||||
|
|
||||||
|
// Somehow ugly that we need to know the size in advance here at this point
|
||||||
|
// Same length calculation is also in the decrypt() function
|
||||||
uint16_t plainApduLength = apdu.length() - 1 - 6 - 4; // secureAdsuLength - sizeof(scf) - sizeof(seqNum) - sizeof(mac)
|
uint16_t plainApduLength = apdu.length() - 1 - 6 - 4; // secureAdsuLength - sizeof(scf) - sizeof(seqNum) - sizeof(mac)
|
||||||
CemiFrame plainFrame(plainApduLength);
|
CemiFrame plainFrame(plainApduLength);
|
||||||
|
|
||||||
@ -158,9 +167,17 @@ void SecureApplicationLayer::dataConnectedIndication(Priority priority, uint16_t
|
|||||||
uint8_t tpci = apdu.frame().data()[TPDU_LPDU_DIFF]; // FIXME: when cEMI class is refactored, there might be additional info fields in cEMI [fixed TPDU_LPDU_DIFF]
|
uint8_t tpci = apdu.frame().data()[TPDU_LPDU_DIFF]; // FIXME: when cEMI class is refactored, there might be additional info fields in cEMI [fixed TPDU_LPDU_DIFF]
|
||||||
print("Secure Debug: TPCI: ");
|
print("Secure Debug: TPCI: ");
|
||||||
println(tpci, HEX);
|
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 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)
|
// FIXME: when cEMI class is refactored, there might be additional info fields in cEMI (fixed APDU_LPDU_DIFF)
|
||||||
if (decrypt(plainFrame.data()+APDU_LPDU_DIFF, srcAddress, dstAddress, tpci, apdu.data(), apdu.length()))
|
if (decrypt(plainFrame.data()+APDU_LPDU_DIFF, srcAddress, dstAddress, false, tpci, apdu.data(), apdu.length()))
|
||||||
{
|
{
|
||||||
println("Plain APDU: ");
|
println("Plain APDU: ");
|
||||||
plainFrame.apdu().printPDU();
|
plainFrame.apdu().printPDU();
|
||||||
@ -168,10 +185,6 @@ void SecureApplicationLayer::dataConnectedIndication(Priority priority, uint16_t
|
|||||||
// Process decrypted inner APDU
|
// Process decrypted inner APDU
|
||||||
ApplicationLayer::dataConnectedIndication(priority, tsap, plainFrame.apdu());
|
ApplicationLayer::dataConnectedIndication(priority, tsap, plainFrame.apdu());
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
println("Decryption failed!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -212,106 +225,6 @@ void SecureApplicationLayer::dataConnectedRequest(uint16_t tsap, Priority priori
|
|||||||
ApplicationLayer::dataConnectedRequest(tsap, priority, apdu);
|
ApplicationLayer::dataConnectedRequest(tsap, priority, apdu);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* encryption/decryption stuff */
|
|
||||||
|
|
||||||
class TpTelegram
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TpTelegram()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
~TpTelegram()
|
|
||||||
{
|
|
||||||
if (_data)
|
|
||||||
delete[] _data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void parseByteArray(uint8_t *buf)
|
|
||||||
{
|
|
||||||
_ctrlField = buf[0];
|
|
||||||
_ctrlFieldExt = buf[1];
|
|
||||||
_srcAddr = buf[2] << 8 | buf[3];
|
|
||||||
_dstAddr = buf[4] << 8 | buf[5];
|
|
||||||
_dataLen = buf[6];
|
|
||||||
|
|
||||||
// Copy starting from TPCI octet
|
|
||||||
_dataLen += 1;
|
|
||||||
_data = new uint8_t (_dataLen);
|
|
||||||
memcpy(_data, &buf[7], _dataLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t SrcAddr()
|
|
||||||
{
|
|
||||||
return _srcAddr;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t DstAddr()
|
|
||||||
{
|
|
||||||
return _dstAddr;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t Tpci()
|
|
||||||
{
|
|
||||||
uint8_t tpci;
|
|
||||||
|
|
||||||
tpci = (_data[0] & 0xFC) >> 2;
|
|
||||||
|
|
||||||
return tpci;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t Apci()
|
|
||||||
{
|
|
||||||
uint16_t apci;
|
|
||||||
|
|
||||||
if (_dataLen > 1)
|
|
||||||
{
|
|
||||||
apci = (_data[0] & 0x03) << 8 | _data[1];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
apci = (_data[0] & 0x03);
|
|
||||||
}
|
|
||||||
|
|
||||||
return apci;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* Apdu()
|
|
||||||
{
|
|
||||||
return _data;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t ApduLen()
|
|
||||||
{
|
|
||||||
return _dataLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* Asdu()
|
|
||||||
{
|
|
||||||
return _data + 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t AsduLen()
|
|
||||||
{
|
|
||||||
return _dataLen - 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isSecureTelegram()
|
|
||||||
{
|
|
||||||
return Apci() == SecureService;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint8_t _ctrlField;
|
|
||||||
uint8_t _ctrlFieldExt;
|
|
||||||
uint16_t _srcAddr;
|
|
||||||
uint16_t _dstAddr;
|
|
||||||
uint8_t _dataLen;
|
|
||||||
uint8_t* _data;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t SecureApplicationLayer::calcAuthOnlyMac(uint8_t* apdu, uint8_t apduLength, uint8_t* key, uint8_t* iv, uint8_t* ctr0)
|
uint32_t SecureApplicationLayer::calcAuthOnlyMac(uint8_t* apdu, uint8_t apduLength, uint8_t* key, uint8_t* iv, uint8_t* ctr0)
|
||||||
{
|
{
|
||||||
uint16_t bufLen = 2 + apduLength; // 2 bytes for the length field (uint16_t)
|
uint16_t bufLen = 2 + apduLength; // 2 bytes for the length field (uint16_t)
|
||||||
@ -403,6 +316,69 @@ void SecureApplicationLayer::blockCtr0(uint8_t* buffer, uint8_t* seqNum, uint16_
|
|||||||
pBuf = pushByte(0x01, pBuf);
|
pBuf = pushByte(0x01, pBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uint8_t* SecureApplicationLayer::toolKey(uint16_t devAddr)
|
||||||
|
{
|
||||||
|
const uint8_t* toolKey = _secIfObj.propertyData(PID_TOOL_KEY);
|
||||||
|
return toolKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t* SecureApplicationLayer::p2pKey(uint16_t addressIndex)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
return _secIfObj.propertyData(PID_P2P_KEY_TABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t* SecureApplicationLayer::groupKey(uint16_t addressIndex)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
return _secIfObj.propertyData(PID_GRP_KEY_TABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t SecureApplicationLayer::groupAddressIndex(uint16_t groupAddr)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t SecureApplicationLayer::indAddressIndex(uint16_t indAddr)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t* SecureApplicationLayer::securityKey(uint16_t addr, bool isGroupAddress)
|
||||||
|
{
|
||||||
|
if (isGroupAddress)
|
||||||
|
{
|
||||||
|
uint16_t gaIndex = groupAddressIndex(addr);
|
||||||
|
if (gaIndex > 0)
|
||||||
|
return groupKey(gaIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uint16_t iaIndex = indAddressIndex(addr);
|
||||||
|
if (iaIndex > 0)
|
||||||
|
return 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;
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t SecureApplicationLayer::lastValidSequenceNumber(bool toolAcces, uint16_t srcAddr)
|
uint64_t SecureApplicationLayer::lastValidSequenceNumber(bool toolAcces, uint16_t srcAddr)
|
||||||
{
|
{
|
||||||
if (toolAcces)
|
if (toolAcces)
|
||||||
@ -414,7 +390,50 @@ uint64_t SecureApplicationLayer::lastValidSequenceNumber(bool toolAcces, uint16_
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SecureApplicationLayer::decrypt(uint8_t* plainApdu, uint16_t srcAddr, uint16_t dstAddr, uint8_t tpci, uint8_t* secureAsdu, uint16_t secureAdsuLength)
|
void SecureApplicationLayer::updateLastValidSequence(bool toolAccess, uint16_t remoteAddr, uint64_t seqNo)
|
||||||
|
{
|
||||||
|
if (toolAccess)
|
||||||
|
// TODO
|
||||||
|
//lastValidSequenceToolAccess.put(remoteAddr, seqNo);
|
||||||
|
lastValidSequenceNumberTool = seqNo;
|
||||||
|
//else
|
||||||
|
// TODO
|
||||||
|
//lastValidSequence.put(remoteAddr, seqNo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SecureApplicationLayer::sendSyncResponse(uint16_t dstAddr, bool dstAddrIsGroupAddr, bool toolAccess, uint16_t remoteNextSeqNum)
|
||||||
|
{
|
||||||
|
uint64_t ourNextSeqNum = nextSequenceNumber(toolAccess);
|
||||||
|
// asdu = ByteBuffer.allocate(12).put(sixBytes(ourNextSeq)).put(sixBytes(remoteNextSeq));
|
||||||
|
// response = secure(SecureSyncResponse, address(), dst, asdu.array(), toolAccess, true).get();
|
||||||
|
|
||||||
|
_lastSyncRes = millis();
|
||||||
|
|
||||||
|
// Send encrypted SyncResponse message using T_DATA_INDIVIDUAL
|
||||||
|
//dataIndividualRequest(AckType::AckDontCare, NetworkLayerParameter, SystemPriority, dstAddr, apdu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SecureApplicationLayer::receivedSyncRequest(uint16_t srcAddr, uint16_t dstAddr, bool dstAddrIsGroupAddr, bool toolAccess, uint8_t* seqNum, long challenge)
|
||||||
|
{
|
||||||
|
uint64_t nextRemoteSeqNum = ((uint64_t)seqNum[0] << 40) | ((uint64_t)seqNum[1] << 32) | ((uint64_t)seqNum[2] << 24) |
|
||||||
|
((uint64_t)seqNum[3] << 16) | ((uint64_t)seqNum[4] << 8) | (uint64_t)seqNum[5];
|
||||||
|
|
||||||
|
uint64_t nextSeqNum = 1 + lastValidSequenceNumber(toolAccess, srcAddr);
|
||||||
|
|
||||||
|
if (nextRemoteSeqNum > nextSeqNum)
|
||||||
|
{
|
||||||
|
updateLastValidSequence(toolAccess, srcAddr, nextRemoteSeqNum - 1);
|
||||||
|
nextSeqNum = nextRemoteSeqNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
_syncReqBroadcast = (dstAddr == 0x0000) && dstAddrIsGroupAddr;
|
||||||
|
|
||||||
|
uint16_t toAddr = _syncReqBroadcast ? dstAddr : srcAddr;
|
||||||
|
bool toIsGroupAddress = _syncReqBroadcast;
|
||||||
|
sendSyncResponse(toAddr, toIsGroupAddress, toolAccess, nextSeqNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SecureApplicationLayer::decrypt(uint8_t* plainApdu, uint16_t srcAddr, uint16_t dstAddr, bool dstAddrIsGroupAddr, uint8_t tpci, uint8_t* secureAsdu, uint16_t secureAdsuLength)
|
||||||
{
|
{
|
||||||
uint8_t extendedFrameFormat = 0;
|
uint8_t extendedFrameFormat = 0;
|
||||||
|
|
||||||
@ -424,18 +443,27 @@ bool SecureApplicationLayer::decrypt(uint8_t* plainApdu, uint16_t srcAddr, uint1
|
|||||||
pBuf = popByte(scf, secureAsdu);
|
pBuf = popByte(scf, secureAsdu);
|
||||||
|
|
||||||
bool toolAccess = ((scf & 0x80) == 0x80);
|
bool toolAccess = ((scf & 0x80) == 0x80);
|
||||||
// bool systemBroadcast = ((scf & 0x08) == 0x08); // not used for decryption
|
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
|
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);
|
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
|
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
|
||||||
|
|
||||||
|
bool syncReq = service == SecureSyncRequest;
|
||||||
|
bool syncRes = service == SecureSyncResponse;
|
||||||
|
|
||||||
|
uint8_t* key = dstAddrIsGroupAddr ? securityKey(dstAddr) : toolAccess ? toolKey(srcAddr == _deviceObj.induvidualAddress() ? dstAddr : srcAddr) : securityKey(srcAddr);
|
||||||
|
|
||||||
uint8_t seqNum[6];
|
uint8_t seqNum[6];
|
||||||
pBuf = popByteArray(seqNum, 6, pBuf);
|
pBuf = popByteArray(seqNum, 6, pBuf);
|
||||||
|
uint64_t receivedSeqNumber = ((uint64_t)seqNum[0] << 40) | ((uint64_t)seqNum[1] << 32) | ((uint64_t)seqNum[2] << 24) |
|
||||||
|
((uint64_t)seqNum[3] << 16) | ((uint64_t)seqNum[4] << 8) | (uint64_t)seqNum[5];
|
||||||
|
|
||||||
|
// Provide array for KNX serial number if it is a SyncRequest
|
||||||
|
// DataService and SyncResponse do not use this variable.
|
||||||
|
uint8_t knxSerialNumber[6];
|
||||||
|
|
||||||
if (service == SecureDataPdu)
|
if (service == SecureDataPdu)
|
||||||
{
|
{
|
||||||
uint64_t receivedSeqNumber = ((uint64_t)seqNum[0] << 40) | ((uint64_t)seqNum[1] << 32) | ((uint64_t)seqNum[2] << 24) |
|
|
||||||
((uint64_t)seqNum[3] << 16) | ((uint64_t)seqNum[4] << 8) | (uint64_t)seqNum[5];
|
|
||||||
uint64_t expectedSeqNumber = lastValidSequenceNumber(toolAccess, srcAddr) + 1;
|
uint64_t expectedSeqNumber = lastValidSequenceNumber(toolAccess, srcAddr) + 1;
|
||||||
|
|
||||||
if (receivedSeqNumber < expectedSeqNumber)
|
if (receivedSeqNumber < expectedSeqNumber)
|
||||||
@ -448,6 +476,38 @@ bool SecureApplicationLayer::decrypt(uint8_t* plainApdu, uint16_t srcAddr, uint1
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(syncReq)
|
||||||
|
{
|
||||||
|
pBuf = popByteArray(knxSerialNumber, 6, pBuf);
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
//final var request = pendingSyncRequests.get(src);
|
||||||
|
//if (request == null)
|
||||||
|
// return null;
|
||||||
|
|
||||||
|
// 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 seq to use it for block0 and ctr0
|
||||||
|
//final var challengeXorRandom = BitSet.valueOf(seq);
|
||||||
|
//final var challenge = BitSet.valueOf(sixBytes((long) request[0]));
|
||||||
|
//challengeXorRandom.xor(challenge);
|
||||||
|
//seq = challengeXorRandom.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t apduLength = secureAdsuLength - 1 - 6 - 4; // secureAdsuLength - sizeof(scf) - sizeof(seqNum) - sizeof(mac)
|
uint16_t apduLength = secureAdsuLength - 1 - 6 - 4; // secureAdsuLength - sizeof(scf) - sizeof(seqNum) - sizeof(mac)
|
||||||
pBuf = popByteArray(plainApdu, apduLength, pBuf);
|
pBuf = popByteArray(plainApdu, apduLength, pBuf);
|
||||||
@ -455,7 +515,7 @@ bool SecureApplicationLayer::decrypt(uint8_t* plainApdu, uint16_t srcAddr, uint1
|
|||||||
// Clear IV buffer
|
// Clear IV buffer
|
||||||
uint8_t iv[16] = {0x00};
|
uint8_t iv[16] = {0x00};
|
||||||
// Create first block B0 for AES CBC MAC calculation, used as IV later
|
// Create first block B0 for AES CBC MAC calculation, used as IV later
|
||||||
block0(iv, seqNum, srcAddr, dstAddr, false, extendedFrameFormat, tpci | (SecureService >> 8), SecureService & 0x00FF, apduLength);
|
block0(iv, seqNum, srcAddr, dstAddr, dstAddrIsGroupAddr, extendedFrameFormat, tpci | (SecureService >> 8), SecureService & 0x00FF, apduLength);
|
||||||
|
|
||||||
// Clear block counter0 buffer
|
// Clear block counter0 buffer
|
||||||
uint8_t ctr0[16] = {0x00};
|
uint8_t ctr0[16] = {0x00};
|
||||||
@ -481,6 +541,8 @@ bool SecureApplicationLayer::decrypt(uint8_t* plainApdu, uint16_t srcAddr, uint1
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memcpy(plainApdu, secureAsdu, apduLength);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -507,7 +569,12 @@ bool SecureApplicationLayer::decrypt(uint8_t* plainApdu, uint16_t srcAddr, uint1
|
|||||||
popByteArray(plainApdu, apduLength, &buffer[4]); // apdu is now decrypted (overwritten)
|
popByteArray(plainApdu, apduLength, &buffer[4]); // apdu is now decrypted (overwritten)
|
||||||
|
|
||||||
// Do calculations for Auth+Conf
|
// Do calculations for Auth+Conf
|
||||||
uint8_t associatedData[1] = {scf};
|
uint8_t associatedData[syncReq ? 7 : 1];
|
||||||
|
associatedData[0] = scf;
|
||||||
|
if (syncReq)
|
||||||
|
{
|
||||||
|
memcpy(&associatedData[1], knxSerialNumber, 6);
|
||||||
|
}
|
||||||
uint32_t calculatedMac = calcConfAuthMac(associatedData, sizeof(associatedData), plainApdu, apduLength, _key, iv);
|
uint32_t calculatedMac = calcConfAuthMac(associatedData, sizeof(associatedData), plainApdu, apduLength, _key, iv);
|
||||||
if (calculatedMac != decryptedMac)
|
if (calculatedMac != decryptedMac)
|
||||||
{
|
{
|
||||||
@ -518,39 +585,43 @@ bool SecureApplicationLayer::decrypt(uint8_t* plainApdu, uint16_t srcAddr, uint1
|
|||||||
print(decryptedMac, HEX);
|
print(decryptedMac, HEX);
|
||||||
return false;
|
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 = ((uint64_t)plainApdu[0] << 40) | ((uint64_t)plainApdu[1] << 32) | ((uint64_t)plainApdu[2] << 24) |
|
||||||
|
((uint64_t)plainApdu[3] << 16) | ((uint64_t)plainApdu[4] << 8) | (uint64_t)plainApdu[5];
|
||||||
|
receivedSyncRequest(srcAddr, dstAddr, dstAddrIsGroupAddr, toolAccess, seqNum, challenge);
|
||||||
|
}
|
||||||
|
else if (syncRes)
|
||||||
|
{
|
||||||
|
//receivedSyncResponse(srcAddr, toolAccess, plainApdu);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (srcAddr == _deviceObj.induvidualAddress())
|
||||||
|
{
|
||||||
|
//logger.trace("update next {}seq -> {}", toolAccess ? "tool access " : "", receivedSeq);
|
||||||
|
updateSequenceNumber(toolAccess, receivedSeqNumber + 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//logger.trace("update last valid {}seq of {} -> {}", toolAccess ? "tool access " : "", src, receivedSeq);
|
||||||
|
updateLastValidSequence(toolAccess, srcAddr, receivedSeqNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (syncReq || syncRes)
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
void SecureApplicationLayer::encrypt(uint8_t* buffer, uint16_t srcAddr, uint16_t dstAddr, bool dstAddrIsGroupAddr, uint8_t tpci, uint8_t* apdu, uint16_t apduLength)
|
||||||
void SecureApplicationLayer::test_datasecure_decrypt()
|
|
||||||
{
|
|
||||||
TpTelegram t;
|
|
||||||
t.parseByteArray(secureTelegram);
|
|
||||||
|
|
||||||
if (t.isSecureTelegram())
|
|
||||||
{
|
|
||||||
uint16_t apduLength = t.AsduLen() - 1 - 6 - 4; // secureAdsuLength - sizeof(scf) - sizeof(seqNum) - sizeof(mac)
|
|
||||||
uint8_t apdu[apduLength];
|
|
||||||
|
|
||||||
if (decrypt(apdu, t.SrcAddr(), t.DstAddr(), t.Tpci(), t.Asdu(), t.AsduLen()))
|
|
||||||
{
|
|
||||||
std::cout << "Plain APDU: ";
|
|
||||||
for (uint8_t i = 0; i< apduLength; i++)
|
|
||||||
{
|
|
||||||
std::cout << std::hex << static_cast<unsigned int>(apdu[i]) << " ";
|
|
||||||
}
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::cout << "Telegram is not secured!" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
void SecureApplicationLayer::encrypt(uint8_t* buffer, uint16_t srcAddr, uint16_t dstAddr, uint8_t tpci, uint8_t* apdu, uint16_t apduLength)
|
|
||||||
{
|
{
|
||||||
uint8_t scf = 0x90;
|
uint8_t scf = 0x90;
|
||||||
uint8_t seqNum[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04};
|
uint8_t seqNum[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04};
|
||||||
@ -560,7 +631,7 @@ void SecureApplicationLayer::encrypt(uint8_t* buffer, uint16_t srcAddr, uint16_t
|
|||||||
// Clear IV buffer
|
// Clear IV buffer
|
||||||
uint8_t iv[16] = {0x00};
|
uint8_t iv[16] = {0x00};
|
||||||
// Create first block B0 for AES CBC MAC calculation, used as IV later
|
// Create first block B0 for AES CBC MAC calculation, used as IV later
|
||||||
block0(iv, seqNum, srcAddr, dstAddr, false, extendedFrameFormat, tpci | (SecureService >> 8), SecureService & 0x00FF, apduLength);
|
block0(iv, seqNum, srcAddr, dstAddr, dstAddrIsGroupAddr, extendedFrameFormat, tpci | (SecureService >> 8), SecureService & 0x00FF, apduLength);
|
||||||
|
|
||||||
// Clear block counter0 buffer
|
// Clear block counter0 buffer
|
||||||
uint8_t ctr0[16] = {0x00};
|
uint8_t ctr0[16] = {0x00};
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#include "knx_types.h"
|
#include "knx_types.h"
|
||||||
#include "apdu.h"
|
#include "apdu.h"
|
||||||
|
|
||||||
|
class DeviceObject;
|
||||||
|
class SecurityInterfaceObject;
|
||||||
class AssociationTableObject;
|
class AssociationTableObject;
|
||||||
class BusAccessUnit;
|
class BusAccessUnit;
|
||||||
/**
|
/**
|
||||||
@ -22,7 +24,7 @@ class SecureApplicationLayer : public ApplicationLayer
|
|||||||
* @param assocTable The AssociationTable is used to translate between asap (i.e. group objects) and group addresses.
|
* @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
|
* @param bau methods are called here depending of the content of the APDU
|
||||||
*/
|
*/
|
||||||
SecureApplicationLayer(AssociationTableObject& assocTable, BusAccessUnit& bau);
|
SecureApplicationLayer(DeviceObject& deviceObj, SecurityInterfaceObject& secIfObj, AssociationTableObject& assocTable, BusAccessUnit& bau);
|
||||||
|
|
||||||
// from transport layer
|
// from transport layer
|
||||||
virtual void dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu) override;
|
virtual void dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu) override;
|
||||||
@ -56,12 +58,32 @@ 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 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 blockCtr0(uint8_t* buffer, uint8_t* seqNum, uint16_t indSrcAddr, uint16_t dstAddr);
|
||||||
|
|
||||||
|
const uint8_t *toolKey(uint16_t devAddr);
|
||||||
|
const uint8_t* securityKey(uint16_t addr, bool isGroupAddress);
|
||||||
|
|
||||||
|
uint16_t indAddressIndex(uint16_t indAddr); // returns 1-based index of address in security IA table
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
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);
|
uint64_t lastValidSequenceNumber(bool toolAcces, uint16_t srcAddr);
|
||||||
|
void updateLastValidSequence(bool toolAccess, uint16_t remoteAddr, uint64_t seqNo);
|
||||||
|
|
||||||
bool decrypt(uint8_t* plainApdu, uint16_t srcAddr, uint16_t dstAddr, uint8_t tpci, uint8_t* secureAsdu, uint16_t secureAdsuLength);
|
void sendSyncResponse(uint16_t dstAddr, bool dstAddrIsGroupAddr, bool toolAccess, uint16_t remoteNextSeqNum);
|
||||||
void encrypt(uint8_t* buffer, uint16_t srcAddr, uint16_t dstAddr, uint8_t tpci, uint8_t* apdu, uint16_t apduLength);
|
void receivedSyncRequest(uint16_t srcAddr, uint16_t dstAddr, bool dstAddrIsGroupAddr, bool toolAccess, uint8_t* seq, long challenge);
|
||||||
|
|
||||||
// Our FDSK
|
bool decrypt(uint8_t* plainApdu, uint16_t srcAddr, uint16_t dstAddr, bool dstAddrIsGroupAddr, uint8_t tpci, uint8_t* secureAsdu, uint16_t secureAdsuLength);
|
||||||
static uint8_t _key[];
|
void encrypt(uint8_t* buffer, uint16_t srcAddr, uint16_t dstAddr, bool dstAddrIsGroupAddr, uint8_t tpci, uint8_t* apdu, uint16_t apduLength);
|
||||||
|
|
||||||
|
bool _syncReqBroadcast;
|
||||||
|
uint32_t _lastSyncRes;
|
||||||
|
|
||||||
|
SecurityInterfaceObject& _secIfObj;
|
||||||
|
DeviceObject& _deviceObj;
|
||||||
};
|
};
|
||||||
|
@ -8,6 +8,9 @@
|
|||||||
#include "callback_property.h"
|
#include "callback_property.h"
|
||||||
#include "function_property.h"
|
#include "function_property.h"
|
||||||
|
|
||||||
|
// Our FDSK
|
||||||
|
uint8_t SecurityInterfaceObject::_fdsk[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
|
||||||
|
|
||||||
SecurityInterfaceObject::SecurityInterfaceObject()
|
SecurityInterfaceObject::SecurityInterfaceObject()
|
||||||
{
|
{
|
||||||
Property* properties[] =
|
Property* properties[] =
|
||||||
@ -50,7 +53,7 @@ SecurityInterfaceObject::SecurityInterfaceObject()
|
|||||||
[](SecurityInterfaceObject* obj, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) -> void {
|
[](SecurityInterfaceObject* obj, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) -> void {
|
||||||
// TODO
|
// TODO
|
||||||
}),
|
}),
|
||||||
new DataProperty( PID_TOOL_KEY, true, PDT_GENERIC_16, 1, ReadLv3 | WriteLv0, (uint16_t)0 ), // TODO: value (default is FDSK)
|
new DataProperty( PID_TOOL_KEY, true, PDT_GENERIC_16, 1, ReadLv3 | WriteLv0, (uint8_t*) _fdsk ), // default is FDSK
|
||||||
new DataProperty( PID_SECURITY_REPORT, true, PDT_BITSET8, 1, ReadLv3 | WriteLv0, (uint16_t)0 ), // TODO: value
|
new DataProperty( PID_SECURITY_REPORT, true, PDT_BITSET8, 1, ReadLv3 | WriteLv0, (uint16_t)0 ), // TODO: value
|
||||||
new DataProperty( PID_SECURITY_REPORT_CONTROL, true, PDT_BINARY_INFORMATION, 1, ReadLv3 | WriteLv0, (uint16_t)0 ), // TODO: value
|
new DataProperty( PID_SECURITY_REPORT_CONTROL, true, PDT_BINARY_INFORMATION, 1, ReadLv3 | WriteLv0, (uint16_t)0 ), // TODO: value
|
||||||
new DataProperty( PID_SEQUENCE_NUMBER_SENDING, true, PDT_GENERIC_06, 1, ReadLv3 | WriteLv0, (uint16_t)0 ), // TODO: value
|
new DataProperty( PID_SEQUENCE_NUMBER_SENDING, true, PDT_GENERIC_06, 1, ReadLv3 | WriteLv0, (uint16_t)0 ), // TODO: value
|
||||||
|
@ -13,5 +13,8 @@ public:
|
|||||||
const uint8_t* restore(const uint8_t* buffer) override;
|
const uint8_t* restore(const uint8_t* buffer) override;
|
||||||
uint16_t saveSize() override;
|
uint16_t saveSize() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Our FDSK
|
||||||
|
static uint8_t _fdsk[];
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user