Device Bootstrapping and Configuration
Device bootstrapping in KNX IoT establishes secure communication between a Management Client (MaC) and KNX IoT devices through device individualization and configuration download. All communication uses OSCORE-secured CoAP for authentication and data protection.
Device bootstrapping consists of two independent phases: authenticating the device and installing security credentials (Part 1), and downloading device configuration data and security tokens (Part 2). These phases can run independently or in combination to complete the full initialization of a KNX IoT device into a KNX installation.
Overview
| Phase | Description | Endpoints |
|---|---|---|
| Configure Individual Address (Part 1) | Authenticate with the device (PASE: SPAKE2+ handshake, tool key install, PASE access token delete), assign the individual address, and disable programming mode. | /.well-known/knx/spake, /auth/at, /.well-known/knx/ia, /dev/pm |
| Configuration Download (Part 2) | Factory-reset (preserving IA), transition through the load state machine, and download tables, parameters, and security tokens. | /.well-known/knx, /a/lsm, /fp/r,/fp/p, /fp/g, /p/*, /auth/at |
The MaC (Management Client) is the commissioning tool that orchestrates both phases. Both phases use OSCORE-secured CoAP. The shared secret derives from the PASE handshake in Part 1, then the commissioning tool replaces it with a long-lived tool key for Part 2.
PASE (Password Authenticated Session Establishment) is the complete session setup protocol. It contains SPAKE2+ (RFC 9383) as its cryptographic core for mutual authentication and key agreement, then continues with tool key installation and temporary key cleanup:
| Protocol | Scope |
|---|---|
| SPAKE2+ | 3-step cryptographic key exchange on /.well-known/knx/spake that derives K_shared |
| PASE | SPAKE2+ handshake + install tool key + delete PASE access token on /auth/at |
Bootstrapping w/ TLS and LDevID Enrollment
Devices that support X.509 certificates can also use TLS as an extension to the password-based SPAKE2+ flow. This document describes only the SPAKE2+-based PASE variant. See Device Bootstrapping w/ TLS for the full TLS onboarding and LDevID enrollment workflow.
Configure Individual Address (Part 1)
Part 1 authenticates the device using PASE (Password Authenticated Session Establishment) and installs a persistent tool key for secure communication. It assigns the device a unique Individual Address (IA) and disables programming mode.
Before Part 1 begins, the installer may activate programming mode on the device, typically by pressing a physical programming button. The device then advertises its programming mode state (for example, via mDNS), signaling the MaC that the device is ready for configuration. Battery-powered or sleepy devices, for example, wake up or switch to a faster wake-up cycle for a limited time after an installer presses the programming button.
MaC Client KNX IoT Device
| |
|--- [NON] GET /.well-known/core ------->| Discovery (multicast, ?ia or ?sn)
|<-- [NON] 2.05 Content -----------------|
| |
|--- [CON] GET /a/lsm ------------------>| Connection and auth check
|<-- [ACK] 4.01 Unauthorized ------------|
| |
| ┌-------- PASE --------┐ |
| │ │ |
| │ ┌-- SPAKE2+ --┐ │ |
|--- [CON] POST /.well-known/knx/spake ->| Step 1: Parameter Request
|<-- [CON] 2.04 Changed -----------------| Step 1: Parameter Response
|--- [CON] POST /.well-known/knx/spake ->| Step 2: Credential Request (shareP)
|<-- [CON] 2.04 Changed -----------------| Step 2: Credential Response (shareV, confirmV)
|--- [CON] POST /.well-known/knx/spake ->| Step 3: Verification (confirmP)
|<-- [CON] 2.04 Changed -----------------| Step 3: Verification Response
| │ └-------------┘ │ |
| │ │ |
|--- [CON] POST /auth/at --------------->| Install access token w/ tool key (OSCORE)
|<-- [ACK] 2.01 Created -----------------|
| │ │ |
|--- [CON] DELETE /auth/at/<kid> ------->| Delete PASE access token w/ temporary key (OSCORE)
|<-- [ACK] 2.02 Deleted -----------------|
| │ │ |
| └----------------------┘ |
| |
|--- [CON] POST /.well-known/knx/ia ---->| Write Individual Address + IID
|<-- [ACK] 2.04 Changed -----------------|
| |
|--- [CON] PUT /dev/pm ----------------->| Disable programming mode
|<-- [ACK] 2.04 Changed -----------------|
| |
Device Individualization Steps
1. Device Discovery
The MaC sends a multicast [NON] GET /.well-known/core to locate the
device. The two queries are alternatives, the MaC uses whichever
matches its available information:
- Query by IA (
?ia=<ia>&iid=<iid>): checks whether a device with this individual address already exists in the installation. Useful during re-commissioning. Devices also use this query at runtime to resolve IPv6 addresses, for example, for Direct Unicast Message Exchange. - Query by SN (
?sn=<serial>): locates an unconfigured device by serial number (from barcode/QR). The device responds with its current IA (for example,15.15.255if unconfigured).
2. Connection and Authentication Check
A [CON] GET /a/lsm establishes the UDP/IPv6 transport path and
tests the current authentication state. An unconfigured device responds
with 4.01 Unauthorized because no OSCORE context exists yet, PASE
establishes it in the next step. During re-commissioning, a 2.05
response confirms that the existing credentials are still valid.
3. PASE (Password Authenticated Session Establishment)
PASE establishes a temporary OSCORE session between the MaC and the device. It consists of three sub-steps: the SPAKE2+ cryptographic handshake, tool key installation, and temporary key cleanup.
3a. SPAKE2+ Handshake
The 3-step SPAKE2+ key exchange on /.well-known/knx/spake derives
K_shared. See
SPAKE2+ Protocol Detail below for the full
cryptographic specification.
SPAKE2+ Visualizer
The SPAKE2+ Visualizer provides a step-by-step guide to understand and verify the SPAKE2+ handshake.
3b. Install Tool Key
After the SPAKE2+ handshake establishes a temporary OSCORE context, the
client installs a persistent tool key via POST /auth/at.
Access Token Format (CBOR):
[{
0: kid, // key identifier (hex string of key_identifier bytes)
8: { // cnf (confirmation)
4: { // OSCORE_Input_Material
0: sender_id, // bytes (key_identifier from security CSV)
2: master_secret // 16 bytes
}
},
9: scope, // ["if.c", "if.p", "if.d", "if.sec", "if.swu", "if.g.s"]
38: 2, // profile = OSCORE
}]
Access Token Format and Scope
See Device Access Control for the full access token CBOR structure, credential types (OSCORE and X.509), and scope definitions.
3c. Delete PASE Access Token
The commissioning tool removes the temporary PASE access token
with the PASE key: DELETE /auth/at/<kid> (for example,
/auth/at/+64STfs).
After this step, the PASE session is complete. The device now uses only the persistent tool key for all following OSCORE communication.
4. Write Individual Address
POST /.well-known/knx/ia with CBOR payload:
{12: ia, 26: iid} // ia = individual address (int), iid = installation ID (int)
5. Disable Programming Mode
PUT /dev/pm with CBOR payload {1: false}.
This clears the programming mode flag that the installer activated before Part 1. The device stops advertising its readiness for configuration (for example, via mDNS).
Configuration Download (Part 2)
Part 2 downloads the actual device configuration, function blocks, and security tokens after the MaC has established a secure tool key connection. This phase includes a factory reset to clear any previous configuration, then uses the Load State Machine to orchestrate the sequential download of tables, parameters, and cryptographic material.
MaC Client KNX IoT Device
| |
| (Part 1 completed—tool key active) |
| |
|--- GET /dev/sna, /dev/da, /dev/mid --->| Read device info
|<-- 2.05 Content -----------------------|
| |
|--- POST /.well-known/knx ------------->| Reset(FactoryResetWithoutIA)
|<-- 2.04 Changed (Success, 5 s) --------|
| |
| ~~~ wait for device reboot ~~~ |
| |
|--- POST /a/lsm ----------------------->| Unload
|<-- 2.04 Changed (Unloaded) ------------|
| |
|--- POST /a/lsm ----------------------->| StartLoading
|<-- 2.04 Changed (Loading) -------------|
| |
|--- POST /fp/g, /fp/r, /fp/p, /auth/at >| Download Function Point Tables and access tokens
|<-- 2.01 Created -----------------------| (repeated per entry)
| |
|--- PUT /p/* (parameters) ------------->| Configure parameters
|<-- 2.04 Changed -----------------------| (repeated per param)
| |
|--- PUT ApplicationProgramId ---------->| Lock application identity
|<-- 2.04 Changed -----------------------|
| |
|--- POST /a/lsm ---------------------->| LoadComplete
|<-- 2.04 Changed (Loaded) --------------|
| |
|--- GET /a/lsm ------------------------>| Verify loaded
|<-- 2.05 Content (Loaded) --------------|
| |
|--- GET Fingerprint ------------------->| Read config fingerprint
|<-- 2.05 Content (config hash) ---------|
| |
Configuration Download Steps
After Part 1 establishes a secure tool key connection, the MaC downloads device configuration through several steps. These steps include reading device properties, resetting configuration, managing the load state machine, and installing tables and parameters.
1. Read Device Information
After the Configure Individual Address phase, the client reads device properties to confirm identity:
| Property | Endpoint | Example Value |
|---|---|---|
| Subnet Address | GET /dev/sna |
2.1 |
| Device Address | GET /dev/da |
4 |
| Manufacturer ID | GET /dev/mid |
0x00fd |
| Hardware Type | GET /dev/hwt |
0x0001000200030004 |
2. Factory Reset (Without IA)
POST /.well-known/knx with CBOR payload:
{2: "reset", 1: 2} // erase_code = 2 → FactoryResetWithoutIA
The device responds with 2.04 Changed and a reset delay (for example, 5 s).
The individual address remains intact. All configuration data erases during reboot.
The device responds with IA=15.15.255 after reboot, then
resumes with its assigned IA once the OSCORE context is re-established.
3. Load State Machine
The load state machine governs configuration download:
Unloaded ──→ StartLoading ──→ Loading ──→ LoadComplete ──→ Loaded
| Transition | Method | Payload | Response |
|---|---|---|---|
| → Unloaded | POST /a/lsm |
Unload |
Unloaded |
| → Loading | POST /a/lsm |
StartLoading |
Loading |
| → Loaded | POST /a/lsm |
LoadComplete |
Loaded |
4. Download Tables
While in Loading state, the client posts table entries:
Group Object and Function Point Tables
The section Group Communication describes how Group Objects map data points to group addresses. It also explains how Function Point Tables link them for device-to-device communication. For the CBOR key definitions and examples of each table, see Device Function Point Tables.
Group Object Table (POST /fp/g)
Maps application data points to group addresses. Each entry contains
a resource path, group address, and resource type (for example, rt for
read/transmit, t for transmit only).
Recipient Table (POST /fp/r)
Defines the group communication target the device sends to (who receives messages). Entries can configure unicast (point-to-point) or multicast delivery, specifying the destination address and the group addresses covered.
Publisher Table (POST /fp/p)
Defines the group communication sources the device receives from (who publishes messages). As with the Recipient Table, entries can be unicast or multicast.
Access Token Table (POST /auth/at)
The MaC configures one access token per security context: one for each multicast group, one for each unicast peer, and a separate tool key for MaC access. See Device Access Control for the full access token structure and credential types.
5. Configure Parameters
Application-specific parameters use PUT /p/*:
The device stores each parameter as a PUT to the
corresponding /p/<path> resource with a CBOR-encoded value.
6. Application Program Identity
PUT ApplicationProgramId locks the application identity (manufacturer + program version).
7. Complete Loading and Verify
POST /a/lsm = LoadComplete→ device transitions toLoaded.GET /a/lsm→ confirmsLoadedstate.GET Fingerprint→ returns a configuration hash.
SPAKE2+ Protocol Detail
SPAKE2+ (RFC 9383) is a Password Authenticated Key Exchange (PAKE) that enables two parties who share a password to establish a mutual authenticated shared secret. KNX IoT uses SPAKE2+ as the cryptographic core of PASE (Password Authenticated Session Establishment). SPAKE2+ handles only the key agreement—PASE wraps it with tool key installation and temporary key cleanup (see step 3).
Cryptographic Parameters
| Parameter | Value |
|---|---|
| Curve | NIST P-256 (secp256r1) |
| Hash | SHA-256 |
| KDF | HKDF-SHA256 |
| Password KDF | PBKDF2-HMAC-SHA256 |
| PBKDF2 output | 80 bytes → w0s \|\| w1s (40 + 40) |
| Cofactor (h) | 1 (identity—no cofactor multiplication needed) |
| MAC | HMAC-SHA-256 |
| Shared key length | 16 bytes (AES-CCM-16-64-128 master secret) |
Curve Constants
Field prime (p):
0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF
Curve parameters:
a = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC
b = 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B
Generator (G):
Gx = 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296
Gy = 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5
Order (N):
0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551
M and N Constants (RFC 9383)
These are nothing-up-the-sleeve points used for blinding:
M = 04 886E2F97ACE46E55BA9DD7242579F2993B64E16EF3DCAB95AFD497333D8FA12F
5FF355163E43CE224E0B0E65FF02AC8E5C7BE09419C785E0CA547D55A12E2D20
N = 04 D8BBD6C639C62937B04D997F38C3770719C629D7014D49A24B4F98BAA1292B49
07D60AA6BFADE45008A636337F5168C64D9BD36034808CD564490B1E656EDBE7
KNX IoT Protocol Constants
| Constant | Value | Description |
|---|---|---|
| TT_CONTEXT | "knxpase" (7 bytes) |
Transcript context string |
| HKDF_INFO_CONFIRM | "ConfirmationKeys" |
HKDF info for confirmation keys (64 bytes output) |
| HKDF_INFO_SHARED | "SharedKey" |
HKDF info for shared key (16 bytes output) |
| HKDF_SALT | empty (0 bytes) | Salt for both HKDF derivations |
| ID_PROVER | empty (0 bytes) | Prover identity (KNX IoT spec) |
| ID_VERIFIER | empty (0 bytes) | Verifier identity (KNX IoT spec) |
Complete Protocol Sequence
Phase 0: Offline Registration
The device stores configuration information during manufacturing, which remains unchanged during protocol execution.
Device (Verifier) stores during manufacturing:
w0 = w0s mod N
L = w1 * G (compressed point)
salt, iterations (PBKDF2 params)
Password transferred to client (QR code/barcode)
Phase 1: Parameter Exchange
The MaC and device exchange random nonces and PBKDF2 parameters to set up key derivation.
MaC KNX IoT Device
| |
|--- POST /.well-known/knx/spake ----------->| Step 1
| CBOR: {0: kid, 15: rnd_client} | (send kid and random)
| |
|<-- 2.04 Changed ---------------------------| Step 1
| CBOR: {15: rnd_server, | (recv server random
| 12: {5: salt, 16: iterations}} | and PBKDF2 params)
| |
Phase 2: Key Derivation (Both Sides, Offline)
Both parties independently compute the same shared keys using the exchanged parameters.
Client side:
inputHash = len(pw)||pw || len(idProver) || len(idVerifier)
w0s||w1s = PBKDF2-HMAC-SHA256(inputHash, salt, iterations, 80)
w0 = w0s mod N
w1 = w1s mod N
Generate random x: x ∈ [1, N-1]
Compute: ShareP = x·G + w0·M (65 bytes, uncompressed)
Device side:
Already has w0 and L from manufacturing
Generate random y: y ∈ [1, N-1]
Compute: ShareV = y·G + w0·N (65 bytes, uncompressed)
Phase 3: Credential Exchange
The MaC and device exchange blinded shares and verify mutual knowledge of the password through MAC values.
Message exchange:
MaC KNX IoT Device
| |
|--- POST /.well-known/knx/spake ----------->| Step 2
| CBOR: {10: shareP} | (send ShareP)
| |
|<-- 2.04 Changed ---------------------------| Step 2
| CBOR: {11: shareV, 13: confirmV} | (recv ShareV+confirmV)
| |
|--- POST /.well-known/knx/spake ----------->| Step 3
| CBOR: {14: confirmP} | (send confirmP)
| |
|<-- 2.04 Changed ---------------------------| Step 3
| (verification complete) | (auth confirmed)
| |
Device computation (after receiving ShareP):
Z = y · (ShareP − w0·M)
V = y · L
K_main = SHA-256(TT)
K_confirmP||K_confirmV = HKDF(K_main, "ConfirmationKeys", 64)
K_shared = HKDF(K_main, "SharedKey", 16)
confirmV = HMAC-SHA-256(K_confirmV, ShareP)
Client computation (after receiving ShareV):
Z = x · (ShareV − w0·N)
V = w1 · (ShareV − w0·N)
K_main = SHA-256(TT)
K_confirmP||K_confirmV = HKDF(K_main, "ConfirmationKeys", 64)
K_shared = HKDF(K_main, "SharedKey", 16)
Verify: HMAC(K_confirmV, ShareP) == received confirmV
confirmP = HMAC-SHA-256(K_confirmP, ShareV)
Device verification (after receiving confirmP):
Verify: HMAC(K_confirmP, ShareV) == received confirmP
Phase 4: Temporary OSCORE Context Established
Both parties now share the same K_shared. This concludes the SPAKE2+
handshake. The resulting temporary OSCORE context enables the remaining
PASE steps (tool key installation and PASE access token deletion).
Both MaC and KNX IoT Device now share K_shared (16 bytes)
Temporary OSCORE access token established with:
kid = provided key identifier
master_secret = K_shared (16 bytes)
sender_id = kid as UTF-8 bytes
recipient_id = empty (zero length)
context_id = omit entirely (do NOT set to empty)
scope = ["if.sec"] (security interface only)
algorithm = AES-CCM-16-64-128
OSCORE Context ID
Setting context_id to an empty byte string changes HKDF key derivation (CBOR 0x40 vs 0xf6) and adds a spurious Key ID flag to the OSCORE option.
PBKDF2 Input Construction
Non-Standard PBKDF2 Input Format
The PBKDF2 input is not the raw password. KNX IoT prepends length-prefixed identity fields to the password before passing it to PBKDF2. This differs from standard PBKDF2 usage and is critical for correct implementation.
input = len(pw) || pw || len(idProver) || len(idVerifier)
- All lengths are 8-byte little-endian unsigned integers.
pw= UTF-8 encoding of the password stringidProver= empty (length = 0)idVerifier= empty (length = 0)
Pseudocode:
pbkdf2_input = LE64(len(pw)) || pw || LE64(0) || LE64(0)
output = PBKDF2-HMAC-SHA256(pbkdf2_input, salt, iterations, dkLen=80)
w0s = output[0..39] // first 40 bytes
w1s = output[40..79] // last 40 bytes
Scalar Derivation
w0 = int(w0s, big-endian) mod N
w1 = int(w1s, big-endian) mod N
L = w1 · G (stored on device, compressed point, 33 bytes)
Share Computation
Client: ShareP = x·G + w0·M (uncompressed, 65 bytes, prefix 0x04)
Server: ShareV = y·G + w0·N (uncompressed, 65 bytes, prefix 0x04)
Where x and y are random scalars in [1, N-1].
Shared Secret Computation
Server side:
Z = y · (ShareP − w0·M)
V = y · L
Client side:
Z = x · (ShareV − w0·N)
V = w1 · (ShareV − w0·N)
Both sides compute the same Z and V values. This core SPAKE2+ property ensures that only someone who knows the password can remove the correct underlying point from the blinded share.
Transcript Hash (TT → K_main)
The transcript hash binds all protocol values into a single SHA-256 digest:
TT = len(Context)||Context
|| len(idProver)||idProver
|| len(idVerifier)||idVerifier
|| len(M)||M
|| len(N)||N
|| len(ShareP)||ShareP
|| len(ShareV)||ShareV
|| len(Z)||Z
|| len(V)||V
|| len(w0)||w0
K_main = SHA-256(TT)
Each entry uses an 8-byte little-endian
unsigned integer as a prefix. The || operator denotes concatenation.
Context="knxpase"(7 bytes)idProver,idVerifier= empty (0 bytes each)M,N= uncompressed point bytes (65 bytes each)ShareP,ShareV,Z,V= uncompressed point bytes (65 bytes each)w0= minimal big-endian byte encoding (no leading zero padding)
Key Derivation from K_main
K_confirmP || K_confirmV = HKDF-SHA256(
salt = "",
ikm = K_main,
info = "ConfirmationKeys",
L = 64 bytes
)
K_confirmP = first 32 bytes
K_confirmV = last 32 bytes
K_shared = HKDF-SHA256(
salt = "",
ikm = K_main,
info = "SharedKey",
L = 16 bytes
)
Confirmation Values
confirmV = HMAC-SHA-256(K_confirmV, ShareP) → server sends to client
confirmP = HMAC-SHA-256(K_confirmP, ShareV) → client sends to server
The client verifies confirmV before sending confirmP. This
ensures the server knows the password before the client reveals its
own confirmation.
PASE CBOR Key Mapping
| CBOR Key | Field | Direction |
|---|---|---|
| 0 | kid (key identifier) | Request (step 1) |
| 5 | salt (within pbkdf2) | Response (step 1) |
| 10 | shareP | Request (step 2) |
| 11 | shareV | Response (step 2) |
| 12 | pbkdf2 params {5: salt, 16: iterations} |
Response (step 1) |
| 13 | confirmV | Response (step 2) |
| 14 | confirmP | Request (step 3) |
| 15 | rnd (32-byte nonce) | Both (step 1) |
| 16 | iterations (within pbkdf2) | Response (step 1) |
Test Vectors
These vectors can verify any SPAKE2+ implementation.
PBKDF2 / Scalar Derivation
password: "LJFYEARVK56STM70"
iterations: 1000
salt: "aabbccddaabbccddaabbccddaabbccddaabbccddaabbccddaabbccddaabbccdd"
w0s: "937aa8dbd5d1db6b26a2304c68537e68a5b96570d0ae10c414af5eb333cecd22ae40338ac336fac6"
w1s: "88390042af5c552316569419c985c42218a189d494b38ff9e2796f8b4b7b0c5488b1b80dcc601d9d"
w0: "fc740bb5ff06fa22cc60e9add391cde0b38baddbc599ecead03366bd53d102a0"
w1: "c5b2e93b91f06ebd3c55c54c2162b824cbe117de2ea9a852636004672eb97fa8"
L: "0254fac7ad879c01d15034131d12c01c66cd802274c9301f5b6df75c5d72a5a65f"
Share Computation Vectors
x: "18d51a5e1b8cad2d95a4c3f9e7b3a0f2c6d8e4f1a3b5c7d9e0f2a4b6c8d0e2f4"
y: "2a3b4c5d6e7f8091a2b3c4d5e6f7081920314253647586970a1b2c3d4e5f6071"
ShareP: "04c797ee127bddec5d030c2cbfee1b9e1fca371e841aa8f56c8083048f6e6ae45b
31b8d8063f6295cad1342c202c755b937b75d69c041e64557317b7e2bcbe4fe2"
ShareV: "04322b44a7b3bcf1d424948c273c11f5cfc882e3cfac1a333602710d7c42feaf5b
a8a32ec10d5fe3541e3a346122f675e02714f99331a2a092137d3cf68de02d9c"
K_main Derivation
These vectors use independent inputs (different w0, ShareP, ShareV) from the preceding sections and verify the transcript hash and key derivation in isolation.
Z: "045a7523840b1eaefb99e25a21e9145c5cc7702029a373d7984b6cca8bfbc0f53c
da0295af096bf1cf8bb6f6ed1b32c650ddf796f6ff4a889c50b3b20f2327aa76"
V: "048754934d34b2b2e944ef9868b7b588412ee6c14e3dfa61510ccbe4efa19fa18a
491c6d6537018c1ef07178b7eca2b7157528e12b23f9eccf0d39b7926399b6b0"
ShareP: "047abd3bafbb9a613c8c6bd03c5f1861d2189366b447a5a80ce9ce74bfa0d25ddd
a63e55d830574796009de2c3caac284af372a214e2fa99ef26d5323852257d82"
ShareV: "044304e7f309e840a23ddebe74bc169618291a83fa7100da87c78fe91dc984f156
94867a5baa620e568f95dfd30b652fa48272ba1855a2c71d932a3e42e382cd99"
w0: "20a4c3dee4ad09025425114b70b794784957be31d8e32204d53d9ef447aec0fa"
K_main: "da4a6618f5c52b7e9b92de0fdd63f1473b23454ad4c72849ba93f61bcd6db5a0"
Full Protocol Result
shared_secret: "a4432d14f99d9ccef8cf7965e9cbb920" # 16 bytes = OSCORE master secret