2073 lines
64 KiB
C
2073 lines
64 KiB
C
//
|
|
// --------------------------------------------------------------------------
|
|
// Gurux Ltd
|
|
//
|
|
//
|
|
//
|
|
// Filename: $HeadURL$
|
|
//
|
|
// Version: $Revision$,
|
|
// $Date$
|
|
// $Author$
|
|
//
|
|
// Copyright (c) Gurux Ltd
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// This file is a part of Gurux Device Framework.
|
|
//
|
|
// Gurux Device Framework is Open Source 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; version 2 of the License.
|
|
// Gurux Device Framework 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.
|
|
//
|
|
// This code is licensed under the GNU General Public License v2.
|
|
// Full text may be retrieved at http://www.gnu.org/licenses/gpl-2.0.txt
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include "gxignore.h"
|
|
#include "gxmem.h"
|
|
#include "enums.h"
|
|
#include "helpers.h"
|
|
#include "apdu.h"
|
|
#include "errorcodes.h"
|
|
#include "ciphering.h"
|
|
#include "serverevents.h"
|
|
|
|
#ifndef DLMS_IGNORE_CLIENT
|
|
/**
|
|
* Retrieves the string that indicates the level of authentication, if any.
|
|
*/
|
|
int apdu_getAuthenticationString(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* data)
|
|
{
|
|
int ret = 0;
|
|
gxByteBuffer* callingAuthenticationValue = NULL;
|
|
if (settings->authentication != DLMS_AUTHENTICATION_NONE
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
|| settings->cipher.security != DLMS_SECURITY_NONE
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
)
|
|
{
|
|
unsigned char p[] = { 0x60, 0x85, 0x74, 0x05, 0x08, 0x02 };
|
|
// Add sender ACSE-requirements field component.
|
|
if ((ret = bb_setUInt8(data, (uint16_t)BER_TYPE_CONTEXT | (char)PDU_TYPE_SENDER_ACSE_REQUIREMENTS)) != 0 ||
|
|
(ret = bb_setUInt8(data, 2)) != 0 ||
|
|
(ret = bb_setUInt8(data, BER_TYPE_BIT_STRING | BER_TYPE_OCTET_STRING)) != 0 ||
|
|
(ret = bb_setUInt8(data, 0x80)) != 0 ||
|
|
(ret = bb_setUInt8(data, (uint16_t)BER_TYPE_CONTEXT | (char)PDU_TYPE_MECHANISM_NAME)) != 0 ||
|
|
// Len
|
|
(ret = bb_setUInt8(data, 7)) != 0 ||
|
|
// OBJECT IDENTIFIER
|
|
(ret = bb_set(data, p, 6)) != 0 ||
|
|
(ret = bb_setUInt8(data, settings->authentication)) != 0)
|
|
{
|
|
//Error code is returned at the end of the function.
|
|
}
|
|
}
|
|
// If authentication is used.
|
|
if (settings->authentication != DLMS_AUTHENTICATION_NONE)
|
|
{
|
|
// Add Calling authentication information.
|
|
if (settings->authentication == DLMS_AUTHENTICATION_LOW)
|
|
{
|
|
if (settings->password.size != 0)
|
|
{
|
|
callingAuthenticationValue = &settings->password;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
callingAuthenticationValue = &settings->ctoSChallenge;
|
|
}
|
|
// 0xAC
|
|
if ((ret = bb_setUInt8(data, BER_TYPE_CONTEXT | BER_TYPE_CONSTRUCTED | PDU_TYPE_CALLING_AUTHENTICATION_VALUE)) == 0 &&
|
|
// Len
|
|
(ret = bb_setUInt8(data, (unsigned char)(2 + bb_size(callingAuthenticationValue)))) == 0 &&
|
|
// Add authentication information.
|
|
(ret = bb_setUInt8(data, BER_TYPE_CONTEXT)) == 0 &&
|
|
// Len.
|
|
(ret = bb_setUInt8(data, (unsigned char)bb_size(callingAuthenticationValue))) == 0)
|
|
{
|
|
if (callingAuthenticationValue != NULL)
|
|
{
|
|
ret = bb_set(data, callingAuthenticationValue->data, bb_size(callingAuthenticationValue));
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
#endif //DLMS_IGNORE_CLIENT
|
|
|
|
/**
|
|
* Code application context name.
|
|
*
|
|
* @param settings
|
|
* DLMS settings->
|
|
* @param data
|
|
* Byte buffer where data is saved.
|
|
* @param cipher
|
|
* Is ciphering settings->
|
|
*/
|
|
int apdu_generateApplicationContextName(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* data)
|
|
{
|
|
int ret;
|
|
//ProtocolVersion
|
|
if (settings->protocolVersion != 0)
|
|
{
|
|
if ((ret = bb_setUInt8(data, BER_TYPE_CONTEXT | PDU_TYPE_PROTOCOL_VERSION)) != 0 ||
|
|
(ret = bb_setUInt8(data, 2)) != 0 ||
|
|
//Un-used bits.
|
|
(ret = bb_setUInt8(data, 2)) != 0 ||
|
|
(ret = bb_setUInt8(data, settings->protocolVersion)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
unsigned char ciphered;
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
ciphered = isCiphered(&settings->cipher);
|
|
#else
|
|
ciphered = 0;
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
|
|
// Application context name tag
|
|
if ((ret = bb_setUInt8(data, (BER_TYPE_CONTEXT | BER_TYPE_CONSTRUCTED | PDU_TYPE_APPLICATION_CONTEXT_NAME))) != 0 ||
|
|
// Len
|
|
(ret = bb_setUInt8(data, 0x09)) != 0 ||
|
|
(ret = bb_setUInt8(data, BER_TYPE_OBJECT_IDENTIFIER)) != 0 ||
|
|
// Len
|
|
(ret = bb_setUInt8(data, 0x07)) != 0 ||
|
|
(ret = bb_setUInt8(data, 0x60)) != 0 ||
|
|
(ret = bb_setUInt8(data, 0x85)) != 0 ||
|
|
(ret = bb_setUInt8(data, 0x74)) != 0 ||
|
|
(ret = bb_setUInt8(data, 0x05)) != 0 ||
|
|
(ret = bb_setUInt8(data, 0x08)) != 0 ||
|
|
(ret = bb_setUInt8(data, 0x01)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
if (settings->useLogicalNameReferencing)
|
|
{
|
|
if ((ret = bb_setUInt8(data, ciphered ? 0x03 : 0x01)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((ret = bb_setUInt8(data, ciphered ? 0x04 : 0x02)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
// Add system title.
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
if (!settings->server && (ciphered || settings->authentication == DLMS_AUTHENTICATION_HIGH_GMAC))
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
if (settings->cipher.systemTitle.size == 0)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
// Add calling-AP-title
|
|
if ((ret = bb_setUInt8(data, (BER_TYPE_CONTEXT | BER_TYPE_CONSTRUCTED | 6))) != 0 ||
|
|
// LEN
|
|
(ret = bb_setUInt8(data, (unsigned char)(2 + 8))) != 0 ||
|
|
(ret = bb_setUInt8(data, BER_TYPE_OCTET_STRING)) != 0 ||
|
|
// LEN
|
|
(ret = bb_setUInt8(data, (unsigned char)8)) != 0 ||
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
(ret = bb_set(data, settings->cipher.systemTitle.data, 8)) != 0)
|
|
#else
|
|
(ret = bb_set(data, settings->cipher.systemTitle, 8)) != 0)
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
//Add CallingAEInvocationId.
|
|
if (!settings->server && settings->userId != -1 && settings->cipher.security != DLMS_SECURITY_NONE)
|
|
{
|
|
if ((ret = bb_setUInt8(data, (BER_TYPE_CONTEXT | BER_TYPE_CONSTRUCTED | PDU_TYPE_CALLING_AE_INVOCATION_ID))) != 0 ||
|
|
//LEN
|
|
(ret = bb_setUInt8(data, 3)) != 0 ||
|
|
(ret = bb_setUInt8(data, BER_TYPE_INTEGER)) != 0 ||
|
|
//LEN
|
|
(ret = bb_setUInt8(data, 1)) != 0 ||
|
|
(ret = bb_setUInt8(data, (unsigned char)settings->userId)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
return 0;
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
unsigned char useDedicatedKey(dlmsSettings* settings)
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
if (settings->cipher.dedicatedKey == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
return settings->cipher.dedicatedKey->size == 16;
|
|
#else
|
|
return memcmp(settings->cipher.dedicatedKey, EMPTY_KEY, sizeof(EMPTY_KEY)) != 0;
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
|
|
// Reserved for internal use.
|
|
int apdu_getConformanceFromArray(gxByteBuffer* data, uint32_t* value)
|
|
{
|
|
int ret;
|
|
unsigned char v;
|
|
uint32_t tmp;
|
|
if ((ret = bb_getUInt8(data, &v)) == 0)
|
|
{
|
|
*value = hlp_swapBits(v);
|
|
if ((ret = bb_getUInt8(data, &v)) == 0)
|
|
{
|
|
tmp = hlp_swapBits(v);
|
|
tmp <<= 8;
|
|
*value |= tmp;
|
|
if ((ret = bb_getUInt8(data, &v)) == 0)
|
|
{
|
|
tmp = hlp_swapBits(v);
|
|
tmp <<= 16;
|
|
*value |= tmp;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// Reserved for internal use.
|
|
int apdu_setConformanceToArray(uint32_t value, gxByteBuffer* data)
|
|
{
|
|
int ret;
|
|
if ((ret = bb_setUInt8(data, hlp_swapBits((unsigned char)(value & 0xFF)))) == 0)
|
|
{
|
|
value >>= 8;
|
|
if ((ret = bb_setUInt8(data, hlp_swapBits((unsigned char)(value & 0xFF)))) == 0)
|
|
{
|
|
value >>= 8;
|
|
ret = bb_setUInt8(data, hlp_swapBits((unsigned char)(value & 0xFF)));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Generate User information initiate request.
|
|
*
|
|
* @param settings
|
|
* DLMS settings->
|
|
* @param cipher
|
|
* @param data
|
|
*/
|
|
int apdu_getInitiateRequest(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* data)
|
|
{
|
|
int ret;
|
|
// Tag for xDLMS-Initiate request
|
|
bb_setUInt8(data, DLMS_COMMAND_INITIATE_REQUEST);
|
|
// Usage field for the response allowed component.
|
|
|
|
#ifdef DLMS_IGNORE_HIGH_GMAC
|
|
bb_setUInt8(data, 0);
|
|
#else
|
|
// Usage field for dedicated-key component.
|
|
if (settings->cipher.security == DLMS_SECURITY_NONE || !useDedicatedKey(settings))
|
|
{
|
|
bb_setUInt8(data, 0);
|
|
}
|
|
else
|
|
{
|
|
bb_setUInt8(data, 1);
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
hlp_setObjectCount(settings->cipher.dedicatedKey->size, data);
|
|
bb_set(data, settings->cipher.dedicatedKey->data, settings->cipher.dedicatedKey->size);
|
|
#else
|
|
hlp_setObjectCount(8, data);
|
|
bb_set(data, settings->cipher.dedicatedKey, 8);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
|
|
// encoding of the response-allowed component (bool DEFAULT TRUE)
|
|
// usage flag (FALSE, default value TRUE conveyed)
|
|
bb_setUInt8(data, 0);
|
|
|
|
// Usage field of the proposed-quality-of-service component.
|
|
if (settings->qualityOfService == 0)
|
|
{
|
|
// Not used
|
|
bb_setUInt8(data, 0x00);
|
|
}
|
|
else
|
|
{
|
|
bb_setUInt8(data, 0x01);
|
|
bb_setUInt8(data, settings->qualityOfService);
|
|
}
|
|
if ((ret = bb_setUInt8(data, settings->dlmsVersionNumber)) != 0 ||
|
|
// Tag for conformance block
|
|
(ret = bb_setUInt8(data, 0x5F)) != 0 ||
|
|
(ret = bb_setUInt8(data, 0x1F)) != 0 ||
|
|
// length of the conformance block
|
|
(ret = bb_setUInt8(data, 0x04)) != 0 ||
|
|
// encoding the number of unused bits in the bit string
|
|
(ret = bb_setUInt8(data, 0x00)) != 0 ||
|
|
(ret = apdu_setConformanceToArray(settings->proposedConformance, data)) != 0 ||
|
|
(ret = bb_setUInt16(data, settings->maxPduSize)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_CLIENT
|
|
/**
|
|
* Generate user information.
|
|
*
|
|
* @param settings
|
|
* DLMS settings->
|
|
* @param cipher
|
|
* @param data
|
|
* Generated user information.
|
|
*/
|
|
int apdu_generateUserInformation(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* data)
|
|
{
|
|
int ret = 0;
|
|
bb_setUInt8(data, BER_TYPE_CONTEXT | BER_TYPE_CONSTRUCTED | PDU_TYPE_USER_INFORMATION);
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
if (!isCiphered(&settings->cipher))
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
{
|
|
// Length for AARQ user field
|
|
bb_setUInt8(data, 0x10);
|
|
// Coding the choice for user-information (Octet STRING, universal)
|
|
bb_setUInt8(data, BER_TYPE_OCTET_STRING);
|
|
// Length
|
|
bb_setUInt8(data, 0x0E);
|
|
if ((ret = apdu_getInitiateRequest(settings, data)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
else
|
|
{
|
|
gxByteBuffer crypted;
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
BYTE_BUFFER_INIT(&crypted);
|
|
#else
|
|
unsigned char tmp[25 + 12];
|
|
bb_attach(&crypted, tmp, 0, sizeof(tmp));
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
|
|
if ((ret = apdu_getInitiateRequest(settings, &crypted)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
ret = cip_encrypt(
|
|
&settings->cipher,
|
|
settings->cipher.security,
|
|
DLMS_COUNT_TYPE_PACKET,
|
|
settings->cipher.invocationCounter,
|
|
DLMS_COMMAND_GLO_INITIATE_REQUEST,
|
|
settings->cipher.systemTitle.data,
|
|
&settings->cipher.blockCipherKey,
|
|
&crypted);
|
|
#else
|
|
ret = cip_encrypt(
|
|
&settings->cipher,
|
|
settings->cipher.security,
|
|
DLMS_COUNT_TYPE_PACKET,
|
|
settings->cipher.invocationCounter,
|
|
DLMS_COMMAND_GLO_INITIATE_REQUEST,
|
|
settings->cipher.systemTitle,
|
|
settings->cipher.blockCipherKey,
|
|
&crypted);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
if (ret == 0)
|
|
{
|
|
// Length for AARQ user field
|
|
if ((ret = bb_setUInt8(data, (unsigned char)(2 + crypted.size))) != 0 ||
|
|
// Coding the choice for user-information (Octet string, universal)
|
|
(ret = bb_setUInt8(data, BER_TYPE_OCTET_STRING)) != 0 ||
|
|
(ret = bb_setUInt8(data, (unsigned char)crypted.size)) != 0 ||
|
|
(ret = bb_set2(data, &crypted, 0, crypted.size)) != 0)
|
|
{
|
|
//Error code is returned at the end of the function.
|
|
}
|
|
}
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
bb_clear(&crypted);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
return ret;
|
|
}
|
|
#endif //DLMS_IGNORE_CLIENT
|
|
|
|
/**
|
|
* Parse User Information from PDU.
|
|
*/
|
|
int apdu_parseUserInformation(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* data,
|
|
unsigned char ciphered,
|
|
unsigned char* command)
|
|
{
|
|
int ret;
|
|
unsigned char response;
|
|
uint16_t pduSize;
|
|
unsigned char ch, len, tag;
|
|
uint32_t v;
|
|
if ((ret = bb_getUInt8(data, &len)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (data->size - data->position < len)
|
|
{
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
// Encoding the choice for user information
|
|
if ((ret = bb_getUInt8(data, &tag)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (tag != 0x4)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_TAG;
|
|
}
|
|
if ((ret = bb_getUInt8(data, &len)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Tag for xDLMS-Initate.response
|
|
if ((ret = bb_getUInt8(data, &tag)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
DLMS_SECURITY security;
|
|
DLMS_SECURITY_SUITE suite;
|
|
uint64_t invocationCounter;
|
|
if (tag == DLMS_COMMAND_GLO_INITIATE_RESPONSE ||
|
|
tag == DLMS_COMMAND_GLO_INITIATE_REQUEST ||
|
|
tag == DLMS_COMMAND_DED_INITIATE_RESPONSE ||
|
|
tag == DLMS_COMMAND_DED_INITIATE_REQUEST ||
|
|
tag == DLMS_COMMAND_GENERAL_GLO_CIPHERING ||
|
|
tag == DLMS_COMMAND_GENERAL_DED_CIPHERING)
|
|
{
|
|
*command = (unsigned char)tag;
|
|
data->position = (data->position - 1);
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
if ((ret = cip_decrypt(&settings->cipher,
|
|
settings->sourceSystemTitle,
|
|
&settings->cipher.blockCipherKey,
|
|
data,
|
|
&security,
|
|
&suite,
|
|
&invocationCounter)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
#else
|
|
if ((ret = cip_decrypt(&settings->cipher,
|
|
settings->sourceSystemTitle,
|
|
settings->cipher.blockCipherKey,
|
|
data,
|
|
&security,
|
|
&suite,
|
|
&invocationCounter)) != 0)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_DECIPHERING_ERROR;
|
|
}
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
|
|
if (settings->expectedSecurityPolicy != 0xFF && security != settings->expectedSecurityPolicy << 4)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_DECIPHERING_ERROR;
|
|
}
|
|
if (settings->expectedSecuritySuite != 0xFF && suite != settings->expectedSecuritySuite)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_SECURITY_SUITE;
|
|
}
|
|
if (settings->expectedInvocationCounter != NULL)
|
|
{
|
|
if (invocationCounter < 1 + *settings->expectedInvocationCounter)
|
|
{
|
|
return DLMS_ERROR_CODE_INVOCATION_COUNTER_TOO_SMALL;
|
|
}
|
|
#ifdef DLMS_COSEM_INVOCATION_COUNTER_SIZE64
|
|
*settings->expectedInvocationCounter = 1 + invocationCounter;
|
|
#else
|
|
*settings->expectedInvocationCounter = (uint32_t)(1 + invocationCounter);
|
|
#endif //DLMS_COSEM_INVOCATION_COUNTER_SIZE64
|
|
|
|
}
|
|
//If client system title doesn't match.
|
|
if (settings->expectedClientSystemTitle != NULL &&
|
|
memcmp(settings->expectedClientSystemTitle, EMPTY_SYSTEM_TITLE, 8) != 0 &&
|
|
memcmp(settings->sourceSystemTitle, settings->expectedClientSystemTitle, 8) != 0)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_DECIPHERING_ERROR;
|
|
}
|
|
|
|
settings->cipher.security = security;
|
|
settings->cipher.suite = suite;
|
|
if ((ret = bb_getUInt8(data, &tag)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
response = tag == DLMS_COMMAND_INITIATE_RESPONSE;
|
|
if (response)
|
|
{
|
|
// Optional usage field of the negotiated quality of service
|
|
// component
|
|
if ((ret = bb_getUInt8(data, &tag)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (tag != 0)
|
|
{
|
|
if ((ret = bb_getUInt8(data, &settings->qualityOfService)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
else if (tag == DLMS_COMMAND_INITIATE_REQUEST)
|
|
{
|
|
// Optional usage field of the negotiated quality of service
|
|
// component
|
|
if ((ret = bb_getUInt8(data, &tag)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Dedicated key.
|
|
#ifdef DLMS_IGNORE_HIGH_GMAC
|
|
|
|
#else
|
|
if (tag != 0)
|
|
{
|
|
//Return error if ciphering is not used.
|
|
if (!ciphered || settings->cipher.security != DLMS_SECURITY_AUTHENTICATION_ENCRYPTION)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
if ((ret = bb_getUInt8(data, &len)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (len != 16)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
if (settings->cipher.dedicatedKey == NULL)
|
|
{
|
|
settings->cipher.dedicatedKey = gxmalloc(sizeof(gxByteBuffer));
|
|
BYTE_BUFFER_INIT(settings->cipher.dedicatedKey);
|
|
}
|
|
else
|
|
{
|
|
bb_clear(settings->cipher.dedicatedKey);
|
|
}
|
|
bb_set2(settings->cipher.dedicatedKey, data, data->position, len);
|
|
#else
|
|
hlp_setObjectCount(8, data);
|
|
bb_get(data, settings->cipher.dedicatedKey, len);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
else
|
|
{
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
if (settings->cipher.dedicatedKey != NULL)
|
|
{
|
|
bb_clear(settings->cipher.dedicatedKey);
|
|
gxfree(settings->cipher.dedicatedKey);
|
|
settings->cipher.dedicatedKey = NULL;
|
|
}
|
|
#else
|
|
memset(settings->cipher.dedicatedKey, 0, 8);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
// Optional usage field of the negotiated quality of service
|
|
// component
|
|
if ((ret = bb_getUInt8(data, &tag)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Skip if used.
|
|
if (tag != 0)
|
|
{
|
|
if ((ret = bb_getUInt8(data, &tag)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
// Optional usage field of the proposed quality of service component
|
|
if ((ret = bb_getUInt8(data, &tag)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (tag != 0)
|
|
{
|
|
if ((ret = bb_getUInt8(data, &settings->qualityOfService)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_TAG;
|
|
}
|
|
// Get DLMS version number.
|
|
if (settings->server)
|
|
{
|
|
if ((ret = bb_getUInt8(data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
settings->dlmsVersionNumber = ch;
|
|
}
|
|
else
|
|
{
|
|
if ((ret = bb_getUInt8(data, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch < 6)
|
|
{
|
|
//Invalid DLMS version number.
|
|
return DLMS_ERROR_CODE_INVALID_VERSION_NUMBER;
|
|
}
|
|
}
|
|
|
|
// Tag for conformance block
|
|
if ((ret = bb_getUInt8(data, &tag)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (tag != 0x5F)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_TAG;
|
|
}
|
|
// Old Way...
|
|
tag = data->data[data->position];
|
|
if (tag == 0x1F)
|
|
{
|
|
data->position = (data->position + 1);
|
|
}
|
|
if ((ret = bb_getUInt8(data, &len)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// The number of unused bits in the bit string.
|
|
if ((ret = bb_getUInt8(data, &tag)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((ret = apdu_getConformanceFromArray(data, &v)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (settings->server)
|
|
{
|
|
settings->negotiatedConformance = (DLMS_CONFORMANCE)(v & settings->proposedConformance);
|
|
//Remove general protection if ciphered connection is not used.
|
|
if (!ciphered && (settings->negotiatedConformance & DLMS_CONFORMANCE_GENERAL_PROTECTION) != 0)
|
|
{
|
|
settings->negotiatedConformance &= ~DLMS_CONFORMANCE_GENERAL_PROTECTION;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
settings->negotiatedConformance = (DLMS_CONFORMANCE)v;
|
|
}
|
|
if (settings->server)
|
|
{
|
|
if ((ret = bb_getUInt16(data, &pduSize)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
//If client asks too high PDU.
|
|
if (pduSize > settings->maxServerPDUSize)
|
|
{
|
|
pduSize = settings->maxServerPDUSize;
|
|
}
|
|
settings->maxPduSize = pduSize;
|
|
}
|
|
else
|
|
{
|
|
if ((ret = bb_getUInt16(data, &pduSize)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
settings->maxPduSize = pduSize;
|
|
}
|
|
if (response)
|
|
{
|
|
// VAA Name
|
|
uint16_t vaa;
|
|
if ((ret = bb_getUInt16(data, &vaa)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (vaa == 0x0007)
|
|
{
|
|
// If LN
|
|
if (!settings->useLogicalNameReferencing)
|
|
{
|
|
//Invalid VAA.
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
else if (vaa == 0xFA00)
|
|
{
|
|
// If SN
|
|
if (settings->useLogicalNameReferencing)
|
|
{
|
|
//Invalid VAA.
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Unknown VAA.
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Parse application context name.
|
|
*
|
|
* @param settings
|
|
* DLMS settings->
|
|
* @param buff
|
|
* Received data->
|
|
*/
|
|
int apdu_parseApplicationContextName(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* buff,
|
|
unsigned char* ciphered)
|
|
{
|
|
int ret;
|
|
unsigned char len, ch;
|
|
// Get length.
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (buff->size - buff->position < len)
|
|
{
|
|
//Encoding failed. Not enough data->
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch != 0x6)
|
|
{
|
|
//Encoding failed. Not an Object ID.
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
if (settings->server)
|
|
{
|
|
settings->cipher.security = DLMS_SECURITY_NONE;
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
// Object ID length.
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch != 0x60)
|
|
{
|
|
//Encoding failed. Not an Object ID.
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch != 0x85)
|
|
{
|
|
//Encoding failed. Not an Object ID.
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch != 0x74)
|
|
{
|
|
//Encoding failed. Not an Object ID.
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch != 0x05)
|
|
{
|
|
//Encoding failed. Not an Object ID.
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch != 0x08)
|
|
{
|
|
//Encoding failed. Not an Object ID.
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch != 0x01)
|
|
{
|
|
//Encoding failed. Not an Object ID.
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (settings->useLogicalNameReferencing)
|
|
{
|
|
*ciphered = ch == 3;
|
|
if (ch == 1 || *ciphered)
|
|
{
|
|
return 0;
|
|
}
|
|
return DLMS_ERROR_CODE_FALSE;
|
|
}
|
|
*ciphered = ch == 4;
|
|
if (ch == 2 || *ciphered)
|
|
{
|
|
return 0;
|
|
}
|
|
return DLMS_ERROR_CODE_FALSE;
|
|
}
|
|
|
|
int apdu_validateAare(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* buff)
|
|
{
|
|
int ret;
|
|
unsigned char tag;
|
|
if ((ret = bb_getUInt8(buff, &tag)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (settings->server)
|
|
{
|
|
if (tag != (BER_TYPE_APPLICATION
|
|
| BER_TYPE_CONSTRUCTED
|
|
| PDU_TYPE_PROTOCOL_VERSION))
|
|
{
|
|
ret = DLMS_ERROR_CODE_INVALID_TAG;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (tag != (BER_TYPE_APPLICATION
|
|
| BER_TYPE_CONSTRUCTED
|
|
| PDU_TYPE_APPLICATION_CONTEXT_NAME))
|
|
{
|
|
ret = DLMS_ERROR_CODE_INVALID_TAG;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int apdu_updatePassword(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* buff)
|
|
{
|
|
int ret;
|
|
unsigned char ch, len;
|
|
if ((ret = bb_getUInt8(buff, &len)) == 0 &&
|
|
// Get authentication information.
|
|
(ret = bb_getUInt8(buff, &ch)) == 0)
|
|
{
|
|
if (ch != 0x80)
|
|
{
|
|
ret = DLMS_ERROR_CODE_INVALID_TAG;
|
|
}
|
|
else if ((ret = bb_getUInt8(buff, &len)) == 0)
|
|
{
|
|
if (settings->authentication == DLMS_AUTHENTICATION_LOW)
|
|
{
|
|
if ((ret = bb_clear(&settings->password)) != 0 ||
|
|
(ret = bb_set2(&settings->password, buff, buff->position, len)) != 0)
|
|
{
|
|
//Error code is returned at the end of the function.
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("updating password failed. "), ret);
|
|
#endif //DLMS_DEBUG
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (len < 8 || len > 64)
|
|
{
|
|
ret = DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
if ((ret = bb_clear(&settings->ctoSChallenge)) != 0 ||
|
|
(ret = bb_set2(&settings->ctoSChallenge, buff, buff->position, len)) != 0)
|
|
{
|
|
//Error code is returned at the end of the function.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int apdu_updateAuthentication(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* buff)
|
|
{
|
|
int ret;
|
|
unsigned char ch;
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch != 0x60)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_TAG;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch != 0x85)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_TAG;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch != 0x74)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_TAG;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch != 0x05)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_TAG;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch != 0x08)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_TAG;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ch != 0x02)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_TAG;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &ch)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
switch (ch)
|
|
{
|
|
case DLMS_AUTHENTICATION_NONE:
|
|
case DLMS_AUTHENTICATION_LOW:
|
|
case DLMS_AUTHENTICATION_HIGH:
|
|
#ifndef DLMS_IGNORE_HIGH_MD5
|
|
case DLMS_AUTHENTICATION_HIGH_MD5:
|
|
#endif //DLMS_IGNORE_HIGH_MD5
|
|
#ifndef DLMS_IGNORE_HIGH_SHA1
|
|
case DLMS_AUTHENTICATION_HIGH_SHA1:
|
|
#endif //DLMS_IGNORE_HIGH_SHA1
|
|
#ifndef DLMS_IGNORE_HIGH_SHA256
|
|
case DLMS_AUTHENTICATION_HIGH_SHA256:
|
|
#endif //DLMS_IGNORE_HIGH_SHA256
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
case DLMS_AUTHENTICATION_HIGH_GMAC:
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
break;
|
|
default:
|
|
return DLMS_ERROR_CODE_INVALID_TAG;
|
|
}
|
|
settings->authentication = (DLMS_AUTHENTICATION)ch;
|
|
return 0;
|
|
}
|
|
|
|
int apdu_getUserInformation(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* data,
|
|
unsigned char command)
|
|
{
|
|
int ret = 0;
|
|
// Tag for xDLMS-Initiate
|
|
if ((ret = bb_setUInt8(data, DLMS_COMMAND_INITIATE_RESPONSE)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
//NegotiatedQualityOfService
|
|
if (settings->qualityOfService == 0)
|
|
{
|
|
// Not used.
|
|
if ((ret = bb_setUInt8(data, 0x00)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((ret = bb_setUInt8(data, 0x01)) != 0 ||
|
|
(ret = bb_setUInt8(data, settings->qualityOfService)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
// DLMS Version Number
|
|
if ((ret = bb_setUInt8(data, 06)) != 0 ||
|
|
(ret = bb_setUInt8(data, 0x5F)) != 0 ||
|
|
(ret = bb_setUInt8(data, 0x1F)) != 0 ||
|
|
// length of the conformance block
|
|
(ret = bb_setUInt8(data, 0x04)) != 0 ||
|
|
// encoding the number of unused bits in the bit string
|
|
(ret = bb_setUInt8(data, 0x00)) != 0 ||
|
|
(ret = apdu_setConformanceToArray(settings->negotiatedConformance, data)) != 0 ||
|
|
(ret = bb_setUInt16(data, settings->maxPduSize)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// VAA Name VAA name (0x0007 for LN referencing and 0xFA00 for SN)
|
|
if ((ret = bb_setUInt16(data, settings->useLogicalNameReferencing ? 0x0007 : 0xFA00)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
if (isCiphered(&settings->cipher))
|
|
{
|
|
unsigned char cmd;
|
|
if (command == DLMS_COMMAND_GENERAL_DED_CIPHERING)
|
|
{
|
|
cmd = DLMS_COMMAND_GENERAL_DED_CIPHERING;
|
|
}
|
|
else if (command == DLMS_COMMAND_GENERAL_GLO_CIPHERING)
|
|
{
|
|
cmd = DLMS_COMMAND_GENERAL_GLO_CIPHERING;
|
|
}
|
|
else
|
|
{
|
|
cmd = DLMS_COMMAND_GLO_INITIATE_RESPONSE;
|
|
}
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
ret = cip_encrypt(&settings->cipher,
|
|
settings->cipher.security,
|
|
DLMS_COUNT_TYPE_PACKET,
|
|
settings->cipher.invocationCounter,
|
|
cmd,
|
|
settings->cipher.systemTitle.data,
|
|
&settings->cipher.blockCipherKey,
|
|
data);
|
|
#else
|
|
ret = cip_encrypt(&settings->cipher,
|
|
settings->cipher.security,
|
|
DLMS_COUNT_TYPE_PACKET,
|
|
settings->cipher.invocationCounter,
|
|
cmd,
|
|
settings->cipher.systemTitle,
|
|
settings->cipher.blockCipherKey,
|
|
data);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
return ret;
|
|
}
|
|
#ifndef DLMS_IGNORE_CLIENT
|
|
int apdu_generateAarq(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* data)
|
|
{
|
|
#if defined(GX_DLMS_BYTE_BUFFER_SIZE_32) || (!defined(GX_DLMS_MICROCONTROLLER) && (defined(_WIN32) || defined(_WIN64) || defined(__linux__)))
|
|
uint32_t offset;
|
|
#else
|
|
uint16_t offset;
|
|
#endif
|
|
int ret;
|
|
// Length is updated later.
|
|
offset = data->size + 1;
|
|
// AARQ APDU Tag
|
|
if ((ret = bb_setUInt8(data, BER_TYPE_APPLICATION | BER_TYPE_CONSTRUCTED)) == 0 &&
|
|
(ret = bb_setUInt8(data, 0)) == 0 &&
|
|
///////////////////////////////////////////
|
|
// Add Application context name.
|
|
(ret = apdu_generateApplicationContextName(settings, data)) == 0 &&
|
|
(ret = apdu_getAuthenticationString(settings, data)) == 0 &&
|
|
(ret = apdu_generateUserInformation(settings, data)) == 0)
|
|
{
|
|
return bb_setUInt8ByIndex(data, offset, (unsigned char)(data->size - offset - 1));
|
|
}
|
|
return ret;
|
|
}
|
|
#endif //DLMS_IGNORE_CLIENT
|
|
|
|
int apdu_handleResultComponent(unsigned char value)
|
|
{
|
|
int ret;
|
|
switch (value)
|
|
{
|
|
case DLMS_SOURCE_DIAGNOSTIC_NO_REASON_GIVEN:
|
|
ret = DLMS_ERROR_CODE_NO_REASON_GIVEN;
|
|
break;
|
|
case DLMS_SOURCE_DIAGNOSTIC_APPLICATION_CONTEXT_NAME_NOT_SUPPORTED:
|
|
ret = DLMS_ERROR_CODE_APPLICATION_CONTEXT_NAME_NOT_SUPPORTED;
|
|
break;
|
|
case DLMS_SOURCE_DIAGNOSTIC_AUTHENTICATION_MECHANISM_NAME_NOT_RECOGNISED:
|
|
ret = DLMS_ERROR_CODE_AUTHENTICATION_MECHANISM_NAME_NOT_RECOGNISED;
|
|
break;
|
|
case DLMS_SOURCE_DIAGNOSTIC_AUTHENTICATION_MECHANISM_NAME_REQUIRED:
|
|
ret = DLMS_ERROR_CODE_AUTHENTICATION_MECHANISM_NAME_REQUIRED;
|
|
break;
|
|
case DLMS_SOURCE_DIAGNOSTIC_AUTHENTICATION_FAILURE:
|
|
ret = DLMS_ERROR_CODE_AUTHENTICATION_FAILURE;
|
|
break;
|
|
default:
|
|
//OK.
|
|
ret = 0;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int apdu_parseProtocolVersion(dlmsSettings* settings,
|
|
gxByteBuffer* buff)
|
|
{
|
|
unsigned char cnt, unusedBits, value;
|
|
int ret;
|
|
if ((ret = bb_getUInt8(buff, &cnt)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &unusedBits)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (unusedBits > 8)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &value)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
//Protocol version must be 100001.
|
|
//This is not checked in client side because some meters are returning wrong value here.
|
|
if (settings->server && value != 0x84)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
settings->protocolVersion = value;
|
|
return 0;
|
|
}
|
|
|
|
int apdu_parsePDU(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* buff,
|
|
DLMS_ASSOCIATION_RESULT* result,
|
|
unsigned char* diagnostic,
|
|
unsigned char* command)
|
|
{
|
|
unsigned char ciphered = 0;
|
|
uint16_t size;
|
|
unsigned char len;
|
|
unsigned char tag;
|
|
int ret;
|
|
*result = DLMS_ASSOCIATION_RESULT_ACCEPTED;
|
|
*diagnostic = DLMS_SOURCE_DIAGNOSTIC_NONE;
|
|
#ifndef DLMS_IGNORE_SERVER
|
|
typedef enum
|
|
{
|
|
DLMS_AFU_MISSING_NONE = 0x0,
|
|
DLMS_AFU_MISSING_SENDER_ACSE_REQUIREMENTS = 0x1,
|
|
DLMS_AFU_MISSING_MECHANISM_NAME = 0x2,
|
|
DLMS_AFU_MISSING_CALLING_AUTHENTICATION_VALUE = 0x4
|
|
}
|
|
DLMS_AFU_MISSING;
|
|
DLMS_AFU_MISSING afu = DLMS_AFU_MISSING_NONE;
|
|
#endif //DLMS_IGNORE_SERVER
|
|
// Get AARE tag and length
|
|
if ((ret = apdu_validateAare(settings, buff)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if ((ret = hlp_getObjectCount2(buff, &size)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
if (size > buff->size - buff->position)
|
|
{
|
|
//Encoding failed. Not enough data->
|
|
return DLMS_ERROR_CODE_OUTOFMEMORY;
|
|
}
|
|
while (ret == 0 && buff->position < buff->size)
|
|
{
|
|
if ((ret = bb_getUInt8(buff, &tag)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
switch (tag)
|
|
{
|
|
//0xA1
|
|
case BER_TYPE_CONTEXT | BER_TYPE_CONSTRUCTED | PDU_TYPE_APPLICATION_CONTEXT_NAME:
|
|
{
|
|
if ((ret = apdu_parseApplicationContextName(settings, buff, &ciphered)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("apdu_parseApplicationContextName "), ret);
|
|
#endif //DLMS_DEBUG
|
|
* diagnostic = DLMS_SOURCE_DIAGNOSTIC_APPLICATION_CONTEXT_NAME_NOT_SUPPORTED;
|
|
*result = DLMS_ASSOCIATION_RESULT_PERMANENT_REJECTED;
|
|
return 0;
|
|
}
|
|
#ifndef DLMS_IGNORE_SERVER
|
|
if (ciphered)
|
|
{
|
|
afu = (DLMS_AFU_MISSING)(DLMS_AFU_MISSING_SENDER_ACSE_REQUIREMENTS | DLMS_AFU_MISSING_MECHANISM_NAME | DLMS_AFU_MISSING_CALLING_AUTHENTICATION_VALUE);
|
|
}
|
|
#endif //DLMS_IGNORE_SERVER
|
|
}
|
|
break;
|
|
// 0xA2
|
|
case BER_TYPE_CONTEXT | BER_TYPE_CONSTRUCTED | PDU_TYPE_CALLED_AP_TITLE:
|
|
// Get len.
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid AP title "), -1);
|
|
#endif //DLMS_DEBUG
|
|
break;
|
|
}
|
|
if (len != 3)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid AP title "), -1);
|
|
#endif //DLMS_DEBUG
|
|
ret = DLMS_ERROR_CODE_INVALID_TAG;
|
|
break;
|
|
}
|
|
// Choice for result.
|
|
if ((ret = bb_getUInt8(buff, &tag)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid AP title "), -1);
|
|
#endif //DLMS_DEBUG
|
|
break;
|
|
}
|
|
if (settings->server)
|
|
{
|
|
//Ignore if client sends CalledAPTitle.
|
|
if (tag != BER_TYPE_OCTET_STRING)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid AP title "), -1);
|
|
#endif //DLMS_DEBUG
|
|
ret = DLMS_ERROR_CODE_INVALID_TAG;
|
|
break;
|
|
}
|
|
// Get len.
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid AP title "), -1);
|
|
#endif //DLMS_DEBUG
|
|
break;
|
|
}
|
|
buff->position += len;
|
|
}
|
|
else
|
|
{
|
|
if (tag != BER_TYPE_INTEGER)
|
|
{
|
|
ret = DLMS_ERROR_CODE_INVALID_TAG;
|
|
break;
|
|
}
|
|
// Get len.
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
if (len != 1)
|
|
{
|
|
ret = DLMS_ERROR_CODE_INVALID_TAG;
|
|
break;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &tag)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
*result = (DLMS_ASSOCIATION_RESULT)tag;
|
|
}
|
|
break;
|
|
// 0xA3 SourceDiagnostic
|
|
case BER_TYPE_CONTEXT | BER_TYPE_CONSTRUCTED | PDU_TYPE_CALLED_AE_QUALIFIER:
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0 ||
|
|
// ACSE service user tag.
|
|
(ret = bb_getUInt8(buff, &tag)) != 0 ||
|
|
(ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid AE qualifier. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
break;
|
|
}
|
|
if (settings->server)
|
|
{
|
|
//Ignore if client sends CalledAEQualifier.
|
|
if (tag != BER_TYPE_OCTET_STRING)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid AE qualifier. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
ret = DLMS_ERROR_CODE_INVALID_TAG;
|
|
break;
|
|
}
|
|
buff->position += len;
|
|
}
|
|
else
|
|
{
|
|
// Result source diagnostic component.
|
|
if ((ret = bb_getUInt8(buff, &tag)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
if (tag != BER_TYPE_INTEGER)
|
|
{
|
|
ret = DLMS_ERROR_CODE_INVALID_TAG;
|
|
break;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
if (len != 1)
|
|
{
|
|
ret = DLMS_ERROR_CODE_INVALID_TAG;
|
|
break;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &tag)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
*diagnostic = (DLMS_SOURCE_DIAGNOSTIC)tag;
|
|
}
|
|
break;
|
|
// 0xA4 Result
|
|
case BER_TYPE_CONTEXT | BER_TYPE_CONSTRUCTED | PDU_TYPE_CALLED_AP_INVOCATION_ID:
|
|
// Get len.
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid AP invocationID. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
break;
|
|
}
|
|
if (settings->server)
|
|
{
|
|
if (len != 3)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid AP invocationID. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
ret = DLMS_ERROR_CODE_INVALID_TAG;
|
|
break;
|
|
}
|
|
// ACSE service user tag.
|
|
if ((ret = bb_getUInt8(buff, &tag)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid AP invocationID. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
break;
|
|
}
|
|
if (tag != BER_TYPE_INTEGER)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid AP invocationID. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
ret = DLMS_ERROR_CODE_INVALID_TAG;
|
|
break;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid AP invocationID. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
break;
|
|
}
|
|
//Ignore if client sends CalledAEQualifier.
|
|
buff->position += len;
|
|
}
|
|
else
|
|
{
|
|
if (len != 0xA)
|
|
{
|
|
ret = DLMS_ERROR_CODE_INVALID_TAG;
|
|
break;
|
|
}
|
|
// Choice for result (Universal, Octet string type)
|
|
if ((ret = bb_getUInt8(buff, &tag)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
if (tag != BER_TYPE_OCTET_STRING)
|
|
{
|
|
ret = DLMS_ERROR_CODE_INVALID_TAG;
|
|
break;
|
|
}
|
|
// responding-AP-title-field
|
|
// Get len.
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
if ((ret = bb_get(buff, settings->sourceSystemTitle, len)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
//If system title is invalid.
|
|
if (len != 8)
|
|
{
|
|
memset(settings->sourceSystemTitle, 0, 8);
|
|
}
|
|
}
|
|
break;
|
|
// 0xA6 Client system title.
|
|
case BER_TYPE_CONTEXT | BER_TYPE_CONSTRUCTED | PDU_TYPE_CALLING_AP_TITLE:
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0 ||
|
|
(ret = bb_getUInt8(buff, &tag)) != 0 ||
|
|
(ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid client system title. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
break;
|
|
}
|
|
if (ciphered && len != 8)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid client system title. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
* diagnostic = DLMS_SOURCE_DIAGNOSTIC_CALLING_AP_TITLE_NOT_RECOGNIZED;
|
|
*result = DLMS_ASSOCIATION_RESULT_PERMANENT_REJECTED;
|
|
return 0;
|
|
}
|
|
if ((ret = bb_get(buff, settings->sourceSystemTitle, len)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid client system title. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
break;
|
|
}
|
|
break;
|
|
// 0xAA Server system title.
|
|
case BER_TYPE_CONTEXT | BER_TYPE_CONSTRUCTED | PDU_TYPE_SENDER_ACSE_REQUIREMENTS:
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0 ||
|
|
(ret = bb_getUInt8(buff, &tag)) != 0 ||
|
|
(ret = bb_getUInt8(buff, &len)) != 0 ||
|
|
(ret = bb_clear(&settings->stoCChallenge)) != 0 ||
|
|
(ret = bb_set2(&settings->stoCChallenge, buff, buff->position, len)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
break;
|
|
//Client AE Invocation id.
|
|
case BER_TYPE_CONTEXT | BER_TYPE_CONSTRUCTED | PDU_TYPE_CALLING_AE_INVOCATION_ID:
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0 ||
|
|
(ret = bb_getUInt8(buff, &tag)) != 0 ||
|
|
(ret = bb_getUInt8(buff, &len)) != 0 ||
|
|
(ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid AE Invocation ID. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
break;
|
|
}
|
|
break;
|
|
//Client CalledAeInvocationId.
|
|
case BER_TYPE_CONTEXT | BER_TYPE_CONSTRUCTED | PDU_TYPE_CALLED_AE_INVOCATION_ID://0xA5
|
|
if (settings->server)
|
|
{
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid AE Invocation ID. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
break;
|
|
}
|
|
//Invalid length.
|
|
if (len != 3)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid AE Invocation ID. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
ret = DLMS_ERROR_CODE_INVALID_TAG;
|
|
break;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid AE Invocation ID. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
break;
|
|
}
|
|
//Invalid length.
|
|
if (len != 2)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid AE Invocation ID. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
ret = DLMS_ERROR_CODE_INVALID_TAG;
|
|
break;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid AE Invocation ID. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
break;
|
|
}
|
|
//Invalid tag length.
|
|
if (len != 1)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid AE Invocation ID. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
ret = DLMS_ERROR_CODE_INVALID_TAG;
|
|
break;
|
|
}
|
|
//Get value.
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid AE Invocation ID. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0 ||
|
|
(ret = bb_getUInt8(buff, &tag)) != 0 ||
|
|
(ret = bb_getUInt8(buff, &len)) != 0 ||
|
|
(ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
if (ciphered)
|
|
{
|
|
settings->userId = len;
|
|
}
|
|
}
|
|
break;
|
|
//Server RespondingAEInvocationId.
|
|
case BER_TYPE_CONTEXT | BER_TYPE_CONSTRUCTED | PDU_TYPE_CALLING_AE_QUALIFIER://0xA7
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0 ||
|
|
(ret = bb_getUInt8(buff, &tag)) != 0 ||
|
|
(ret = bb_getUInt8(buff, &len)) != 0 ||
|
|
(ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid Responding AE Invocation ID. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
break;
|
|
}
|
|
if (ciphered && len == 0)
|
|
{
|
|
settings->userId = len;
|
|
}
|
|
break;
|
|
case BER_TYPE_CONTEXT | BER_TYPE_CONSTRUCTED | PDU_TYPE_CALLING_AP_INVOCATION_ID://0xA8
|
|
if ((ret = bb_getUInt8(buff, &tag)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid Calling AP Invocation ID. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
break;
|
|
}
|
|
if (tag != 3)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid Calling AP Invocation ID. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
ret = DLMS_ERROR_CODE_INVALID_TAG;
|
|
break;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid Calling AP Invocation ID. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
break;
|
|
}
|
|
if (len != 2)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid Calling AP Invocation ID. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
ret = DLMS_ERROR_CODE_INVALID_TAG;
|
|
break;
|
|
}
|
|
//Invalid tag length.
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid Calling AP Invocation ID. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
return ret;
|
|
}
|
|
if (len != 1)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid Calling AP Invocation ID. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
ret = DLMS_ERROR_CODE_INVALID_TAG;
|
|
break;
|
|
}
|
|
//Get value.
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid Calling AP Invocation ID. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
break;
|
|
}
|
|
break;
|
|
// 0x8A or 0x88
|
|
case (uint16_t)BER_TYPE_CONTEXT | (unsigned char)PDU_TYPE_SENDER_ACSE_REQUIREMENTS:
|
|
case (uint16_t)BER_TYPE_CONTEXT | (unsigned char)PDU_TYPE_CALLING_AP_INVOCATION_ID:
|
|
// Get sender ACSE-requirements field component.
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid sender ACSE-requirements field. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
break;
|
|
}
|
|
if (len != 2)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid sender ACSE-requirements field. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
ret = DLMS_ERROR_CODE_INVALID_TAG;
|
|
break;
|
|
}
|
|
if ((ret = bb_getUInt8(buff, &tag)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid sender ACSE-requirements field. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
break;
|
|
}
|
|
if (tag != BER_TYPE_OBJECT_DESCRIPTOR)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid sender ACSE-requirements field. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
ret = DLMS_ERROR_CODE_INVALID_TAG;
|
|
break;
|
|
}
|
|
//Get only value because client app is sending system title with LOW authentication.
|
|
if ((ret = bb_getUInt8(buff, &tag)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid sender ACSE-requirements field. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
break;
|
|
}
|
|
#ifndef DLMS_IGNORE_SERVER
|
|
if (ciphered && tag == 0x80)
|
|
{
|
|
afu &= ~DLMS_AFU_MISSING_SENDER_ACSE_REQUIREMENTS;
|
|
}
|
|
#endif //DLMS_IGNORE_SERVER
|
|
break;
|
|
// 0x8B or 0x89
|
|
case (uint16_t)BER_TYPE_CONTEXT | (unsigned char)PDU_TYPE_MECHANISM_NAME:
|
|
case (uint16_t)BER_TYPE_CONTEXT | (unsigned char)PDU_TYPE_CALLING_AE_INVOCATION_ID:
|
|
if ((ret = apdu_updateAuthentication(settings, buff)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid mechanism name. "), ret);
|
|
#endif //DLMS_DEBUG
|
|
break;
|
|
}
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
unsigned char invalidSystemTitle;
|
|
invalidSystemTitle = memcmp(settings->sourceSystemTitle, EMPTY_SYSTEM_TITLE, 8) == 0;
|
|
if (settings->server && settings->authentication == DLMS_AUTHENTICATION_HIGH_GMAC && invalidSystemTitle)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid mechanism name. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
* diagnostic = DLMS_SOURCE_DIAGNOSTIC_CALLING_AP_TITLE_NOT_RECOGNIZED;
|
|
*result = DLMS_ASSOCIATION_RESULT_PERMANENT_REJECTED;
|
|
return 0;
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
#ifndef DLMS_IGNORE_SERVER
|
|
if (ciphered)
|
|
{
|
|
afu &= ~DLMS_AFU_MISSING_MECHANISM_NAME;
|
|
}
|
|
#endif //DLMS_IGNORE_SERVER
|
|
break;
|
|
// 0xAC
|
|
case BER_TYPE_CONTEXT | BER_TYPE_CONSTRUCTED | PDU_TYPE_CALLING_AUTHENTICATION_VALUE:
|
|
if ((ret = apdu_updatePassword(settings, buff)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid password. "), ret);
|
|
#endif //DLMS_DEBUG
|
|
break;
|
|
}
|
|
#ifndef DLMS_IGNORE_SERVER
|
|
if (ciphered)
|
|
{
|
|
afu &= ~DLMS_AFU_MISSING_CALLING_AUTHENTICATION_VALUE;
|
|
}
|
|
#endif //DLMS_IGNORE_SERVER
|
|
break;
|
|
// 0xBE
|
|
case BER_TYPE_CONTEXT | BER_TYPE_CONSTRUCTED | PDU_TYPE_USER_INFORMATION:
|
|
//Check result component. Some meters are returning invalid user-information if connection failed.
|
|
if (*result != DLMS_ASSOCIATION_RESULT_ACCEPTED
|
|
&& *diagnostic != DLMS_SOURCE_DIAGNOSTIC_NONE)
|
|
{
|
|
if ((ret = apdu_handleResultComponent(*diagnostic)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Invalid result component. "), ret);
|
|
#endif //DLMS_DEBUG
|
|
}
|
|
return ret;
|
|
}
|
|
if ((ret = apdu_parseUserInformation(settings, buff, ciphered, command)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("parseUserInformation. "), ret);
|
|
#endif //DLMS_DEBUG
|
|
if (ret == DLMS_ERROR_CODE_INVOCATION_COUNTER_TOO_SMALL ||
|
|
ret == DLMS_ERROR_CODE_INVALID_DECIPHERING_ERROR ||
|
|
ret == DLMS_ERROR_CODE_INVALID_SECURITY_SUITE)
|
|
{
|
|
return ret;
|
|
}
|
|
if (ciphered)
|
|
{
|
|
ret = DLMS_ERROR_CODE_INVALID_DECIPHERING_ERROR;
|
|
}
|
|
else
|
|
{
|
|
//Return confirmed service error.
|
|
ret = DLMS_ERROR_CODE_INVALID_TAG;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case BER_TYPE_CONTEXT: //0x80
|
|
if ((ret = apdu_parseProtocolVersion(settings, buff)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("parseProtocolVersion. "), ret);
|
|
#endif //DLMS_DEBUG
|
|
* diagnostic = 0x80 | DLMS_ACSE_SERVICE_PROVIDER_NO_COMMON_ACSE_VERSION;
|
|
*result = DLMS_ASSOCIATION_RESULT_PERMANENT_REJECTED;
|
|
return 0;
|
|
}
|
|
break;
|
|
default:
|
|
// Unknown tags.
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Unknown tag. "), -1);
|
|
#endif //DLMS_DEBUG
|
|
if (buff->position < buff->size)
|
|
{
|
|
if ((ret = bb_getUInt8(buff, &len)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
buff->position = (buff->position + len);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (ret == 0)
|
|
{
|
|
#ifndef DLMS_IGNORE_SERVER
|
|
if (settings->server && afu != 0 &&
|
|
*result == DLMS_ASSOCIATION_RESULT_ACCEPTED &&
|
|
!(
|
|
afu == DLMS_AFU_MISSING_CALLING_AUTHENTICATION_VALUE &&
|
|
settings->authentication == DLMS_AUTHENTICATION_NONE))
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
switch (afu)
|
|
{
|
|
case DLMS_AFU_MISSING_SENDER_ACSE_REQUIREMENTS:
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Sender ACSE requirements is missing."), -1);
|
|
break;
|
|
case DLMS_AFU_MISSING_MECHANISM_NAME:
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Mechanism name is missing."), -1);
|
|
break;
|
|
case DLMS_AFU_MISSING_CALLING_AUTHENTICATION_VALUE:
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("Calling authentication value is missing."), -1);
|
|
break;
|
|
case DLMS_AFU_MISSING_NONE:
|
|
break;
|
|
}
|
|
#endif //DLMS_DEBUG
|
|
* result = DLMS_ASSOCIATION_RESULT_PERMANENT_REJECTED;
|
|
*diagnostic = DLMS_SOURCE_DIAGNOSTIC_AUTHENTICATION_FAILURE;
|
|
return 0;
|
|
}
|
|
#endif //DLMS_IGNORE_SERVER
|
|
//All meters don't send user-information if connection is failed.
|
|
//For this reason result component is check again.
|
|
if ((ret = apdu_handleResultComponent(*diagnostic)) != 0)
|
|
{
|
|
#ifdef DLMS_DEBUG
|
|
svr_notifyTrace(GET_STR_FROM_EEPROM("handleResultComponent."), ret);
|
|
#endif //DLMS_DEBUG
|
|
}
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
//Check that user is not trying to connect without ciphered connection.
|
|
if (ret == 0 && settings->expectedSecurityPolicy != 0xFF)
|
|
{
|
|
if (settings->cipher.security != settings->expectedSecurityPolicy << 4)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_DECIPHERING_ERROR;
|
|
}
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_SERVER
|
|
int apdu_generateAARE(
|
|
dlmsSettings* settings,
|
|
gxByteBuffer* data,
|
|
DLMS_ASSOCIATION_RESULT result,
|
|
unsigned char diagnostic,
|
|
gxByteBuffer* errorData,
|
|
gxByteBuffer* encryptedData,
|
|
unsigned char command)
|
|
{
|
|
int ret;
|
|
#if defined(GX_DLMS_BYTE_BUFFER_SIZE_32) || (!defined(GX_DLMS_MICROCONTROLLER) && (defined(_WIN32) || defined(_WIN64) || defined(__linux__)))
|
|
uint32_t offset = data->size;
|
|
#else
|
|
uint16_t offset = data->size;
|
|
#endif
|
|
// Set AARE tag and length 0x61
|
|
bb_setUInt8(data, BER_TYPE_APPLICATION | BER_TYPE_CONSTRUCTED | PDU_TYPE_APPLICATION_CONTEXT_NAME);
|
|
// Length is updated later.
|
|
bb_setUInt8(data, 0);
|
|
if ((ret = apdu_generateApplicationContextName(settings, data)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
// Result 0xA2
|
|
bb_setUInt8(data, BER_TYPE_CONTEXT | BER_TYPE_CONSTRUCTED | BER_TYPE_INTEGER);
|
|
bb_setUInt8(data, 3); // len
|
|
// Tag
|
|
bb_setUInt8(data, BER_TYPE_INTEGER);
|
|
// Choice for result (INTEGER, universal)
|
|
bb_setUInt8(data, 1); // Len
|
|
// ResultValue
|
|
bb_setUInt8(data, result);
|
|
// SourceDiagnostic
|
|
bb_setUInt8(data, 0xA3);
|
|
// len
|
|
bb_setUInt8(data, 5);
|
|
// Tag
|
|
if ((diagnostic & 0x80) == 0)
|
|
{
|
|
bb_setUInt8(data, 0xA1);
|
|
}
|
|
else
|
|
{
|
|
bb_setUInt8(data, 0xA2);
|
|
diagnostic &= ~0x80;
|
|
}
|
|
bb_setUInt8(data, 3); // len
|
|
bb_setUInt8(data, 2); // Tag
|
|
// Choice for result (INTEGER, universal)
|
|
bb_setUInt8(data, 1); // Len
|
|
// diagnostic
|
|
bb_setUInt8(data, diagnostic);
|
|
// SystemTitle
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
if (settings->authentication == DLMS_AUTHENTICATION_HIGH_GMAC
|
|
|| isCiphered(&settings->cipher))
|
|
{
|
|
bb_setUInt8(data, BER_TYPE_CONTEXT | BER_TYPE_CONSTRUCTED | PDU_TYPE_CALLED_AP_INVOCATION_ID);
|
|
bb_setUInt8(data, 2 + 8);
|
|
bb_setUInt8(data, BER_TYPE_OCTET_STRING);
|
|
bb_setUInt8(data, 8);
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
bb_set(data, settings->cipher.systemTitle.data, 8);
|
|
#else
|
|
bb_set(data, settings->cipher.systemTitle, 8);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
}
|
|
#endif //LMS_IGNORE_HIGH_GMAC
|
|
|
|
//Add CalledAEInvocationId.
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
if (settings->userId != -1 && settings->cipher.security != DLMS_SECURITY_NONE)
|
|
#else
|
|
if (settings->userId != -1)
|
|
#endif
|
|
{
|
|
bb_setUInt8(data, BER_TYPE_CONTEXT | BER_TYPE_CONSTRUCTED | PDU_TYPE_CALLING_AE_QUALIFIER);
|
|
//LEN
|
|
bb_setUInt8(data, 3);
|
|
bb_setUInt8(data, BER_TYPE_INTEGER);
|
|
//LEN
|
|
bb_setUInt8(data, 1);
|
|
bb_setUInt8(data, (unsigned char)settings->userId);
|
|
}
|
|
if (settings->authentication > DLMS_AUTHENTICATION_LOW)
|
|
{
|
|
if (settings->stoCChallenge.size == 0)
|
|
{
|
|
return DLMS_ERROR_CODE_INVALID_PARAMETER;
|
|
}
|
|
// Add server ACSE-requirenents field component.
|
|
if ((ret = bb_setUInt8(data, 0x88)) != 0 ||
|
|
(ret = bb_setUInt8(data, 0x02)) != 0 || // Len.
|
|
(ret = bb_setUInt16(data, 0x0780)) != 0 ||
|
|
// Add tag.
|
|
(ret = bb_setUInt8(data, 0x89)) != 0 ||
|
|
(ret = bb_setUInt8(data, 0x07)) != 0 || // Len
|
|
(ret = bb_setUInt8(data, 0x60)) != 0 ||
|
|
(ret = bb_setUInt8(data, 0x85)) != 0 ||
|
|
(ret = bb_setUInt8(data, 0x74)) != 0 ||
|
|
(ret = bb_setUInt8(data, 0x05)) != 0 ||
|
|
(ret = bb_setUInt8(data, 0x08)) != 0 ||
|
|
(ret = bb_setUInt8(data, 0x02)) != 0 ||
|
|
(ret = bb_setUInt8(data, settings->authentication)) != 0 ||
|
|
// Add tag.
|
|
(ret = bb_setUInt8(data, 0xAA)) != 0 ||
|
|
(ret = bb_setUInt8(data, (unsigned char)(2 + settings->stoCChallenge.size))) != 0 || // Len
|
|
(ret = bb_setUInt8(data, BER_TYPE_CONTEXT)) != 0 ||
|
|
(ret = bb_setUInt8(data, (unsigned char)settings->stoCChallenge.size)) != 0 ||
|
|
(ret = bb_set(data, settings->stoCChallenge.data, settings->stoCChallenge.size)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
if (result == DLMS_ASSOCIATION_RESULT_ACCEPTED || !isCiphered(&settings->cipher))
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
{
|
|
gxByteBuffer tmp;
|
|
unsigned char buff[200];
|
|
// Add User Information. Tag 0xBE
|
|
bb_setUInt8(data, BER_TYPE_CONTEXT | BER_TYPE_CONSTRUCTED | PDU_TYPE_USER_INFORMATION);
|
|
bb_attach(&tmp, buff, 0, sizeof(buff));
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
if (encryptedData != NULL && encryptedData->size != 0)
|
|
{
|
|
bb_capacity(&tmp, 2 + encryptedData->size);
|
|
bb_setUInt8(&tmp, DLMS_COMMAND_GLO_INITIATE_RESPONSE);
|
|
hlp_setObjectCount(encryptedData->size, &tmp);
|
|
bb_set2(&tmp, encryptedData, 0, encryptedData->size);
|
|
}
|
|
else
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
{
|
|
if (errorData != NULL && errorData->size != 0)
|
|
{
|
|
bb_set2(&tmp, errorData, 0, errorData->size);
|
|
}
|
|
else
|
|
{
|
|
if ((ret = apdu_getUserInformation(settings, &tmp, command)) != 0)
|
|
{
|
|
bb_clear(&tmp);
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((ret = bb_setUInt8(data, (unsigned char)(2 + tmp.size))) != 0 ||
|
|
// Coding the choice for user-information (Octet STRING, universal)
|
|
(ret = bb_setUInt8(data, BER_TYPE_OCTET_STRING)) != 0 ||
|
|
// Length
|
|
(ret = bb_setUInt8(data, (unsigned char)tmp.size)) != 0 ||
|
|
(ret = bb_set2(data, &tmp, 0, tmp.size)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
return bb_setUInt8ByIndex(data, (offset + 1), (unsigned char)(data->size - offset - 2));
|
|
}
|
|
#endif //DLMS_IGNORE_SERVER
|