Added security for BACnet stack, based on the BACnet 2012 standard, clause 24.

Implemented the majority of functionalities presented in the standard, but there are several features that this patch currently lacks:
- Set-Master-Key message has a specific order of key adding and decoding which is not covered
- There is no general secure-apdu-handler function
- Checks for the type of keys used for signing/encryption of specific messages is not implemented
- The status of encrypted flag during the calculation of the signature is ambiguous
There is a Linux implementation using the OpenSSL library, with function prototypes broad enough to allow for different implementations.
Thank you, Nikola Jelić!
This commit is contained in:
skarg
2016-04-20 17:00:51 +00:00
parent 2616efda30
commit 3362eecdff
6 changed files with 1554 additions and 37 deletions
+92 -12
View File
@@ -1328,7 +1328,8 @@ typedef enum {
BVLC_DISTRIBUTE_BROADCAST_TO_NETWORK = 9,
BVLC_ORIGINAL_UNICAST_NPDU = 10,
BVLC_ORIGINAL_BROADCAST_NPDU = 11,
MAX_BVLC_FUNCTION = 12
BVLC_SECURE_BVLL = 12,
MAX_BVLC_FUNCTION = 13
} BACNET_BVLC_FUNCTION;
typedef enum {
@@ -1419,7 +1420,17 @@ typedef enum {
NETWORK_MESSAGE_INIT_RT_TABLE_ACK = 7,
NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK = 8,
NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK = 9,
/* X'0A' to X'7F': Reserved for use by ASHRAE, */
NETWORK_MESSAGE_CHALLENGE_REQUEST = 10,
NETWORK_MESSAGE_SECURITY_PAYLOAD = 11,
NETWORK_MESSAGE_SECURITY_RESPONSE = 12,
NETWORK_MESSAGE_REQUEST_KEY_UPDATE = 13,
NETWORK_MESSAGE_UPDATE_KEY_SET = 14,
NETWORK_MESSAGE_UPDATE_DISTRIBUTION_KEY = 15,
NETWORK_MESSAGE_REQUEST_MASTER_KEY = 16,
NETWORK_MESSAGE_SET_MASTER_KEY = 17,
NETWORK_MESSAGE_WHAT_IS_NETWORK_NUMBER = 18,
NETWORK_MESSAGE_NETWORK_NUMBER_IS = 19,
/* X'14' to X'7F': Reserved for use by ASHRAE, */
/* X'80' to X'FF': Available for vendor proprietary messages */
NETWORK_MESSAGE_INVALID = 0x100
} BACNET_NETWORK_MESSAGE_TYPE;
@@ -1441,10 +1452,12 @@ typedef enum {
ABORT_REASON_INVALID_APDU_IN_THIS_STATE = 2,
ABORT_REASON_PREEMPTED_BY_HIGHER_PRIORITY_TASK = 3,
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED = 4,
ABORT_REASON_SECURITY_ERROR = 5,
ABORT_REASON_INSUFFICIENT_SECURITY = 6,
/* Enumerated values 0-63 are reserved for definition by ASHRAE. */
/* Enumerated values 64-65535 may be used by others subject to */
/* the procedures and constraints described in Clause 23. */
MAX_BACNET_ABORT_REASON = 5,
MAX_BACNET_ABORT_REASON = 7,
/* do the MAX here instead of outside of enum so that
compilers will allocate adequate sized datatype for enum */
ABORT_REASON_PROPRIETARY_FIRST = 64,
@@ -1714,7 +1727,7 @@ typedef enum BACnetLightingOperation {
BACNET_LIGHTS_WARN_OFF = 8,
BACNET_LIGHTS_WARN_RELINQUISH = 9,
BACNET_LIGHTS_STOP = 10,
MAX_BACNET_LIGHTING_OPERATION = 11,
MAX_BACNET_LIGHTING_OPERATION = 11,
/* Enumerated values 0-255 are reserved for definition by ASHRAE.
Enumerated values 256-65535 may be used by others subject to
the procedures and constraints described in Clause 23 */
@@ -1728,14 +1741,14 @@ typedef enum BACnetLightingInProgress {
BACNET_LIGHTING_RAMP_ACTIVE = 2,
BACNET_LIGHTING_NOT_CONTROLLED = 3,
BACNET_LIGHTING_OTHER = 4,
MAX_BACNET_LIGHTING_IN_PROGRESS = 5
MAX_BACNET_LIGHTING_IN_PROGRESS = 5
} BACNET_LIGHTING_IN_PROGRESS;
typedef enum BACnetLightingTransition {
BACNET_LIGHTING_TRANSITION_IDLE = 0,
BACNET_LIGHTING_TRANSITION_FADE = 1,
BACNET_LIGHTING_TRANSITION_RAMP = 2,
MAX_BACNET_LIGHTING_TRANSITION = 3,
MAX_BACNET_LIGHTING_TRANSITION = 3,
/* Enumerated values 0-63 are reserved for definition by ASHRAE.
Enumerated values 64-255 may be used by others subject to
the procedures and constraints described in Clause 23. */
@@ -1777,6 +1790,8 @@ typedef enum {
NETWORK_REJECT_ROUTER_BUSY = 2,
NETWORK_REJECT_UNKNOWN_MESSAGE_TYPE = 3,
NETWORK_REJECT_MESSAGE_TOO_LONG = 4,
NETWORK_REJECT_BACNET_SECURITY = 5,
NETWORK_REJECT_BAD_ADDRESS = 6,
/* Reasons this value or above we don't know about */
NETWORK_REJECT_REASON_INVALID
} BACNET_NETWORK_REJECT_REASONS;
@@ -1799,9 +1814,9 @@ typedef enum {
PORT_TYPE_ZIGBEE = 6,
PORT_TYPE_VIRTUAL = 7,
PORT_TYPE_NON_BACNET = 8
/* Enumerated values 0-63 are reserved for definition by ASHRAE.
Enumerated values 64-255 may be used by others subject to the
procedures and constraints described in Clause 23.*/
/* Enumerated values 0-63 are reserved for definition by ASHRAE.
Enumerated values 64-255 may be used by others subject to the
procedures and constraints described in Clause 23. */
} BACNET_PORT_TYPE;
/* BACnetNetworkNumberQuality ::= ENUMERATED */
@@ -1822,9 +1837,74 @@ typedef enum {
PORT_COMMAND_RESTART_AUTONEGOTIATION = 5,
PORT_COMMAND_DISCONNECT = 6,
PORT_COMMAND_RESTART_PORT = 7
/* Enumerated values 0-127 are reserved for definition by ASHRAE.
Enumerated values 128-255 may be used by others subject to the
procedures and constraints described in Clause 23. */
/* Enumerated values 0-127 are reserved for definition by ASHRAE.
Enumerated values 128-255 may be used by others subject to the
procedures and constraints described in Clause 23. */
} BACNET_PORT_COMMAND;
typedef enum {
BACNET_SECURITY_LEVEL_INCAPABLE = 0,
BACNET_SECURITY_LEVEL_PLAIN = 1,
BACNET_SECURITY_LEVEL_SIGNED = 2,
BACNET_SECURITY_LEVEL_ENCRYPTED = 3,
BACNET_SECURITY_LEVEL_SIGNED_END_TO_END = 4,
BACNET_SECURITY_LEVEL_ENCRYPTED_END_TO_END = 5
} BACNET_SECURITY_LEVEL;
typedef enum {
BACNET_SECURITY_POLICY_PLAIN_NOT_TRUSTED = 0,
BACNET_SECURITY_POLICY_PLAIN_TRUSTED = 1,
BACNET_SECURITY_POLICY_SIGNED_TRUSTED = 2,
BACNET_SECURITY_POLICY_ENCRYPTED_TRUSTED = 3
} BACNET_SECURITY_POLICY;
typedef enum {
KIA_AES_MD5 = 0,
KIA_AES_SHA256 = 1,
/* 2-255 reserved */
KIA_MAX_KEY_IDENTIFIER_ALGORITHM = 255
} BACNET_KEY_IDENTIFIER_ALGORITHM;
typedef enum {
KIKN_NOT_USED = 0,
KIKN_DEVICE_MASTER = 1,
KIKN_DISTRIBUTION = 2,
KIKN_INSTALLATION = 3,
KIKN_GENERAL_NETWORK_ACCESS = 4,
KIKN_USER_AUTHENTICATED = 5,
KIKN_MIN_APPLICATION_SPECIFIC = 6,
KIKN_MAX_APPLICATION_SPECIFIC = 127,
/* 128-255 reserved */
KIKN_MAX_KEY_IDENTIFIER_KEY_NUMBER = 255
} BACNET_KEY_IDENTIFIER_KEY_NUMBER;
typedef enum {
SEC_RESP_SUCCESS = 0,
SEC_RESP_ACCESS_DENIED = 1,
SEC_RESP_BAD_DESTINATION_ADDRESS = 2,
SEC_RESP_BAD_DESTINATION_DEVICE_ID = 3,
SEC_RESP_BAD_SIGNATURE = 4,
SEC_RESP_BAD_SOURCE_ADDRESS = 5,
SEC_RESP_BAD_TIMESTAMP = 6,
SEC_RESP_CANNOT_USE_KEY = 7,
SEC_RESP_CANNOT_VERIFY_MESSAGE_ID = 8,
SEC_RESP_CORRECT_KEY_REVISION = 9,
SEC_RESP_DESTINATION_DEVICE_ID_REQUIRED = 10,
SEC_RESP_DUPLICATE_MESSAGE = 11,
SEC_RESP_ENCRYPTION_NOT_CONFIGURED = 12,
SEC_RESP_ENCRYPTION_REQUIRED = 13,
SEC_RESP_INCORRECT_KEY = 14,
SEC_RESP_INVALID_KEY_DATA = 15,
SEC_RESP_KEY_UPDATE_IN_PROGRESS = 16,
SEC_RESP_MALFORMED_MESSAGE = 17,
SEC_RESP_NOT_KEY_SERVER = 18,
SEC_RESP_SECURITY_NOT_CONFIGURED = 19,
SEC_RESP_SOURCE_SECURITY_REQUIRED = 20,
SEC_RESP_TOO_MANY_KEYS = 21,
SEC_RESP_UNKNOWN_AUTHENTICATION_TYPE = 22,
SEC_RESP_UNKNOWN_KEY = 23,
SEC_RESP_UNKNOWN_KEY_REVISION = 24,
SEC_RESP_UNKNOWN_SOURCE_MESSAGE = 25
} BACNET_SECURITY_RESPONSE_CODE;
#endif /* end of BACENUM_H */
+290
View File
@@ -0,0 +1,290 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#ifndef BACNET_SECURITY_H
#define BACNET_SECURITY_H
#define MAX_AUTH_DATA_LEN 16
#define MD5_KEY_SIZE 16
#define AES_KEY_SIZE 16
#define SHA256_KEY_SIZE 32
#define MAX_KEY_LEN 48
#define MAX_UPDATE_KEY_COUNT 32
#define MAX_INCORRECT_KEYS 255
#define MAX_SUPPORTED_ALGORITHMS 255
#define MAX_PAD_LEN 16
#define SIGNATURE_LEN 16
#include <stdbool.h>
#include <stdint.h>
#include "bacdef.h"
#include "bacenum.h"
typedef struct BACnet_Security_Wrapper {
bool payload_net_or_bvll_flag; /* true if NPDU or BVLL */
bool encrypted_flag;
bool authentication_flag; /* always false for responses */
bool do_not_unwrap_flag; /* always true if do-not-encrypt is true */
bool do_not_decrypt_flag; /* when encrypted flag is false, it also false */
bool non_trusted_source_flag;
bool secured_by_router_flag;
uint8_t key_revision; /* 0 for Device-Master key */
uint16_t key_identifier;
uint32_t source_device_instance;
uint32_t message_id; /* monotonically increased value */
uint32_t timestamp; /* seconds from UTC 1970-1-1 00:00:00 */
uint32_t destination_device_instance;
uint16_t dnet;
uint8_t dlen;
uint8_t dadr[MAX_MAC_LEN];
uint16_t snet;
uint8_t slen;
uint8_t sadr[MAX_MAC_LEN];
uint8_t authentication_mechanism; /* present when User-Authenticated or
* Application-Specific keys are used with: */
/* APDU: Confrmed-Request, Unconfirmed-Request */
/* NPDU: Initialize-Routing-Table, Establish-Connection-To-Network,
* Disconnect-Connection-To-Network */
/* BVLL: Write-Broadcast-Distribution-Table, Read-Broadcast-Distribution-Table,
* Register-Foreign-Device, Read-Foreign-Device-Table,
* Delete-Foreign-Device-Table-Entry */
/* 0 is only legitimate value for now. 200-255 are vendor-specific */
uint16_t user_id; /* 0 for unknown */
uint8_t user_role; /* 0 and 1 are "system users": 0 for device-to-device non-human,
* 1 for device-to-device by unknown human */
uint16_t authentication_data_length; /* authentication mechanism 1-255 */
uint16_t vendor_id; /* authentication mechanism 200-255 */
uint8_t authentication_data[MAX_AUTH_DATA_LEN]; /* other than id, role, length and
* vendor-id */
uint16_t service_data_len; /* case-to-case */
uint8_t *service_data;
uint8_t service_type; /* first octet of service_data */
uint16_t padding_len; /* included in padding */
uint8_t padding[MAX_PAD_LEN];
uint8_t signature[SIGNATURE_LEN]; /* hmac-md5 or hmac-sha256, first 16 bytes */
} BACNET_SECURITY_WRAPPER;
typedef struct Challenge_Request {
uint8_t message_challenge; /* 1 as a response, everything else for other */
uint32_t orig_message_id;
uint32_t orig_timestamp;
} BACNET_CHALLENGE_REQUEST;
typedef struct Security_Payload {
uint16_t payload_length;
uint8_t *payload;
} BACNET_SECURITY_PAYLOAD;
struct Bad_Timestamp {
uint32_t expected_timestamp;
};
struct Cannot_Use_Key {
uint16_t key;
};
struct Incorrect_Key {
uint8_t number_of_keys;
uint16_t keys[MAX_INCORRECT_KEYS];
};
struct Unknown_Authentication_Type {
uint8_t original_authentication_type;
uint16_t vendor_id;
};
struct Unknown_Key {
uint16_t original_key;
};
struct Unknown_Key_Revision {
uint8_t original_key_revision;
};
struct Too_Many_Keys {
uint8_t max_num_of_keys;
};
struct Invalid_Key_Data {
uint16_t key;
};
typedef struct Security_Response {
uint16_t response_code;
uint32_t orig_message_id;
uint32_t orig_timestamp;
union {
struct Bad_Timestamp bad_timestamp;
struct Cannot_Use_Key cannot_use_key;
struct Incorrect_Key incorrect_key;
struct Unknown_Authentication_Type unknown_authentication_type;
struct Unknown_Key unknown_key;
struct Unknown_Key_Revision unknown_key_revision;
struct Too_Many_Keys too_many_keys;
struct Invalid_Key_Data invalid_key_data;
} response;
} BACNET_SECURITY_RESPONSE;
typedef struct Request_Key_Update {
uint8_t set_1_key_revision;
uint32_t set_1_activation_time;
uint32_t set_1_expiration_time;
uint8_t set_2_key_revision;
uint32_t set_2_activation_time;
uint32_t set_2_expiration_time;
uint8_t distribution_key_revision;
} BACNET_REQUEST_KEY_UPDATE;
typedef struct Key_Entry {
uint16_t key_identifier;
uint8_t key_len;
uint8_t key[MAX_KEY_LEN];
} BACNET_KEY_ENTRY;
typedef struct Update_Key_Set {
bool set_rae[2], set_ck[2], set_clr[2];
bool more;
bool remove; /* false for add, true for remove */
uint8_t set_key_revision[2];
uint32_t set_activation_time[2];
uint32_t set_expiration_time[2];
uint8_t set_key_count[2];
BACNET_KEY_ENTRY set_keys[2][MAX_UPDATE_KEY_COUNT];
} BACNET_UPDATE_KEY_SET;
typedef struct Update_Distribution_Key {
uint8_t key_revision;
BACNET_KEY_ENTRY key;
} BACNET_UPDATE_DISTRIBUTION_KEY;
typedef struct Request_Master_Key {
uint8_t no_supported_algorithms;
uint8_t es_algorithms[MAX_SUPPORTED_ALGORITHMS];
} BACNET_REQUEST_MASTER_KEY;
typedef struct Set_Master_Key {
BACNET_KEY_ENTRY key;
} BACNET_SET_MASTER_KEY;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* helper functions */
BACNET_KEY_IDENTIFIER_ALGORITHM key_algorithm(uint16_t id);
BACNET_KEY_IDENTIFIER_KEY_NUMBER key_number(uint16_t id);
/* key manipulation functions - port specific! */
BACNET_SECURITY_RESPONSE_CODE bacnet_master_key_set(BACNET_SET_MASTER_KEY *
key);
BACNET_SECURITY_RESPONSE_CODE
bacnet_distribution_key_update(BACNET_UPDATE_DISTRIBUTION_KEY * key);
BACNET_SECURITY_RESPONSE_CODE bacnet_key_set_update(BACNET_UPDATE_KEY_SET *
update_key_sets);
BACNET_SECURITY_RESPONSE_CODE bacnet_find_key(uint8_t revision,
BACNET_KEY_ENTRY * key);
/* signing/verification and encryption/decryption - port specific */
int key_sign_msg(BACNET_KEY_ENTRY * key,
uint8_t * msg,
uint32_t msg_len,
uint8_t * signature);
bool key_verify_sign_msg(BACNET_KEY_ENTRY * key,
uint8_t * msg,
uint32_t msg_len,
uint8_t * signature);
int key_encrypt_msg(BACNET_KEY_ENTRY * key,
uint8_t * msg,
uint32_t msg_len,
uint8_t * signature);
bool key_decrypt_msg(BACNET_KEY_ENTRY * key,
uint8_t * msg,
uint32_t msg_len,
uint8_t * signature);
void key_set_padding(BACNET_KEY_ENTRY * key,
int enc_len,
uint16_t * padding_len,
uint8_t * padding);
/* encoders */
int encode_security_wrapper(int bytes_before,
uint8_t * apdu,
BACNET_SECURITY_WRAPPER * wrapper);
int encode_challenge_request(uint8_t * apdu,
BACNET_CHALLENGE_REQUEST * bc_req);
int encode_security_payload(uint8_t * apdu,
BACNET_SECURITY_PAYLOAD * payload);
int encode_security_response(uint8_t * apdu,
BACNET_SECURITY_RESPONSE * resp);
int encode_request_key_update(uint8_t * apdu,
BACNET_REQUEST_KEY_UPDATE * req);
int encode_key_entry(uint8_t * apdu,
BACNET_KEY_ENTRY * entry);
int encode_update_key_set(uint8_t * apdu,
BACNET_UPDATE_KEY_SET * key_set);
int encode_update_distribution_key(uint8_t * apdu,
BACNET_UPDATE_DISTRIBUTION_KEY * dist_key);
int encode_request_master_key(uint8_t * apdu,
BACNET_REQUEST_MASTER_KEY * req_master_key);
int encode_set_master_key(uint8_t * apdu,
BACNET_SET_MASTER_KEY * set_master_key);
/* safe decoders */
int decode_security_wrapper_safe(int bytes_before,
uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_SECURITY_WRAPPER * wrapper);
int decode_challenge_request_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_CHALLENGE_REQUEST * bc_req);
int decode_security_payload_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_SECURITY_PAYLOAD * payload);
int decode_security_response_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_SECURITY_RESPONSE * resp);
int decode_request_key_update_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_REQUEST_KEY_UPDATE * req);
int decode_key_entry_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_KEY_ENTRY * entry);
int decode_update_key_set_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_UPDATE_KEY_SET * key_set);
int decode_update_distribution_key_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_UPDATE_DISTRIBUTION_KEY * dist_key);
int decode_request_master_key_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_REQUEST_MASTER_KEY * req_master_key);
int decode_set_master_key_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_SET_MASTER_KEY * set_master_key);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+34 -25
View File
@@ -80,13 +80,22 @@
/* #define MAX_APDU 1476 */
#if defined(BACDL_BIP)
#define MAX_APDU 1476
/* #define MAX_APDU 128 enable this IP for testing readrange so you get the More Follows flag set */
#elif defined (BACDL_ETHERNET)
#if defined(BACNET_SECURITY)
#define MAX_APDU 1420
#else
#define MAX_APDU 1476
#endif
#else
#if defined(BACNET_SECURITY)
#define MAX_APDU 412
#else
#define MAX_APDU 480
#endif
#endif
#endif
/* for confirmed messages, this is the number of transactions */
/* that we hold in a queue waiting for timeout. */
@@ -133,32 +142,32 @@
#endif
#if defined (BACAPP_ALL)
#define BACAPP_NULL
#define BACAPP_BOOLEAN
#define BACAPP_UNSIGNED
#define BACAPP_SIGNED
#define BACAPP_REAL
#define BACAPP_DOUBLE
#define BACAPP_OCTET_STRING
#define BACAPP_CHARACTER_STRING
#define BACAPP_BIT_STRING
#define BACAPP_ENUMERATED
#define BACAPP_DATE
#define BACAPP_TIME
#define BACAPP_OBJECT_ID
#define BACAPP_DEVICE_OBJECT_PROP_REF
#define BACAPP_LIGHTING_COMMAND
#define BACAPP_NULL
#define BACAPP_BOOLEAN
#define BACAPP_UNSIGNED
#define BACAPP_SIGNED
#define BACAPP_REAL
#define BACAPP_DOUBLE
#define BACAPP_OCTET_STRING
#define BACAPP_CHARACTER_STRING
#define BACAPP_BIT_STRING
#define BACAPP_ENUMERATED
#define BACAPP_DATE
#define BACAPP_TIME
#define BACAPP_OBJECT_ID
#define BACAPP_DEVICE_OBJECT_PROP_REF
#define BACAPP_LIGHTING_COMMAND
#elif defined (BACAPP_MINIMAL)
#define BACAPP_NULL
#define BACAPP_BOOLEAN
#define BACAPP_UNSIGNED
#define BACAPP_SIGNED
#define BACAPP_REAL
#define BACAPP_CHARACTER_STRING
#define BACAPP_ENUMERATED
#define BACAPP_DATE
#define BACAPP_TIME
#define BACAPP_OBJECT_ID
#define BACAPP_NULL
#define BACAPP_BOOLEAN
#define BACAPP_UNSIGNED
#define BACAPP_SIGNED
#define BACAPP_REAL
#define BACAPP_CHARACTER_STRING
#define BACAPP_ENUMERATED
#define BACAPP_DATE
#define BACAPP_TIME
#define BACAPP_OBJECT_ID
#endif
/*
+1
View File
@@ -74,6 +74,7 @@ CORE_SRC = \
$(BACNET_CORE)/readrange.c \
$(BACNET_CORE)/timestamp.c \
$(BACNET_CORE)/lighting.c \
$(BACNET_CORE)/bacsec.c \
$(BACNET_CORE)/version.c
HANDLER_SRC = \
+303
View File
@@ -0,0 +1,303 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#include "bacsec.h"
#include <string.h>
#include <time.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#define TMP_BUF_LEN 2048
/* structures for keys - we use existing structures */
BACNET_KEY_ENTRY master_key;
BACNET_UPDATE_DISTRIBUTION_KEY distribution_key;
BACNET_UPDATE_KEY_SET key_sets;
static bool rand_set = false;
static HMAC_CTX hmac_ctx;
static EVP_CIPHER_CTX evp_ctx;
static uint8_t tmp_buf[TMP_BUF_LEN];
static uint16_t next_mult_of_16(uint16_t arg)
{
if ((arg & 0xF) == 0)
return arg;
else
return ((arg >> 4) + 1) << 4;
}
/* signing/verification and encryption/decryption - port specific */
int key_sign_msg(BACNET_KEY_ENTRY * key,
uint8_t * msg,
uint32_t msg_len,
uint8_t * signature)
{
uint8_t full_signature[32]; /* longest case */
HMAC_CTX_init(&hmac_ctx);
switch (key_algorithm(key->key_identifier)) {
case KIA_AES_MD5:
HMAC_Init_ex(&hmac_ctx, &key->key[16], 16, EVP_md5(), NULL);
break;
case KIA_AES_SHA256:
HMAC_Init_ex(&hmac_ctx, &key->key[16], 32, EVP_sha256(), NULL);
break;
default:
return -1;
}
HMAC_Update(&hmac_ctx, msg, msg_len);
HMAC_Final(&hmac_ctx, full_signature, NULL); /* we ignore the signature size */
HMAC_CTX_cleanup(&hmac_ctx);
memcpy(signature, full_signature, SIGNATURE_LEN);
return 0;
}
bool key_verify_sign_msg(BACNET_KEY_ENTRY * key,
uint8_t * msg,
uint32_t msg_len,
uint8_t * signature)
{
uint8_t full_signature[32]; /* longest case */
HMAC_CTX_init(&hmac_ctx);
switch (key_algorithm(key->key_identifier)) {
case KIA_AES_MD5:
HMAC_Init_ex(&hmac_ctx, &key->key[16], 16, EVP_md5(), NULL);
break;
case KIA_AES_SHA256:
HMAC_Init_ex(&hmac_ctx, &key->key[16], 32, EVP_sha256(), NULL);
break;
default:
return false;
}
HMAC_Update(&hmac_ctx, msg, msg_len);
HMAC_Final(&hmac_ctx, full_signature, NULL); /* we ignore the signature size */
HMAC_CTX_cleanup(&hmac_ctx);
return (memcmp(signature, full_signature,
SIGNATURE_LEN) == 0 ? true : false);
}
int key_encrypt_msg(BACNET_KEY_ENTRY * key,
uint8_t * msg,
uint32_t msg_len,
uint8_t * signature)
{
int outlen, outlen2;
switch (key_algorithm(key->key_identifier)) {
case KIA_AES_MD5:
case KIA_AES_SHA256:
EVP_EncryptInit_ex(&evp_ctx, EVP_aes_128_cbc(), NULL, key->key,
signature);
break;
default:
return -1;
}
EVP_EncryptUpdate(&evp_ctx, tmp_buf, &outlen, msg, msg_len);
EVP_EncryptFinal(&evp_ctx, &msg[outlen], &outlen2);
EVP_CIPHER_CTX_cleanup(&evp_ctx);
if (outlen2 != 0)
return -1;
memcpy(msg, tmp_buf, msg_len);
return 0;
}
bool key_decrypt_msg(BACNET_KEY_ENTRY * key,
uint8_t * msg,
uint32_t msg_len,
uint8_t * signature)
{
int outlen, outlen2;
switch (key_algorithm(key->key_identifier)) {
case KIA_AES_MD5:
case KIA_AES_SHA256:
EVP_DecryptInit_ex(&evp_ctx, EVP_aes_128_cbc(), NULL, key->key,
signature);
break;
default:
return false;
}
if (EVP_DecryptUpdate(&evp_ctx, tmp_buf, &outlen, msg, msg_len) == 0)
return false;
if (EVP_DecryptFinal(&evp_ctx, &msg[outlen], &outlen2) == 0)
return false;
EVP_CIPHER_CTX_cleanup(&evp_ctx);
if (outlen2 != 0)
return false;
memcpy(msg, tmp_buf, msg_len);
return true;
}
void key_set_padding(BACNET_KEY_ENTRY * key,
int enc_len,
uint16_t * padding_len,
uint8_t * padding)
{
/* in the future, we should check for the block size, but for now it is always 16 */
int i;
uint16_t padlen = next_mult_of_16(enc_len + 2);
(void) key;
(void) padding_len;
if (!rand_set) {
srand(time(NULL));
rand_set = true;
}
if (padlen > 2)
for (i = 0; i < padlen - 2; i++)
padding[i] = rand();
}
BACNET_SECURITY_RESPONSE_CODE bacnet_master_key_set(BACNET_SET_MASTER_KEY *
key)
{
memcpy(&master_key, &key->key, sizeof(BACNET_KEY_ENTRY));
return SEC_RESP_SUCCESS;
}
BACNET_SECURITY_RESPONSE_CODE
bacnet_distribution_key_update(BACNET_UPDATE_DISTRIBUTION_KEY * key)
{
memcpy(&distribution_key, key, sizeof(BACNET_KEY_ENTRY));
return SEC_RESP_SUCCESS;
}
BACNET_SECURITY_RESPONSE_CODE bacnet_key_set_update(BACNET_UPDATE_KEY_SET *
update_key_sets)
{
int i, j, k, l;
bool found;
for (i = 0; i < 2; i++) {
if (update_key_sets->set_rae[i]) {
found = false;
/* try with a valid set */
for (j = 0; j < 2; j++)
if ((key_sets.set_key_revision[j] ==
update_key_sets->set_key_revision[i]) &&
key_sets.set_rae[j]) {
found = true;
break;
}
/* try with an empty set */
if (!found) {
for (j = 0; j < 2; j++)
if (!key_sets.set_rae[j]) {
found = true;
break;
}
}
/* failure */
if (!found)
return -SEC_RESP_UNKNOWN_KEY_REVISION;
/* just in case we're writing over an empty set */
key_sets.set_key_revision[j] =
update_key_sets->set_key_revision[i];
/* update revision activation and expiration time */
key_sets.set_activation_time[j] =
update_key_sets->set_activation_time[i];
key_sets.set_expiration_time[j] =
update_key_sets->set_expiration_time[i];
/* should we clear the key set? */
if (update_key_sets->set_clr[i]) {
key_sets.set_key_count[j] = 0;
}
for (k = 0; k < update_key_sets->set_key_count[i]; k++) {
found = false;
for (l = 0; l < key_sets.set_key_count[j]; l++)
if (update_key_sets->set_keys[i][k].key_identifier ==
key_sets.set_keys[j][l].key_identifier) {
found = true;
break;
}
if (!found) {
if (!update_key_sets->remove) { /* add key */
/* check for available space */
if (key_sets.set_key_count[j] == MAX_UPDATE_KEY_COUNT)
return -SEC_RESP_TOO_MANY_KEYS;
memcpy(&key_sets.set_keys[j][key_sets.
set_key_count[j]],
&update_key_sets->set_keys[i][k],
sizeof(BACNET_KEY_ENTRY));
key_sets.set_key_count[j]++;
} /* else do nothing, successfuly */
} else {
if (!update_key_sets->remove) { /* update key */
memcpy(&key_sets.set_keys[j][l],
&update_key_sets->set_keys[i][k],
sizeof(BACNET_KEY_ENTRY));
} else { /* remove key */
memmove(&key_sets.set_keys[j][l],
&key_sets.set_keys[j][l + 1],
sizeof(BACNET_KEY_ENTRY) *
(key_sets.set_key_count[j] - l));
key_sets.set_key_count[j]--;
}
}
}
}
}
return SEC_RESP_SUCCESS;
}
BACNET_SECURITY_RESPONSE_CODE bacnet_find_key(uint8_t revision,
BACNET_KEY_ENTRY * key)
{
int i, j;
unsigned int current_time = time(NULL);
switch (key_number(key->key_identifier)) {
case KIKN_DEVICE_MASTER:
if (revision != 0)
return -SEC_RESP_UNKNOWN_KEY_REVISION;
else
memcpy(key, &master_key, sizeof(BACNET_KEY_ENTRY));
break;
case KIKN_DISTRIBUTION:
if (revision != distribution_key.key_revision)
return -SEC_RESP_UNKNOWN_KEY_REVISION;
else
memcpy(key, &distribution_key.key, sizeof(BACNET_KEY_ENTRY));
break;
default: /* all other keys must be in a key set */
for (i = 0; i < 2; i++) {
if ((revision == key_sets.set_key_revision[i]) &&
(key_sets.set_activation_time[i] <= current_time) &&
(current_time <= key_sets.set_expiration_time[i])) {
for (j = 0; j < key_sets.set_key_count[i]; j++)
if (key->key_identifier ==
key_sets.set_keys[i][j].key_identifier) {
memcpy(key,
&key_sets.set_keys[i][j].key_identifier,
sizeof(BACNET_KEY_ENTRY));
return SEC_RESP_SUCCESS;
}
}
}
return -SEC_RESP_UNKNOWN_KEY_REVISION;
}
return SEC_RESP_SUCCESS;
}
+834
View File
@@ -0,0 +1,834 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "bacdcode.h"
#include "bacsec.h"
BACNET_KEY_IDENTIFIER_ALGORITHM key_algorithm(uint16_t id)
{
return (BACNET_KEY_IDENTIFIER_ALGORITHM) ((id >> 8) & 0xFF);
}
BACNET_KEY_IDENTIFIER_KEY_NUMBER key_number(uint16_t id)
{
return (BACNET_KEY_IDENTIFIER_KEY_NUMBER) (id & 0xFF);
}
int encode_security_wrapper(int bytes_before,
uint8_t * apdu,
BACNET_SECURITY_WRAPPER * wrapper)
{
int curr = 0;
int enc_begin = 0;
BACNET_KEY_ENTRY key;
BACNET_SECURITY_RESPONSE_CODE res = SEC_RESP_SUCCESS;
apdu[curr] = 0;
/* control byte */
if (wrapper->payload_net_or_bvll_flag) {
apdu[curr] |= 1 << 7;
}
/* encryption flag will be set after signature calculation */
/* bit 5 is reserved and shall be 0 */
if (wrapper->authentication_flag) {
apdu[curr] |= 1 << 4;
}
if (wrapper->do_not_unwrap_flag) {
apdu[curr] |= 1 << 3;
}
if (wrapper->do_not_decrypt_flag) {
apdu[curr] |= 1 << 2;
}
if (wrapper->non_trusted_source_flag) {
apdu[curr] |= 1 << 1;
}
if (wrapper->secured_by_router_flag) {
apdu[curr] |= 1;
}
curr++;
/* basic integrity checks */
if (wrapper->do_not_decrypt_flag && !wrapper->do_not_unwrap_flag) {
return -SEC_RESP_MALFORMED_MESSAGE;
}
if (!wrapper->encrypted_flag && wrapper->do_not_decrypt_flag) {
return -SEC_RESP_MALFORMED_MESSAGE;
}
/* key */
apdu[curr++] = wrapper->key_revision;
curr += encode_unsigned16(&apdu[curr], wrapper->key_identifier);
/* find appropriate key */
key.key_identifier = wrapper->key_identifier;
res = bacnet_find_key(wrapper->key_revision, &key);
if (res != SEC_RESP_SUCCESS) {
return -res;
}
/* source device instance */
curr += encode_unsigned24(&apdu[curr], wrapper->source_device_instance);
/* message id */
curr += encode_unsigned32(&apdu[curr], wrapper->message_id);
/* timestamp */
curr += encode_unsigned32(&apdu[curr], wrapper->timestamp);
/* begin encryption starting from destination device instance */
enc_begin = curr;
/* destination device instance */
curr +=
encode_unsigned24(&apdu[curr], wrapper->destination_device_instance);
/* dst address */
curr += encode_unsigned16(&apdu[curr], wrapper->dnet);
apdu[curr++] = wrapper->dlen;
memcpy(&apdu[curr], wrapper->dadr, wrapper->dlen);
curr += wrapper->dlen;
/* src address */
curr += encode_unsigned16(&apdu[curr], wrapper->snet);
apdu[curr++] = wrapper->slen;
memcpy(&apdu[curr], wrapper->sadr, wrapper->slen);
curr += wrapper->slen;
/* authentication */
if (wrapper->authentication_flag) {
apdu[curr++] = wrapper->authentication_mechanism;
/* authentication data */
curr += encode_unsigned16(&apdu[curr], wrapper->user_id);
apdu[curr++] = wrapper->user_role;
if ((wrapper->authentication_mechanism >= 1) &&
(wrapper->authentication_mechanism <= 199)) {
curr +=
encode_unsigned16(&apdu[curr],
wrapper->authentication_data_length + 5);
memcpy(&apdu[curr], wrapper->authentication_data,
wrapper->authentication_data_length);
curr += wrapper->authentication_data_length;
} else if (wrapper->authentication_mechanism >= 200) {
curr +=
encode_unsigned16(&apdu[curr],
wrapper->authentication_data_length + 7);
curr += encode_unsigned16(&apdu[curr], wrapper->vendor_id);
memcpy(&apdu[curr], wrapper->authentication_data,
wrapper->authentication_data_length);
curr += wrapper->authentication_data_length;
}
}
memcpy(&apdu[curr], wrapper->service_data, wrapper->service_data_len);
curr += wrapper->service_data_len;
/* signature calculation */
key_sign_msg(&key, &apdu[-bytes_before], bytes_before + curr,
wrapper->signature);
/* padding and encryption */
if (wrapper->encrypted_flag) {
/* set encryption flag, signing is done */
apdu[0] |= 1 << 6;
/* handle padding */
key_set_padding(&key, curr - enc_begin, &wrapper->padding_len,
wrapper->padding);
if (wrapper->padding_len > 2) {
memcpy(&apdu[curr], wrapper->padding, wrapper->padding_len - 2);
curr += wrapper->padding_len - 2;
}
curr += encode_unsigned16(&apdu[curr], wrapper->padding_len);
/* encryption */
key_encrypt_msg(&key, &apdu[enc_begin], curr - enc_begin,
wrapper->signature);
}
memcpy(&apdu[curr], wrapper->signature, SIGNATURE_LEN);
curr += SIGNATURE_LEN;
return curr;
}
int encode_challenge_request(uint8_t * apdu,
BACNET_CHALLENGE_REQUEST * bc_req)
{
int curr = 0;
apdu[curr++] = bc_req->message_challenge;
curr += encode_unsigned32(&apdu[curr], bc_req->orig_message_id);
curr += encode_unsigned32(&apdu[curr], bc_req->orig_timestamp);
return curr;
}
int encode_security_payload(uint8_t * apdu,
BACNET_SECURITY_PAYLOAD * payload)
{
encode_unsigned16(&apdu[0], payload->payload_length);
memcpy(&apdu[2], payload->payload, payload->payload_length);
return 2 + payload->payload_length;
}
int encode_security_response(uint8_t * apdu,
BACNET_SECURITY_RESPONSE * resp)
{
int curr = 0;
int i;
apdu[curr++] = resp->response_code;
curr += encode_unsigned32(&apdu[curr], resp->orig_message_id);
curr += encode_unsigned32(&apdu[curr], resp->orig_timestamp);
switch ((BACNET_SECURITY_RESPONSE_CODE) resp->response_code) {
case SEC_RESP_BAD_TIMESTAMP:
curr +=
encode_unsigned32(&apdu[curr],
resp->response.bad_timestamp.expected_timestamp);
break;
case SEC_RESP_CANNOT_USE_KEY:
curr +=
encode_unsigned16(&apdu[curr],
resp->response.cannot_use_key.key);
break;
case SEC_RESP_INCORRECT_KEY:
apdu[curr++] = resp->response.incorrect_key.number_of_keys;
for (i = 0; i < resp->response.incorrect_key.number_of_keys; i++) {
curr +=
encode_unsigned16(&apdu[curr],
resp->response.incorrect_key.keys[i]);
}
break;
case SEC_RESP_UNKNOWN_AUTHENTICATION_TYPE:
apdu[curr++] =
resp->response.unknown_authentication_type.
original_authentication_type;
curr +=
encode_unsigned16(&apdu[curr],
resp->response.unknown_authentication_type.vendor_id);
break;
case SEC_RESP_UNKNOWN_KEY:
curr +=
encode_unsigned16(&apdu[curr],
resp->response.unknown_key.original_key);
break;
case SEC_RESP_UNKNOWN_KEY_REVISION:
apdu[curr++] =
resp->response.unknown_key_revision.original_key_revision;
break;
case SEC_RESP_TOO_MANY_KEYS:
apdu[curr++] = resp->response.too_many_keys.max_num_of_keys;
break;
case SEC_RESP_INVALID_KEY_DATA:
curr +=
encode_unsigned16(&apdu[curr],
resp->response.invalid_key_data.key);
break;
case SEC_RESP_SUCCESS:
case SEC_RESP_ACCESS_DENIED:
case SEC_RESP_BAD_DESTINATION_ADDRESS:
case SEC_RESP_BAD_DESTINATION_DEVICE_ID:
case SEC_RESP_BAD_SIGNATURE:
case SEC_RESP_BAD_SOURCE_ADDRESS:
case SEC_RESP_CANNOT_VERIFY_MESSAGE_ID:
case SEC_RESP_CORRECT_KEY_REVISION:
case SEC_RESP_DESTINATION_DEVICE_ID_REQUIRED:
case SEC_RESP_DUPLICATE_MESSAGE:
case SEC_RESP_ENCRYPTION_NOT_CONFIGURED:
case SEC_RESP_ENCRYPTION_REQUIRED:
case SEC_RESP_KEY_UPDATE_IN_PROGRESS:
case SEC_RESP_MALFORMED_MESSAGE:
case SEC_RESP_NOT_KEY_SERVER:
case SEC_RESP_SECURITY_NOT_CONFIGURED:
case SEC_RESP_SOURCE_SECURITY_REQUIRED:
case SEC_RESP_UNKNOWN_SOURCE_MESSAGE:
break;
default:
return -1; /* unknown message type */
}
return curr;
}
int encode_request_key_update(uint8_t * apdu,
BACNET_REQUEST_KEY_UPDATE * req)
{
int curr = 0;
apdu[curr++] = req->set_1_key_revision;
curr += encode_unsigned32(&apdu[curr], req->set_1_activation_time);
curr += encode_unsigned32(&apdu[curr], req->set_1_expiration_time);
apdu[curr++] = req->set_2_key_revision;
curr += encode_unsigned32(&apdu[curr], req->set_2_activation_time);
curr += encode_unsigned32(&apdu[curr], req->set_2_expiration_time);
apdu[curr++] = req->distribution_key_revision;
return curr;
}
int encode_key_entry(uint8_t * apdu,
BACNET_KEY_ENTRY * entry)
{
int curr = 0;
curr += encode_unsigned16(&apdu[curr], entry->key_identifier);
apdu[curr++] = entry->key_len;
memcpy(&apdu[curr], entry->key, entry->key_len);
curr += entry->key_len;
return curr;
}
int encode_update_key_set(uint8_t * apdu,
BACNET_UPDATE_KEY_SET * key_set)
{
int curr = 0;
int i, res;
apdu[curr] = 0;
if (key_set->remove) {
apdu[curr] |= 1;
}
if (key_set->more) {
apdu[curr] |= 1 << 1;
}
if (key_set->set_clr[1]) {
apdu[curr] |= 1 << 2;
}
if (key_set->set_ck[1]) {
apdu[curr] |= 1 << 3;
}
if (key_set->set_rae[1]) {
apdu[curr] |= 1 << 4;
}
if (key_set->set_clr[0]) {
apdu[curr] |= 1 << 5;
}
if (key_set->set_ck[0]) {
apdu[curr] |= 1 << 6;
}
if (key_set->set_rae[0]) {
apdu[curr] |= 1 << 7;
}
curr++;
if (key_set->set_rae[0]) {
apdu[curr++] = key_set->set_key_revision[0];
curr +=
encode_unsigned32(&apdu[curr], key_set->set_activation_time[0]);
curr +=
encode_unsigned32(&apdu[curr], key_set->set_expiration_time[0]);
}
if (key_set->set_ck[0]) {
apdu[curr++] = key_set->set_key_count[0];
if (key_set->set_key_count[0] > MAX_UPDATE_KEY_COUNT) {
return -1;
}
for (i = 0; i < key_set->set_key_count[0]; i++) {
res = encode_key_entry(&apdu[curr], &key_set->set_keys[0][i]);
if (res < 0) {
return -1;
}
curr += res;
}
}
if (key_set->set_rae[1]) {
apdu[curr++] = key_set->set_key_revision[1];
curr +=
encode_unsigned32(&apdu[curr], key_set->set_activation_time[1]);
curr +=
encode_unsigned32(&apdu[curr], key_set->set_expiration_time[1]);
}
if (key_set->set_ck[1]) {
apdu[curr++] = key_set->set_key_count[1];
if (key_set->set_key_count[1] > MAX_UPDATE_KEY_COUNT) {
return -1;
}
for (i = 0; i < key_set->set_key_count[1]; i++) {
res = encode_key_entry(&apdu[curr], &key_set->set_keys[1][i]);
if (res < 0) {
return -1;
}
curr += res;
}
}
return curr;
}
int encode_update_distribution_key(uint8_t * apdu,
BACNET_UPDATE_DISTRIBUTION_KEY * dist_key)
{
int curr = 0;
int res;
apdu[curr++] = dist_key->key_revision;
res = encode_key_entry(&apdu[curr], &dist_key->key);
if (res < 0) {
return -1;
}
return curr + res;
}
int encode_request_master_key(uint8_t * apdu,
BACNET_REQUEST_MASTER_KEY * req_master_key)
{
int curr = 0;
apdu[curr++] = req_master_key->no_supported_algorithms;
memcpy(&apdu[curr], req_master_key->es_algorithms,
req_master_key->no_supported_algorithms);
return curr + req_master_key->no_supported_algorithms;
}
int encode_set_master_key(uint8_t * apdu,
BACNET_SET_MASTER_KEY * set_master_key)
{
return encode_key_entry(apdu, &set_master_key->key);
}
int decode_security_wrapper_safe(int bytes_before,
uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_SECURITY_WRAPPER * wrapper)
{
int curr = 0;
int enc_begin = 0;
int real_len = apdu_len_remaining - SIGNATURE_LEN;
BACNET_KEY_ENTRY key;
BACNET_SECURITY_RESPONSE_CODE res = SEC_RESP_SUCCESS;
if (apdu_len_remaining < 40) {
return -SEC_RESP_MALFORMED_MESSAGE;
}
wrapper->payload_net_or_bvll_flag = ((apdu[curr] & (1 << 7)) != 0);
wrapper->encrypted_flag = ((apdu[curr] & (1 << 6)) != 0);
wrapper->authentication_flag = ((apdu[curr] & (1 << 4)) != 0);
wrapper->do_not_unwrap_flag = ((apdu[curr] & (1 << 3)) != 0);
wrapper->do_not_decrypt_flag = ((apdu[curr] & (1 << 2)) != 0);
wrapper->non_trusted_source_flag = ((apdu[curr] & (1 << 1)) != 0);
wrapper->secured_by_router_flag = ((apdu[curr] & 1) != 0);
/* basic integrity checks */
if (wrapper->do_not_decrypt_flag && !wrapper->do_not_unwrap_flag) {
return -SEC_RESP_MALFORMED_MESSAGE;
}
if (!wrapper->encrypted_flag && wrapper->do_not_decrypt_flag) {
return -SEC_RESP_MALFORMED_MESSAGE;
}
/* remove encryption flag for signature validation */
apdu[curr] &= ~((uint8_t) (1 << 6));
curr++;
/* key */
wrapper->key_revision = apdu[curr++];
curr += decode_unsigned16(&apdu[curr], &wrapper->key_identifier);
/* find appropriate key */
key.key_identifier = wrapper->key_identifier;
res = bacnet_find_key(wrapper->key_revision, &key);
if (res != SEC_RESP_SUCCESS) {
return -res;
}
/* source device instance */
curr += decode_unsigned24(&apdu[curr], &wrapper->source_device_instance);
/* message id */
curr += decode_unsigned32(&apdu[curr], &wrapper->message_id);
/* timestamp */
curr += decode_unsigned32(&apdu[curr], &wrapper->timestamp);
/* begin decryption starting from destination device instance */
enc_begin = curr;
/* read signature */
memcpy(wrapper->signature, &apdu[real_len], SIGNATURE_LEN);
if (wrapper->encrypted_flag) {
if (!key_decrypt_msg(&key, &apdu[enc_begin], real_len - enc_begin,
wrapper->signature)) {
return -SEC_RESP_MALFORMED_MESSAGE;
}
curr += decode_unsigned16(&apdu[real_len - 2], &wrapper->padding_len);
real_len -= wrapper->padding_len;
memcpy(wrapper->padding, &apdu[wrapper->padding_len],
wrapper->padding_len - 2);
}
/* destination device instance */
curr +=
decode_unsigned24(&apdu[curr], &wrapper->destination_device_instance);
/* dst address */
curr += decode_unsigned16(&apdu[curr], &wrapper->dnet);
wrapper->dlen = apdu[curr++];
memcpy(wrapper->dadr, &apdu[curr], wrapper->dlen);
curr += wrapper->dlen;
/* src address */
curr += decode_unsigned16(&apdu[curr], &wrapper->snet);
wrapper->slen = apdu[curr++];
memcpy(wrapper->sadr, &apdu[curr], wrapper->slen);
curr += wrapper->slen;
/* authentication */
if (wrapper->authentication_flag) {
wrapper->authentication_mechanism = apdu[curr++];
/* authentication data */
curr += decode_unsigned16(&apdu[curr], &wrapper->user_id);
wrapper->user_role = apdu[curr++];
if ((wrapper->authentication_mechanism >= 1) &&
(wrapper->authentication_mechanism <= 199)) {
curr +=
decode_unsigned16(&apdu[curr],
&wrapper->authentication_data_length);
wrapper->authentication_data_length -= 5;
memcpy(wrapper->authentication_data, &apdu[curr],
wrapper->authentication_data_length);
curr += wrapper->authentication_data_length;
} else if (wrapper->authentication_mechanism >= 200) {
curr +=
decode_unsigned16(&apdu[curr],
&wrapper->authentication_data_length);
wrapper->authentication_data_length -= 7;
curr += decode_unsigned16(&apdu[curr], &wrapper->vendor_id);
memcpy(wrapper->authentication_data, &apdu[curr],
wrapper->authentication_data_length);
curr += wrapper->authentication_data_length;
}
}
wrapper->service_data_len = real_len - curr;
memcpy(wrapper->service_data, &apdu[curr], wrapper->service_data_len);
curr += wrapper->service_data_len;
if (!key_verify_sign_msg(&key, &apdu[-bytes_before],
bytes_before + real_len, wrapper->signature)) {
return -SEC_RESP_BAD_SIGNATURE;
}
return curr;
}
int decode_challenge_request_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_CHALLENGE_REQUEST * bc_req)
{
int curr = 0;
if (apdu_len_remaining < 9) {
return -1;
}
bc_req->message_challenge = apdu[curr++];
curr += decode_unsigned32(&apdu[curr], &bc_req->orig_message_id);
curr += decode_unsigned32(&apdu[curr], &bc_req->orig_timestamp);
return curr; /* always 9! */
}
int decode_security_payload_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_SECURITY_PAYLOAD * payload)
{
if (apdu_len_remaining < 2) {
return -1;
}
decode_unsigned16(&apdu[0], &payload->payload_length);
if (apdu_len_remaining - 2 < payload->payload_length) {
return -1;
}
memcpy(payload->payload, &apdu[2], payload->payload_length);
return 2 + payload->payload_length;
}
int decode_security_response_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_SECURITY_RESPONSE * resp)
{
int curr = 0;
int i;
if (apdu_len_remaining < 9) {
return -1;
}
resp->response_code = apdu[curr++];
curr += decode_unsigned32(&apdu[curr], &resp->orig_message_id);
curr += decode_unsigned32(&apdu[curr], &resp->orig_timestamp);
switch ((BACNET_SECURITY_RESPONSE_CODE) resp->response_code) {
case SEC_RESP_BAD_TIMESTAMP:
if (apdu_len_remaining < 13) {
return -1;
}
curr +=
decode_unsigned32(&apdu[curr],
&resp->response.bad_timestamp.expected_timestamp);
break;
case SEC_RESP_CANNOT_USE_KEY:
if (apdu_len_remaining < 11) {
return -1;
}
curr +=
decode_unsigned16(&apdu[curr],
&resp->response.cannot_use_key.key);
break;
case SEC_RESP_INCORRECT_KEY:
if (apdu_len_remaining < 10) {
return -1;
}
resp->response.incorrect_key.number_of_keys = apdu[curr++];
if (apdu_len_remaining - 10 <
resp->response.incorrect_key.number_of_keys * 2) {
return -1;
}
for (i = 0; i < resp->response.incorrect_key.number_of_keys; i++) {
curr +=
decode_unsigned16(&apdu[curr],
&resp->response.incorrect_key.keys[i]);
}
break;
case SEC_RESP_UNKNOWN_AUTHENTICATION_TYPE:
if (apdu_len_remaining < 12) {
return -1;
}
resp->response.unknown_authentication_type.
original_authentication_type = apdu[curr++];
curr +=
decode_unsigned16(&apdu[curr],
&resp->response.unknown_authentication_type.vendor_id);
if (resp->response.unknown_authentication_type.
original_authentication_type < 200 &&
resp->response.unknown_authentication_type.vendor_id != 0) {
return -1;
}
break;
case SEC_RESP_UNKNOWN_KEY:
if (apdu_len_remaining < 11) {
return -1;
}
curr +=
decode_unsigned16(&apdu[curr],
&resp->response.unknown_key.original_key);
break;
case SEC_RESP_UNKNOWN_KEY_REVISION:
if (apdu_len_remaining < 10) {
return -1;
}
resp->response.unknown_key_revision.original_key_revision =
apdu[curr++];
break;
case SEC_RESP_TOO_MANY_KEYS:
if (apdu_len_remaining < 10) {
return -1;
}
resp->response.too_many_keys.max_num_of_keys = apdu[curr++];
break;
case SEC_RESP_INVALID_KEY_DATA:
if (apdu_len_remaining < 11) {
return -1;
}
curr +=
decode_unsigned16(&apdu[curr],
&resp->response.invalid_key_data.key);
break;
case SEC_RESP_SUCCESS:
case SEC_RESP_ACCESS_DENIED:
case SEC_RESP_BAD_DESTINATION_ADDRESS:
case SEC_RESP_BAD_DESTINATION_DEVICE_ID:
case SEC_RESP_BAD_SIGNATURE:
case SEC_RESP_BAD_SOURCE_ADDRESS:
case SEC_RESP_CANNOT_VERIFY_MESSAGE_ID:
case SEC_RESP_CORRECT_KEY_REVISION:
case SEC_RESP_DESTINATION_DEVICE_ID_REQUIRED:
case SEC_RESP_DUPLICATE_MESSAGE:
case SEC_RESP_ENCRYPTION_NOT_CONFIGURED:
case SEC_RESP_ENCRYPTION_REQUIRED:
case SEC_RESP_KEY_UPDATE_IN_PROGRESS:
case SEC_RESP_MALFORMED_MESSAGE:
case SEC_RESP_NOT_KEY_SERVER:
case SEC_RESP_SECURITY_NOT_CONFIGURED:
case SEC_RESP_SOURCE_SECURITY_REQUIRED:
case SEC_RESP_UNKNOWN_SOURCE_MESSAGE:
break;
default:
return -1; /* unknown message type */
}
return curr;
}
int decode_request_key_update_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_REQUEST_KEY_UPDATE * req)
{
int curr = 0;
if (apdu_len_remaining < 19) {
return -1;
}
req->set_1_key_revision = apdu[curr++];
curr += decode_unsigned32(&apdu[curr], &req->set_1_activation_time);
curr += decode_unsigned32(&apdu[curr], &req->set_1_expiration_time);
req->set_2_key_revision = apdu[curr++];
curr += decode_unsigned32(&apdu[curr], &req->set_2_activation_time);
curr += decode_unsigned32(&apdu[curr], &req->set_2_expiration_time);
req->distribution_key_revision = apdu[curr++];
return curr;
}
int decode_key_entry_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_KEY_ENTRY * entry)
{
int curr = 0;
if (apdu_len_remaining < 3) {
return -1;
}
curr += decode_unsigned16(&apdu[curr], &entry->key_identifier);
entry->key_len = apdu[curr++];
if (apdu_len_remaining - 3 < entry->key_len ||
entry->key_len > MAX_KEY_LEN) {
return -1;
}
memcpy(entry->key, &apdu[curr], entry->key_len);
curr += entry->key_len;
return curr;
}
int decode_update_key_set_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_UPDATE_KEY_SET * key_set)
{
int curr = 0;
int i, res;
if (apdu_len_remaining < 1) {
return -1;
}
if (apdu[curr] & 1) {
key_set->remove = true;
}
if ((apdu[curr] >> 1) & 1) {
key_set->more = true;
}
if ((apdu[curr] >> 2) & 1) {
key_set->set_clr[1] = true;
}
if ((apdu[curr] >> 3) & 1) {
key_set->set_ck[1] = true;
}
if ((apdu[curr] >> 4) & 1) {
key_set->set_rae[1] = true;
}
if ((apdu[curr] >> 5) & 1) {
key_set->set_clr[0] = true;
}
if ((apdu[curr] >> 6) & 1) {
key_set->set_ck[0] = true;
}
if ((apdu[curr] >> 7) & 1) {
key_set->set_rae[0] = true;
}
curr++;
if (key_set->set_rae[0]) {
if (apdu_len_remaining - curr < 9) {
return -1;
}
key_set->set_key_revision[0] = apdu[curr++];
curr +=
decode_unsigned32(&apdu[curr], &key_set->set_activation_time[0]);
curr +=
decode_unsigned32(&apdu[curr], &key_set->set_expiration_time[0]);
}
if (key_set->set_ck[0]) {
if (apdu_len_remaining - curr < 1) {
return -1;
}
key_set->set_key_count[0] = apdu[curr++];
if (key_set->set_key_count[0] > MAX_UPDATE_KEY_COUNT) {
return -1;
}
for (i = 0; i < key_set->set_key_count[0]; i++) {
res =
decode_key_entry_safe(apdu + curr, apdu_len_remaining - curr,
&key_set->set_keys[0][i]);
if (res < 0) {
return -1;
}
curr += res;
}
}
if (key_set->set_rae[1]) {
if (apdu_len_remaining - curr < 9) {
return -1;
}
key_set->set_key_revision[1] = apdu[curr++];
curr +=
decode_unsigned32(&apdu[curr], &key_set->set_activation_time[1]);
curr +=
decode_unsigned32(&apdu[curr], &key_set->set_expiration_time[1]);
}
if (key_set->set_ck[1]) {
if (apdu_len_remaining - curr < 1) {
return -1;
}
key_set->set_key_count[1] = apdu[curr++];
if (key_set->set_key_count[1] > MAX_UPDATE_KEY_COUNT) {
return -1;
}
for (i = 0; i < key_set->set_key_count[1]; i++) {
res =
decode_key_entry_safe(apdu + curr, apdu_len_remaining - curr,
&key_set->set_keys[1][i]);
if (res < 0) {
return -1;
}
curr += res;
}
}
return curr;
}
int decode_update_distribution_key_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_UPDATE_DISTRIBUTION_KEY * dist_key)
{
int curr = 0;
int res;
if (apdu_len_remaining < 1) {
return -1;
}
dist_key->key_revision = apdu[curr++];
res =
decode_key_entry_safe(&apdu[curr], apdu_len_remaining - curr,
&dist_key->key);
if (res < 0) {
return -1;
}
return curr + res;
}
int decode_request_master_key_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_REQUEST_MASTER_KEY * req_master_key)
{
uint32_t curr = 0;
if (apdu_len_remaining < 1) {
return -1;
}
req_master_key->no_supported_algorithms = apdu[curr++];
if (apdu_len_remaining < curr + req_master_key->no_supported_algorithms) {
return -1;
}
memcpy(req_master_key->es_algorithms, &apdu[curr],
req_master_key->no_supported_algorithms);
return curr + req_master_key->no_supported_algorithms;
}
int decode_set_master_key_safe(uint8_t * apdu,
uint32_t apdu_len_remaining,
BACNET_SET_MASTER_KEY * set_master_key)
{
return decode_key_entry_safe(apdu, apdu_len_remaining,
&set_master_key->key);
}