Issue with PSK-Based Dynamic Provisioning on Golioth – DTLS Handshake Fails

Description

Hello everyone,

We’re currently trying to implement dynamic provisioning for our device using PSK credentials on the Golioth platform. The provisioning process is partially successful — the PSK is generated on our backend and sent to both the device and the Golioth dashboard. The key shows up correctly on the Golioth dashboard.

However, the device fails to establish a connection, and we get the following error in the logs:

E (14005) golioth_coap_client_libcoap: Receive timeout
E (14005) golioth_coap_client_libcoap: DTLS handshake failed. Maybe your PSK-ID or PSK is incorrect?
I (14005) golioth_coap_client_libcoap: Ending session
E (14005) golioth_coap_client_libcoap: Received nack reason: COAP_NACK_TLS_FAILED

We suspect the issue may be related to the PSK generation or formatting.

Here is the function we’re currently using on the backend to generate the PSK:
private generatePSK(): string {
return randomBytes(32).toString(‘hex’); // 64 hex characters
}

Questions:
1. Is this the correct way to generate a valid PSK for use with Golioth?
2. Are there any special encoding/formatting requirements (e.g., hex vs base64)?
3. Should the PSK ID follow a specific format or naming convention?
4. What are the correct steps to implement dynamic provisioning using PSK on Golioth?

Any guidance, example implementations, or common pitfalls to avoid would be greatly appreciated.

Thanks in advance!

Expected Behavior

The backend generates a valid PSK using randomBytes(32).toString('hex').
•	The generated PSK and PSK-ID are sent securely to:
•	the Golioth dashboard (where the PSK is registered under the device entry), and
•	the device (which uses these credentials for DTLS authentication).
•	The device attempts to connect to the Golioth cloud over DTLS (CoAP + PSK).
•	The DTLS handshake succeeds, establishing a secure session.
•	The device begins communicating with Golioth and is able to send/receive data.

Actual Behavior

The PSK appears correctly on the Golioth dashboard.
• The device attempts to connect but fails during the DTLS handshake.
• The following logs appear:

E (14005) golioth_coap_client_libcoap: Receive timeout
E (14005) golioth_coap_client_libcoap: DTLS handshake failed. Maybe your PSK-ID or PSK is incorrect?
I (14005) golioth_coap_client_libcoap: Ending session
E (14005) golioth_coap_client_libcoap: Received nack reason: COAP_NACK_TLS_FAILED

No data exchange occurs, and the device is unable to communicate with Golioth.

Environment

Firmware Framework : ESP_IDF v5.4
Backend: Nodejs

Logs and Console Output

E (14005) golioth_coap_client_libcoap: Receive timeout
E (14005) golioth_coap_client_libcoap: DTLS handshake failed. Maybe your PSK-ID or PSK is incorrect?
I (14005) golioth_coap_client_libcoap: Ending session
E (14005) golioth_coap_client_libcoap: Received nack reason: COAP_NACK_TLS_FAILED

Hey @adesuwa,

The PSK must be hex-encoded. By default, it’s 16 bytes, which translates to 32 hex characters. If you want a longer PSK, you’ll need to increase the maximum length accordingly, but from the device output you shared, that is not the issue here.

A quick way to verify that your PSK generation method works correctly is to use the Open API documentation and call this endpoint:

POST /v1/projects/{projectId}/credentials

In the request body, provide the device ID and the PSK, for example:

{
  "deviceId": "your-device-id",
  "type": "PRE_SHARED_KEY",
  "identity": "my-esp32-device",
  "preSharedKey": "a3f7b9c421d8e6547f2e10ab3c9d45fe"
}

The server automatically appends @my-project-id to the identity string to form the PSK-ID, so in this case, the PSK-ID is my-esp32-device@my-project-id.

Once you execute the POST request, the PSK-ID and PSK pair will appear in the console, and you can test them with any of our sample applications.

By the way, are you using the REST API or our CLI to send the PSK-ID and PSK to the console? And how are you delivering the pre-shared keys to the device?

1 Like

Hi Marko, we use REST API

The frontend connects to the sensor through its Access Point and sends the unique device ID to the backend for provisioning. The backend uses this device ID to generate credentials on the Golioth dashboard. Once the credentials are created, it sends the PSK ID and PSK back to the device, which then uses them to establish a secure handshake.

1 Like