Add secure transport and OAM router runtime implementations
- Implement secure transport mechanisms in `gateway_knx_secure_transport.cpp` for handling secure sessions, including AES encryption, session key generation, and secure packet wrapping and unwrapping. - Introduce `OamRouterRuntime` in `oam_router_runtime.cpp` to manage OAM router identity, individual addresses, and tunnel frame handling. - Enhance secure session management with functions for session allocation, authentication, and secure packet processing. - Ensure compatibility with existing KNXnet/IP protocols while adding support for secure communications. Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
@@ -199,6 +199,22 @@ cJSON* FactoryCertificateToCjson(const openknx::FactoryCertificatePayload& certi
|
||||
return root;
|
||||
}
|
||||
|
||||
cJSON* IpSecureCredentialStatusToCjson(
|
||||
const openknx::IpSecureCredentialStatus& status) {
|
||||
cJSON* root = cJSON_CreateObject();
|
||||
if (root == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
cJSON_AddBoolToObject(root, "activated", status.activated);
|
||||
cJSON_AddBoolToObject(root, "backboneKeyAvailable", status.backboneKeyAvailable);
|
||||
cJSON_AddBoolToObject(root, "deviceAuthenticationKeyAvailable",
|
||||
status.deviceAuthenticationKeyAvailable);
|
||||
cJSON_AddNumberToObject(root, "tunnelUserCount", status.tunnelUserCount);
|
||||
cJSON_AddNumberToObject(root, "routingSequence",
|
||||
static_cast<double>(status.routingSequence));
|
||||
return root;
|
||||
}
|
||||
|
||||
cJSON* SecurityFailuresToCjson() {
|
||||
cJSON* root = cJSON_CreateObject();
|
||||
if (root == nullptr) {
|
||||
@@ -1522,6 +1538,10 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
[this](uint16_t group_object_number, const uint8_t* data, size_t len) {
|
||||
return service.routeKnxGroupObjectWrite(group_object_number, data, len);
|
||||
});
|
||||
knx_router->setOamIpSecureRoutingSequenceStoreHandler([](uint64_t sequence) {
|
||||
openknx::StoreOamIpSecureRoutingSequence(sequence);
|
||||
});
|
||||
knx_router->setOamIpSecureCredentials(openknx::LoadOamIpSecureCredentialMaterial());
|
||||
if (const auto active_knx = activeKnxConfigLocked(); active_knx.has_value()) {
|
||||
knx->setConfig(active_knx.value());
|
||||
knx_router->setConfig(active_knx.value());
|
||||
@@ -2154,6 +2174,7 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
runtime_config.individual_address);
|
||||
knx->setConfig(runtime_config);
|
||||
knx_router->setConfig(runtime_config);
|
||||
knx_router->setOamIpSecureCredentials(openknx::LoadOamIpSecureCredentialMaterial());
|
||||
knx_router->setCommissioningOnly(commissioning_only);
|
||||
if (!runtime_config.ip_router_enabled) {
|
||||
knx_started = false;
|
||||
@@ -2218,6 +2239,7 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
endpoint_runtime = const_cast<GatewayBridgeService&>(service).selectKnxEndpointRuntime();
|
||||
}
|
||||
bool programming_mode = false;
|
||||
bool oam_programming_mode = false;
|
||||
bool programming_control_available = false;
|
||||
int endpoint_owner_gateway_id = -1;
|
||||
if (endpoint_runtime != nullptr) {
|
||||
@@ -2227,6 +2249,7 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
endpoint_runtime->knx_router->started();
|
||||
if (programming_control_available) {
|
||||
programming_mode = endpoint_runtime->knx_router->programmingMode();
|
||||
oam_programming_mode = endpoint_runtime->knx_router->oamProgrammingMode();
|
||||
}
|
||||
}
|
||||
const auto effective_knx =
|
||||
@@ -2236,6 +2259,7 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
cJSON_AddBoolToObject(knx_json, "started", knx_started);
|
||||
cJSON_AddBoolToObject(knx_json, "routerReady", knx_router != nullptr && knx_router->started());
|
||||
cJSON_AddBoolToObject(knx_json, "programmingMode", programming_mode);
|
||||
cJSON_AddBoolToObject(knx_json, "oamProgrammingMode", oam_programming_mode);
|
||||
cJSON_AddBoolToObject(knx_json, "programmingControlAvailable",
|
||||
programming_control_available);
|
||||
cJSON_AddBoolToObject(knx_json, "endpointOwner",
|
||||
@@ -2265,7 +2289,11 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
#else
|
||||
cJSON_AddBoolToObject(security_json, "knxnetIpSecureServicesRecognized", false);
|
||||
#endif
|
||||
#if defined(CONFIG_GATEWAY_KNX_IP_SECURE_SUPPORTED)
|
||||
cJSON_AddBoolToObject(security_json, "knxnetIpSecureImplemented", true);
|
||||
#else
|
||||
cJSON_AddBoolToObject(security_json, "knxnetIpSecureImplemented", false);
|
||||
#endif
|
||||
#if defined(CONFIG_GATEWAY_KNX_SECURITY_DEV_ENDPOINTS)
|
||||
cJSON_AddBoolToObject(security_json, "developmentEndpointsEnabled", true);
|
||||
#else
|
||||
@@ -2294,6 +2322,27 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
cJSON_AddItemToObject(security_json, "failures", failures_json);
|
||||
}
|
||||
#endif
|
||||
cJSON* oam_security_json = cJSON_CreateObject();
|
||||
if (oam_security_json != nullptr) {
|
||||
cJSON* oam_fdsk_json = FactoryFdskInfoToCjson(
|
||||
openknx::LoadOamFactoryFdskInfo(), false);
|
||||
if (oam_fdsk_json != nullptr) {
|
||||
cJSON_AddItemToObject(oam_security_json, "factorySetupKey", oam_fdsk_json);
|
||||
}
|
||||
cJSON* oam_certificate_json = FactoryCertificateToCjson(
|
||||
openknx::BuildOamFactoryCertificatePayload(), false);
|
||||
if (oam_certificate_json != nullptr) {
|
||||
cJSON_AddItemToObject(oam_security_json, "factoryCertificate",
|
||||
oam_certificate_json);
|
||||
}
|
||||
cJSON* oam_credentials_json = IpSecureCredentialStatusToCjson(
|
||||
openknx::LoadOamIpSecureCredentialStatus());
|
||||
if (oam_credentials_json != nullptr) {
|
||||
cJSON_AddItemToObject(oam_security_json, "ipSecureCredentials",
|
||||
oam_credentials_json);
|
||||
}
|
||||
cJSON_AddItemToObject(security_json, "oamRouter", oam_security_json);
|
||||
}
|
||||
cJSON_AddItemToObject(knx_json, "security", security_json);
|
||||
}
|
||||
if (effective_knx.has_value()) {
|
||||
@@ -2314,6 +2363,29 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
effective_knx->ip_interface_individual_address);
|
||||
cJSON_AddNumberToObject(knx_json, "individualAddress",
|
||||
effective_knx->individual_address);
|
||||
cJSON* oam_json = ToCjson(
|
||||
GatewayKnxOamRouterConfigToValue(effective_knx->oam_router));
|
||||
if (oam_json != nullptr) {
|
||||
cJSON_AddItemToObject(knx_json, "oamRouter", oam_json);
|
||||
}
|
||||
cJSON* cloud_remote_json = cJSON_CreateObject();
|
||||
if (cloud_remote_json != nullptr) {
|
||||
const auto& cloud_remote = effective_knx->oam_router.cloud_remote;
|
||||
cJSON_AddBoolToObject(cloud_remote_json, "prepared", true);
|
||||
cJSON_AddBoolToObject(cloud_remote_json, "enabled", cloud_remote.enabled);
|
||||
cJSON_AddStringToObject(cloud_remote_json, "mode", cloud_remote.mode.c_str());
|
||||
cJSON_AddBoolToObject(cloud_remote_json, "requireSecureTunnel",
|
||||
cloud_remote.require_secure_tunnel);
|
||||
cJSON_AddBoolToObject(cloud_remote_json, "udpPunchEnabled",
|
||||
cloud_remote.udp_punch_enabled);
|
||||
cJSON_AddBoolToObject(cloud_remote_json, "relayEndpointConfigured",
|
||||
!cloud_remote.relay_endpoint.empty());
|
||||
cJSON_AddBoolToObject(cloud_remote_json, "mqttTopicPrefixConfigured",
|
||||
!cloud_remote.mqtt_topic_prefix.empty());
|
||||
cJSON_AddBoolToObject(cloud_remote_json, "authTokenRefConfigured",
|
||||
!cloud_remote.auth_token_ref.empty());
|
||||
cJSON_AddItemToObject(knx_json, "cloudKnxRemoteAccess", cloud_remote_json);
|
||||
}
|
||||
cJSON* serial_json = cJSON_CreateObject();
|
||||
if (serial_json != nullptr) {
|
||||
cJSON_AddNumberToObject(serial_json, "uartPort", effective_knx->tp_uart.uart_port);
|
||||
@@ -3220,6 +3292,46 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
}
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (config.oam_router.enabled) {
|
||||
if (config.oam_router.individual_address == 0 ||
|
||||
config.oam_router.individual_address == 0xffff) {
|
||||
if (error_message != nullptr) {
|
||||
*error_message = "OAM KNX/IP router individual address must be a configured address";
|
||||
}
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (config.oam_router.tunnel_address_base == 0 ||
|
||||
config.oam_router.tunnel_address_base == 0xffff ||
|
||||
config.oam_router.tunnel_address_base > 0xffef) {
|
||||
if (error_message != nullptr) {
|
||||
*error_message = "OAM KNX/IP router tunnel address base must leave room for 16 tunnels";
|
||||
}
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (config.oam_router.individual_address == config.individual_address ||
|
||||
config.oam_router.individual_address == config.ip_interface_individual_address ||
|
||||
config.oam_router.tunnel_address_base == config.individual_address ||
|
||||
config.oam_router.tunnel_address_base == config.ip_interface_individual_address) {
|
||||
if (error_message != nullptr) {
|
||||
*error_message = "OAM KNX/IP router addresses must differ from the shared IP interface and KNX-DALI gateway addresses";
|
||||
}
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (config.programming_button_gpio >= 0 &&
|
||||
config.programming_button_gpio == config.oam_router.programming_button_gpio) {
|
||||
if (error_message != nullptr) {
|
||||
*error_message = "OAM and KNX-DALI programming buttons must use separate GPIOs";
|
||||
}
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (config.programming_led_gpio >= 0 &&
|
||||
config.programming_led_gpio == config.oam_router.programming_led_gpio) {
|
||||
if (error_message != nullptr) {
|
||||
*error_message = "OAM and KNX-DALI programming LEDs must use separate GPIOs";
|
||||
}
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
}
|
||||
if (!config.ip_router_enabled || !GatewayKnxConfigUsesTpUart(config)) {
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -3360,6 +3472,7 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
}
|
||||
if (knx_router != nullptr) {
|
||||
knx_router->setConfig(merged_config);
|
||||
knx_router->setOamIpSecureCredentials(openknx::LoadOamIpSecureCredentialMaterial());
|
||||
knx_router->setCommissioningOnly(false);
|
||||
}
|
||||
if (restart_router) {
|
||||
@@ -4615,6 +4728,40 @@ GatewayBridgeHttpResponse GatewayBridgeService::handlePost(
|
||||
}
|
||||
return handleGet("status", gateway_id.value());
|
||||
}
|
||||
if (action == "knx_oam_programming_mode") {
|
||||
cJSON* body_root = body.empty() ? nullptr : cJSON_ParseWithLength(body.data(), body.size());
|
||||
if (body_root == nullptr) {
|
||||
return ErrorResponse(ESP_ERR_INVALID_ARG, "OAM KNX programming mode JSON is required");
|
||||
}
|
||||
const cJSON* enabled_item = cJSON_GetObjectItemCaseSensitive(body_root, "enabled");
|
||||
if (!cJSON_IsBool(enabled_item)) {
|
||||
cJSON_Delete(body_root);
|
||||
return ErrorResponse(ESP_ERR_INVALID_ARG, "boolean enabled field is required");
|
||||
}
|
||||
const bool enabled = cJSON_IsTrue(enabled_item);
|
||||
cJSON_Delete(body_root);
|
||||
|
||||
ChannelRuntime* owner = selectKnxEndpointRuntime();
|
||||
if (owner == nullptr) {
|
||||
return ErrorResponse(ESP_ERR_NOT_FOUND, "no KNX/IP endpoint owner is configured");
|
||||
}
|
||||
|
||||
esp_err_t err = ESP_ERR_INVALID_STATE;
|
||||
std::string detail = "KNX/IP router is unavailable";
|
||||
{
|
||||
LockGuard guard(owner->lock);
|
||||
if (owner->knx_router != nullptr) {
|
||||
err = owner->knx_router->setOamProgrammingMode(enabled);
|
||||
detail = owner->knx_router->lastError();
|
||||
}
|
||||
owner->knx_last_error = err == ESP_OK ? std::string() : detail;
|
||||
}
|
||||
if (err != ESP_OK) {
|
||||
return ErrorResponse(err, detail.empty() ? "failed to change OAM KNX programming mode"
|
||||
: detail.c_str());
|
||||
}
|
||||
return handleGet("status", gateway_id.value());
|
||||
}
|
||||
if (action == "knx_security_read_factory_key") {
|
||||
#if defined(CONFIG_GATEWAY_KNX_SECURITY_DEV_ENDPOINTS) && \
|
||||
defined(CONFIG_GATEWAY_KNX_DATA_SECURE_SUPPORTED)
|
||||
@@ -4807,6 +4954,225 @@ GatewayBridgeHttpResponse GatewayBridgeService::handlePost(
|
||||
#else
|
||||
return ErrorResponse(ESP_ERR_NOT_SUPPORTED,
|
||||
"KNX security development endpoints are disabled");
|
||||
#endif
|
||||
}
|
||||
if (action == "knx_oam_security_read_factory_key") {
|
||||
#if defined(CONFIG_GATEWAY_KNX_SECURITY_DEV_ENDPOINTS)
|
||||
cJSON* body_root = body.empty() ? nullptr : cJSON_ParseWithLength(body.data(), body.size());
|
||||
if (body_root == nullptr) {
|
||||
return ErrorResponse(ESP_ERR_INVALID_ARG, "confirmation JSON is required");
|
||||
}
|
||||
const char* confirm = JsonString(body_root, "confirm");
|
||||
const bool confirmed = confirm != nullptr &&
|
||||
std::string_view(confirm) == "read-oam-factory-setup-key";
|
||||
cJSON_Delete(body_root);
|
||||
if (!confirmed) {
|
||||
return ErrorResponse(ESP_ERR_INVALID_ARG,
|
||||
"OAM factory setup key read confirmation is required");
|
||||
}
|
||||
cJSON* response = cJSON_CreateObject();
|
||||
if (response == nullptr) {
|
||||
return ErrorResponse(ESP_ERR_NO_MEM, "failed to allocate OAM security response");
|
||||
}
|
||||
cJSON* fdsk_json = FactoryFdskInfoToCjson(openknx::LoadOamFactoryFdskInfo(), true);
|
||||
if (fdsk_json != nullptr) {
|
||||
cJSON_AddItemToObject(response, "factorySetupKey", fdsk_json);
|
||||
}
|
||||
return JsonOk(response);
|
||||
#else
|
||||
return ErrorResponse(ESP_ERR_NOT_SUPPORTED,
|
||||
"KNX security development endpoints are disabled");
|
||||
#endif
|
||||
}
|
||||
if (action == "knx_oam_security_generate_factory_key") {
|
||||
#if defined(CONFIG_GATEWAY_KNX_SECURITY_DEV_ENDPOINTS)
|
||||
cJSON* body_root = body.empty() ? nullptr : cJSON_ParseWithLength(body.data(), body.size());
|
||||
if (body_root == nullptr) {
|
||||
return ErrorResponse(ESP_ERR_INVALID_ARG, "confirmation JSON is required");
|
||||
}
|
||||
const char* confirm = JsonString(body_root, "confirm");
|
||||
const bool include_secret = JsonBool(body_root, "includeSecret", false);
|
||||
const bool confirmed = confirm != nullptr &&
|
||||
std::string_view(confirm) == "generate-oam-factory-setup-key";
|
||||
cJSON_Delete(body_root);
|
||||
if (!confirmed) {
|
||||
return ErrorResponse(ESP_ERR_INVALID_ARG,
|
||||
"OAM factory setup key generation confirmation is required");
|
||||
}
|
||||
openknx::FactoryFdskInfo info;
|
||||
if (!openknx::GenerateOamFactoryFdsk(&info)) {
|
||||
return ErrorResponse(ESP_FAIL, "failed to generate OAM factory setup key");
|
||||
}
|
||||
cJSON* response = cJSON_CreateObject();
|
||||
if (response == nullptr) {
|
||||
return ErrorResponse(ESP_ERR_NO_MEM, "failed to allocate OAM security response");
|
||||
}
|
||||
cJSON* fdsk_json = FactoryFdskInfoToCjson(info, include_secret);
|
||||
if (fdsk_json != nullptr) {
|
||||
cJSON_AddItemToObject(response, "factorySetupKey", fdsk_json);
|
||||
}
|
||||
return JsonOk(response);
|
||||
#else
|
||||
return ErrorResponse(ESP_ERR_NOT_SUPPORTED,
|
||||
"KNX security development endpoints are disabled");
|
||||
#endif
|
||||
}
|
||||
if (action == "knx_oam_security_reset_factory_key") {
|
||||
#if defined(CONFIG_GATEWAY_KNX_SECURITY_DEV_ENDPOINTS)
|
||||
cJSON* body_root = body.empty() ? nullptr : cJSON_ParseWithLength(body.data(), body.size());
|
||||
if (body_root == nullptr) {
|
||||
return ErrorResponse(ESP_ERR_INVALID_ARG, "confirmation JSON is required");
|
||||
}
|
||||
const char* confirm = JsonString(body_root, "confirm");
|
||||
const bool confirmed = confirm != nullptr &&
|
||||
std::string_view(confirm) == "reset-oam-factory-setup-key";
|
||||
cJSON_Delete(body_root);
|
||||
if (!confirmed) {
|
||||
return ErrorResponse(ESP_ERR_INVALID_ARG,
|
||||
"OAM factory setup key reset confirmation is required");
|
||||
}
|
||||
openknx::FactoryFdskInfo info;
|
||||
if (!openknx::ResetOamFactoryFdskCache(&info)) {
|
||||
return ErrorResponse(ESP_FAIL, "failed to reload OAM factory setup key");
|
||||
}
|
||||
cJSON* response = cJSON_CreateObject();
|
||||
if (response == nullptr) {
|
||||
return ErrorResponse(ESP_ERR_NO_MEM, "failed to allocate OAM security response");
|
||||
}
|
||||
cJSON* fdsk_json = FactoryFdskInfoToCjson(info, false);
|
||||
if (fdsk_json != nullptr) {
|
||||
cJSON_AddItemToObject(response, "factorySetupKey", fdsk_json);
|
||||
}
|
||||
return JsonOk(response);
|
||||
#else
|
||||
return ErrorResponse(ESP_ERR_NOT_SUPPORTED,
|
||||
"KNX security development endpoints are disabled");
|
||||
#endif
|
||||
}
|
||||
if (action == "knx_oam_security_export_factory_certificate") {
|
||||
#if defined(CONFIG_GATEWAY_KNX_SECURITY_DEV_ENDPOINTS)
|
||||
cJSON* body_root = body.empty() ? nullptr : cJSON_ParseWithLength(body.data(), body.size());
|
||||
if (body_root == nullptr) {
|
||||
return ErrorResponse(ESP_ERR_INVALID_ARG, "confirmation JSON is required");
|
||||
}
|
||||
const char* confirm = JsonString(body_root, "confirm");
|
||||
const bool confirmed = confirm != nullptr &&
|
||||
std::string_view(confirm) == "export-oam-factory-certificate";
|
||||
cJSON_Delete(body_root);
|
||||
if (!confirmed) {
|
||||
return ErrorResponse(ESP_ERR_INVALID_ARG,
|
||||
"OAM factory certificate export confirmation is required");
|
||||
}
|
||||
cJSON* response = cJSON_CreateObject();
|
||||
if (response == nullptr) {
|
||||
return ErrorResponse(ESP_ERR_NO_MEM, "failed to allocate OAM security response");
|
||||
}
|
||||
cJSON* certificate_json = FactoryCertificateToCjson(
|
||||
openknx::BuildOamFactoryCertificatePayload(), true);
|
||||
if (certificate_json != nullptr) {
|
||||
cJSON_AddItemToObject(response, "factoryCertificate", certificate_json);
|
||||
}
|
||||
return JsonOk(response);
|
||||
#else
|
||||
return ErrorResponse(ESP_ERR_NOT_SUPPORTED,
|
||||
"KNX security development endpoints are disabled");
|
||||
#endif
|
||||
}
|
||||
if (action == "knx_oam_security_upload_keyring") {
|
||||
#if defined(CONFIG_GATEWAY_KNX_SECURITY_DEV_ENDPOINTS) && \
|
||||
defined(CONFIG_GATEWAY_KNX_IP_SECURE_SUPPORTED)
|
||||
cJSON* body_root = body.empty() ? nullptr : cJSON_ParseWithLength(body.data(), body.size());
|
||||
if (body_root == nullptr) {
|
||||
return ErrorResponse(ESP_ERR_INVALID_ARG, "OAM keyring JSON is required");
|
||||
}
|
||||
const char* confirm = JsonString(body_root, "confirm");
|
||||
const char* backbone_key = JsonString(body_root, "backboneKeyHex");
|
||||
const char* device_auth_key = JsonString(body_root, "deviceAuthenticationKeyHex");
|
||||
const bool activated = JsonBool(body_root, "activated", true);
|
||||
const bool confirmed = confirm != nullptr &&
|
||||
std::string_view(confirm) == "upload-oam-ip-secure-keyring";
|
||||
std::vector<std::string> tunnel_keys;
|
||||
const cJSON* tunnel_array = cJSON_GetObjectItemCaseSensitive(body_root, "tunnelUserKeysHex");
|
||||
if (tunnel_array == nullptr) {
|
||||
tunnel_array = cJSON_GetObjectItemCaseSensitive(body_root, "tunnelUserKeyHex");
|
||||
}
|
||||
if (cJSON_IsArray(tunnel_array)) {
|
||||
const cJSON* item = nullptr;
|
||||
cJSON_ArrayForEach(item, tunnel_array) {
|
||||
if (cJSON_IsString(item) && item->valuestring != nullptr) {
|
||||
tunnel_keys.emplace_back(item->valuestring);
|
||||
}
|
||||
}
|
||||
} else if (cJSON_IsString(tunnel_array) && tunnel_array->valuestring != nullptr) {
|
||||
tunnel_keys.emplace_back(tunnel_array->valuestring);
|
||||
}
|
||||
if (!confirmed || backbone_key == nullptr) {
|
||||
cJSON_Delete(body_root);
|
||||
return ErrorResponse(ESP_ERR_INVALID_ARG,
|
||||
"OAM keyring upload confirmation and backboneKeyHex are required");
|
||||
}
|
||||
const std::string backbone_key_value(backbone_key);
|
||||
const std::string device_auth_key_value(device_auth_key == nullptr ? "" : device_auth_key);
|
||||
cJSON_Delete(body_root);
|
||||
if (!openknx::WriteOamIpSecureKeyringHex(backbone_key_value, tunnel_keys,
|
||||
device_auth_key_value, activated)) {
|
||||
return ErrorResponse(ESP_ERR_INVALID_ARG,
|
||||
"invalid OAM IP Secure keyring material");
|
||||
}
|
||||
if (runtime->knx_router != nullptr) {
|
||||
runtime->knx_router->setOamIpSecureCredentials(
|
||||
openknx::LoadOamIpSecureCredentialMaterial());
|
||||
}
|
||||
cJSON* response = cJSON_CreateObject();
|
||||
if (response == nullptr) {
|
||||
return ErrorResponse(ESP_ERR_NO_MEM, "failed to allocate OAM keyring response");
|
||||
}
|
||||
cJSON* credentials_json = IpSecureCredentialStatusToCjson(
|
||||
openknx::LoadOamIpSecureCredentialStatus());
|
||||
if (credentials_json != nullptr) {
|
||||
cJSON_AddItemToObject(response, "ipSecureCredentials", credentials_json);
|
||||
}
|
||||
return JsonOk(response);
|
||||
#else
|
||||
return ErrorResponse(ESP_ERR_NOT_SUPPORTED,
|
||||
"KNXnet/IP Secure development endpoints are disabled");
|
||||
#endif
|
||||
}
|
||||
if (action == "knx_oam_security_clear_keyring") {
|
||||
#if defined(CONFIG_GATEWAY_KNX_SECURITY_DEV_ENDPOINTS) && \
|
||||
defined(CONFIG_GATEWAY_KNX_IP_SECURE_SUPPORTED)
|
||||
cJSON* body_root = body.empty() ? nullptr : cJSON_ParseWithLength(body.data(), body.size());
|
||||
if (body_root == nullptr) {
|
||||
return ErrorResponse(ESP_ERR_INVALID_ARG, "confirmation JSON is required");
|
||||
}
|
||||
const char* confirm = JsonString(body_root, "confirm");
|
||||
const bool confirmed = confirm != nullptr &&
|
||||
std::string_view(confirm) == "clear-oam-ip-secure-keyring";
|
||||
cJSON_Delete(body_root);
|
||||
if (!confirmed) {
|
||||
return ErrorResponse(ESP_ERR_INVALID_ARG,
|
||||
"OAM keyring clear confirmation is required");
|
||||
}
|
||||
if (!openknx::ClearOamIpSecureKeyring()) {
|
||||
return ErrorResponse(ESP_FAIL, "failed to clear OAM IP Secure keyring");
|
||||
}
|
||||
if (runtime->knx_router != nullptr) {
|
||||
runtime->knx_router->setOamIpSecureCredentials(
|
||||
openknx::LoadOamIpSecureCredentialMaterial());
|
||||
}
|
||||
cJSON* response = cJSON_CreateObject();
|
||||
if (response == nullptr) {
|
||||
return ErrorResponse(ESP_ERR_NO_MEM, "failed to allocate OAM keyring response");
|
||||
}
|
||||
cJSON* credentials_json = IpSecureCredentialStatusToCjson(
|
||||
openknx::LoadOamIpSecureCredentialStatus());
|
||||
if (credentials_json != nullptr) {
|
||||
cJSON_AddItemToObject(response, "ipSecureCredentials", credentials_json);
|
||||
}
|
||||
return JsonOk(response);
|
||||
#else
|
||||
return ErrorResponse(ESP_ERR_NOT_SUPPORTED,
|
||||
"KNXnet/IP Secure development endpoints are disabled");
|
||||
#endif
|
||||
}
|
||||
if (action == "bacnet_start") {
|
||||
|
||||
Reference in New Issue
Block a user