diff --git a/bacnet-stack/include/bacenum.h b/bacnet-stack/include/bacenum.h index 5f51023c..08e05732 100644 --- a/bacnet-stack/include/bacenum.h +++ b/bacnet-stack/include/bacenum.h @@ -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 */ diff --git a/bacnet-stack/include/bacsec.h b/bacnet-stack/include/bacsec.h new file mode 100644 index 00000000..156147e9 --- /dev/null +++ b/bacnet-stack/include/bacsec.h @@ -0,0 +1,290 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* 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 +#include + +#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 diff --git a/bacnet-stack/include/config.h b/bacnet-stack/include/config.h index da806ec0..59adef48 100644 --- a/bacnet-stack/include/config.h +++ b/bacnet-stack/include/config.h @@ -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 /* diff --git a/bacnet-stack/lib/Makefile b/bacnet-stack/lib/Makefile index b5bda394..35d84d53 100644 --- a/bacnet-stack/lib/Makefile +++ b/bacnet-stack/lib/Makefile @@ -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 = \ diff --git a/bacnet-stack/ports/linux/bacsec_linux.c b/bacnet-stack/ports/linux/bacsec_linux.c new file mode 100644 index 00000000..8e6fc928 --- /dev/null +++ b/bacnet-stack/ports/linux/bacsec_linux.c @@ -0,0 +1,303 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* 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 +#include +#include +#include + +#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; +} diff --git a/bacnet-stack/src/bacsec.c b/bacnet-stack/src/bacsec.c new file mode 100644 index 00000000..32b7f647 --- /dev/null +++ b/bacnet-stack/src/bacsec.c @@ -0,0 +1,834 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* 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 +#include +#include +#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); +}