Chapters

19 CoAP Security: Implementation and Operations

coap
security

19.1 Start With the Decision

A sound CoAP design can still fail when tokens or retries are loose. The team needs clear rules for each resource and error.

19.2 Route Overview

This is part 1 of 2. Continue with CoAP Security: CON and NON Trade-Offs.

19.3 Part Objectives

  • Test coap implementation patterns with a concrete scenario and pass criteria.
  • Validate troubleshooting common issues with a concrete scenario and pass criteria.

19.4 Start With the Situation

The security mode and application fit are clear, but a deployment still fails if tokens, retransmissions, resources, or error responses are handled loosely. The team now turns the design into bounded implementation and operating checks.

19.5 Overview

This route applies implementation patterns, practical resources, response-code tools, debugging records, and common-failure controls.

This is part 2 of 2. Review CoAP Security: Protection and Application Fit when you need the first route.

19.6 Learning Objectives

By the end of this chapter, you will be able to:

  • implement CoAP token, retransmission, and resource patterns
  • prevent open-server reflection and retry failures
  • build an operational debugging record from response evidence

19.7 Chapter Roadmap

  • Start With the Situation
  • Overview
  • CoAP Implementation Patterns
  • Practical CoAP Implementation Resources
  • Checkpoint: Operational Debugging
  • Try It: CoAP Response Code Reference
  • Troubleshooting Common Issues
  • Common Problems and Solutions
  • Common Pitfalls
  • Common Pitfall: CoAP Message Size Exceeds MTU
  • Common Pitfall: Misusing CON vs NON Message Types

19.8 CoAP Implementation Patterns

A CoAP implementation keeps resource intent, message delivery, and observation continuity as distinct parts of the same exchange. GET, POST, PUT, and DELETE describe operations on resources rather than the delivery policy. CON and NON select exchange behavior, while Token and Observe state preserve the application relationship. A performance comparison must therefore account for the combined pattern instead of treating a short header as the entire implementation.

An implementation combines three contracts that are easy to learn separately but must cooperate in code. Inspect Figure 19.1 to connect message delivery, REST operations, and long-lived observation before comparing performance.

Diagram showing CoAP message types (CON, NON, ACK, RST), RESTful methods (GET, POST, PUT, DELETE), and the Observe pattern for push notifications with sequence numbers
Figure 19.1: CoAP Message Types, RESTful Methods, and Observe Pattern Overview

In Figure 19.1, begin with the message types that control delivery behavior, move to GET, POST, PUT, and DELETE as operations on resources, and finish with Observe registration plus ordered notifications. A stack must keep those layers distinct: the method states intent, the message type chooses an exchange policy, and the Token and Observe state preserve application continuity. The performance comparison below then evaluates the cost of that combined pattern.

19.8.1 CoAP vs HTTP vs MQTT: Performance Comparison

Overhead Analysis (20-byte temperature reading):

Read these points as one connected sequence: start with CoAP: 4-byte header, 24 total bytes, 16.7% overhead, UDP transport, ~50 ms latency; then HTTP/1.1: ~200-byte header, 220 total bytes, 90.9% overhead, TCP transport, ~200 ms latency; and finish with MQTT: 2-byte header, 22 total bytes, 9.1% overhead, TCP transport, ~150 ms latency.

  • CoAP: 4-byte header, 24 total bytes, 16.7% overhead, UDP transport, ~50 ms latency
  • HTTP/1.1: ~200-byte header, 220 total bytes, 90.9% overhead, TCP transport, ~200 ms latency
  • MQTT: 2-byte header, 22 total bytes, 9.1% overhead, TCP transport, ~150 ms latency

Energy Consumption (24 hours, readings every 30s):

Read these points as one connected sequence: start with CoAP: 2,880 messages/day, 153 mJ per message, 122.4 mWh total, baseline battery life; then HTTP: 2,880 messages/day, 190 mJ per message, 152.0 mWh total, about 24% less battery life; and finish with MQTT: 2,880 messages/day, 163 mJ per message, 130.4 mWh total, about 6% less battery life.

  • CoAP: 2,880 messages/day, 153 mJ per message, 122.4 mWh total, baseline battery life
  • HTTP: 2,880 messages/day, 190 mJ per message, 152.0 mWh total, about 24% less battery life
  • MQTT: 2,880 messages/day, 163 mJ per message, 130.4 mWh total, about 6% less battery life
Practical CoAP Implementation Resources

Official Libraries and Tools:

Read these points as one connected sequence: start with Python: aiocoap via pip install aiocoap with docs at aiocoap.readthedocs.io; then Arduino/ESP32: CoAP Simple Library via Arduino Library Manager with docs at GitHub: coap-simple; then Node.js: coap via npm install coap with docs at npmjs.com/package/coap; then Java: Eclipse Californium via Maven dependency with docs at eclipse.org/californium; and finish with C/C++: libcoap via system package with docs at libcoap.net.

Testing and Debugging Tools:

Keep one practical point in view: coap-client (libcoap): Command-line CoAP client for testing.

  • coap-client (libcoap): Command-line CoAP client for testing
    # GET request
    coap-client -m get coap://localhost/temperature
    
    # POST with payload
    coap-client -m post coap://localhost/sensor -e "22.5"
    
    # Observe resource
    coap-client -m get -s 60 coap://localhost/temperature

Read these points as one connected sequence: start with Copper (Cu): Firefox/Chrome plugin for CoAP browsing (deprecated but useful for learning); then Wireshark: CoAP dissector included (filter: coap); and finish with nRF Connect CoAP: Mobile app for testing CoAP servers.

  • Copper (Cu): Firefox/Chrome plugin for CoAP browsing (deprecated but useful for learning)
  • Wireshark: CoAP dissector included (filter: coap)
  • nRF Connect CoAP: Mobile app for testing CoAP servers

Example Implementation Patterns:

Pattern 1: Sensor Reading (NON message)

Sensor → Gateway:  CON GET /temperature
Gateway → Sensor:  ACK 2.05 Content
                   Payload: 22.5°C

Overhead: 2 messages, ~50 bytes total
Latency: 1 RTT (~20-50ms on local network)
Reliability: Guaranteed (CON requires ACK)

Pattern 2: Frequent Updates (Observe)

Client → Server:   CON GET /temperature, Observe: 0
Server → Client:   ACK 2.05 Content, Observe: 12
                   Initial value: 22.5°C

[30 seconds later]
Server → Client:   CON 2.05 Content, Observe: 13
                   Updated value: 23.1°C

[30 seconds later]
Server → Client:   CON 2.05 Content, Observe: 14
                   Updated value: 22.9°C

Overhead: 1 subscribe + N notifications
Battery savings: Avoid polling every 30s

Pattern 3: Multicast Discovery

Client → FF02::FD: NON GET /.well-known/core
Device1 → Client:  NON 2.05 Content
                   </temperature>,</humidity>
Device2 → Client:  NON 2.05 Content
                   </pressure>,</light>

Use case: Discover all CoAP devices on local network
Result: List of available resources from all devices

Broker BexCheckpoint: Operational Debugging

You now know:

  • Observe clients must map each 0-8 byte token to the resource being observed, or concurrent notifications can be misattributed.
  • CON retransmission should follow exponential backoff with ACK_TIMEOUT 2.0, ACK_RANDOM_FACTOR 1.5, MAX_RETRANSMIT 4, and about 45 seconds worst-case wait.
  • Response code classes divide the first digit by role: 2.xx success, 4.xx client error, and 5.xx server error.

19.8.2 CoAP Response Code Categories

Read these points as one connected sequence: start with Success (2.xx): 2.01-2.05, meaning the request succeeded. Examples: 2.05 Content for a successful GET and 2.04 Changed for a successful PUT; then Client Error (4.xx): 4.00-4.15, meaning the client made an error. Examples: 4.04 Not Found for a bad URI and 4.01 Unauthorized when authentication is required; and finish with Server Error (5.xx): 5.00-5.05, meaning the server failed. Examples: 5.00 Internal Server Error and 5.03 Service Unavailable.

  • Success (2.xx): 2.01-2.05, meaning the request succeeded. Examples: 2.05 Content for a successful GET and 2.04 Changed for a successful PUT.
  • Client Error (4.xx): 4.00-4.15, meaning the client made an error. Examples: 4.04 Not Found for a bad URI and 4.01 Unauthorized when authentication is required.
  • Server Error (5.xx): 5.00-5.05, meaning the server failed. Examples: 5.00 Internal Server Error and 5.03 Service Unavailable.
Try It: CoAP Response Code Reference

Look up CoAP response codes interactively. Select a category or type a code to see its meaning, HTTP equivalent, and when you would encounter it in practice.

The troubleshooting material below reuses the same pattern: identify the failed layer first, then choose the smallest fix that matches that layer.

19.8.3 Hands-On Learning Resources

Interactive Tutorials:

Read these points as one connected sequence: start with Eclipse Californium CoAP Demo Server: coap://californium.eclipseprojects.io:5683/; and finish with Try: coap-client -m get coap://californium.eclipseprojects.io:5683/.well-known/core.

  1. Eclipse Californium CoAP Demo Server: coap://californium.eclipseprojects.io:5683/
    • Try: coap-client -m get coap://californium.eclipseprojects.io:5683/.well-known/core

Read these points as one connected sequence: start with CoAP.me Public Test Server: coap://coap.me:5683/; and finish with Test GET, POST, PUT, DELETE without setting up your own server.

  1. CoAP.me Public Test Server: coap://coap.me:5683/
    • Test GET, POST, PUT, DELETE without setting up your own server

Code Examples:

Read these points as one connected sequence: start with CoAP Examples Repository; then aiocoap Guided Tour; and finish with ESP32 CoAP Temperature Sensor.

Video Tutorials:

Read these points as one connected sequence: start with Building IoT with CoAP - IoT Developer Conference; then CoAP Deep Dive - Eclipse Foundation; and finish with Constrained Devices and CoAP - IETF Educational Series.

  • Building IoT with CoAP - IoT Developer Conference
  • CoAP Deep Dive - Eclipse Foundation
  • Constrained Devices and CoAP - IETF Educational Series

19.9 Troubleshooting Common Issues

Common Problems and Solutions

A CoAP failure needs a diagnosis at the boundary where the observed behavior stops matching the request. A timeout can involve packet loss, acknowledgement behavior, or an unavailable endpoint rather than a wrong resource path. A failed DTLS handshake instead calls for key or certificate checks before resource handling can succeed. Response codes then distinguish a missing resource from a method that the resource does not support.

Read these points as one connected sequence: start with Request times out: UDP packet loss or no ACK. Increase retransmission timeout, use CON messages instead of NON, and check network quality; then Confirmable message never acknowledged: Server down or wrong endpoint. Verify the server is running and confirm the CoAP URI format coap://host:port/path; then Block transfer fails mid-stream: MTU too large or packet fragmentation. Reduce block size from 1024 bytes to 512 or 256 and check network MTU settings; then Observe notifications stop: Server crashed or network partition. Re-establish the observe relationship and add client-side timeout detection; then Multicast discovery finds no devices: Wrong multicast address or routing. Use FF02::FD for link-local or FF05::FD for site-local and check IPv6 multicast routing; then DTLS handshake fails: PSK mismatch or certificate error. Verify pre-shared keys match exactly and check certificate validity for certificate mode; then Response code 4.04 Not Found: Wrong resource path. Check URI case sensitivity and verify the resource exists on the server; and finish with Response code 4.05 Method Not Allowed: Unsupported method on the resource. Confirm that the resource supports GET, POST, PUT, or DELETE as expected.

  • Request times out: UDP packet loss or no ACK. Increase retransmission timeout, use CON messages instead of NON, and check network quality.
  • Confirmable message never acknowledged: Server down or wrong endpoint. Verify the server is running and confirm the CoAP URI format coap://host:port/path.
  • Block transfer fails mid-stream: MTU too large or packet fragmentation. Reduce block size from 1024 bytes to 512 or 256 and check network MTU settings.
  • Observe notifications stop: Server crashed or network partition. Re-establish the observe relationship and add client-side timeout detection.
  • Multicast discovery finds no devices: Wrong multicast address or routing. Use FF02::FD for link-local or FF05::FD for site-local and check IPv6 multicast routing.
  • DTLS handshake fails: PSK mismatch or certificate error. Verify pre-shared keys match exactly and check certificate validity for certificate mode.
  • Response code 4.04 Not Found: Wrong resource path. Check URI case sensitivity and verify the resource exists on the server.
  • Response code 4.05 Method Not Allowed: Unsupported method on the resource. Confirm that the resource supports GET, POST, PUT, or DELETE as expected.

Debug Checklist:

Read these points as one connected sequence: start with Connection and Discovery Issues:; then Verify server is reachable on UDP port 5683 (or 5684 for DTLS); then Test with ping to confirm basic network connectivity; then Check firewall allows UDP traffic on CoAP port; then For multicast: verify IPv6 multicast is enabled on network interface; and finish with Use CoAP client tool to test server response (e.g., coap-client, libcoap).

  • Connection and Discovery Issues:
    • Verify server is reachable on UDP port 5683 (or 5684 for DTLS)
    • Test with ping to confirm basic network connectivity
    • Check firewall allows UDP traffic on CoAP port
    • For multicast: verify IPv6 multicast is enabled on network interface
    • Use CoAP client tool to test server response (e.g., coap-client, libcoap)

Read these points as one connected sequence: start with Request/Response Problems:; then Confirm CoAP URI format: coap://server-ip:5683/resource/path; then Check message type (CON requires ACK, NON does not); then Verify token in response matches token in request; then Review response code (2.xx success, 4.xx client error, 5.xx server error); and finish with Monitor retransmission attempts (exponential backoff 2s, 4s, 8s, 16s).

  • Request/Response Problems:
    • Confirm CoAP URI format: coap://server-ip:5683/resource/path
    • Check message type (CON requires ACK, NON does not)
    • Verify token in response matches token in request
    • Review response code (2.xx success, 4.xx client error, 5.xx server error)
    • Monitor retransmission attempts (exponential backoff 2s, 4s, 8s, 16s)

Read these points as one connected sequence: start with Observe and Block Transfer Issues:; then For Observe: confirm observe option set in request (observe=0); then Check server sends notifications with observe option and increasing sequence numbers; then For Block transfers: verify block size negotiation (Block1/Block2 options); then Monitor block number sequence (must be consecutive); and finish with Test smaller block sizes if large transfers fail.

  • Observe and Block Transfer Issues:
    • For Observe: confirm observe option set in request (observe=0)
    • Check server sends notifications with observe option and increasing sequence numbers
    • For Block transfers: verify block size negotiation (Block1/Block2 options)
    • Monitor block number sequence (must be consecutive)
    • Test smaller block sizes if large transfers fail

Read these points as one connected sequence: start with Performance and Reliability:; then Check UDP packet loss rate (CoAP degrades above 5% loss); then Monitor network latency (affects retransmission timing); then Verify server can handle request rate (no overload); then Review message deduplication (servers drop duplicate message IDs); and finish with Check for network congestion (CoAP includes congestion control).

  • Performance and Reliability:
    • Check UDP packet loss rate (CoAP degrades above 5% loss)
    • Monitor network latency (affects retransmission timing)
    • Verify server can handle request rate (no overload)
    • Review message deduplication (servers drop duplicate message IDs)
    • Check for network congestion (CoAP includes congestion control)

Read these points as one connected sequence: start with Security (DTLS) Issues:; then Verify DTLS version compatibility (DTLSv1.2 recommended); then Check pre-shared key encoding (must be exact byte match); then For certificates: verify CA chain and server certificate validity; then Monitor DTLS session timeouts and renegotiation; and finish with Test with plain CoAP first, then add DTLS layer.

  • Security (DTLS) Issues:
    • Verify DTLS version compatibility (DTLSv1.2 recommended)
    • Check pre-shared key encoding (must be exact byte match)
    • For certificates: verify CA chain and server certificate validity
    • Monitor DTLS session timeouts and renegotiation
    • Test with plain CoAP first, then add DTLS layer

Common Error Codes:

Read these points as one connected sequence: start with 2.01 Created: Resource successfully created (POST response); then 2.02 Deleted: Resource successfully deleted; then 2.03 Valid: Resource still valid (cache validation); then 2.04 Changed: Resource successfully updated (PUT response); then 2.05 Content: Resource content returned (GET response); then 4.00 Bad Request: Malformed request or invalid options; then 4.01 Unauthorized: Authentication required or failed; then 4.04 Not Found: Resource does not exist; then 4.05 Method Not Allowed: HTTP method not supported on resource; then 5.00 Internal Server Error: Server encountered error processing request; and finish with 5.03 Service Unavailable: Server temporarily unable to handle request.

  • 2.01 Created: Resource successfully created (POST response)
  • 2.02 Deleted: Resource successfully deleted
  • 2.03 Valid: Resource still valid (cache validation)
  • 2.04 Changed: Resource successfully updated (PUT response)
  • 2.05 Content: Resource content returned (GET response)
  • 4.00 Bad Request: Malformed request or invalid options
  • 4.01 Unauthorized: Authentication required or failed
  • 4.04 Not Found: Resource does not exist
  • 4.05 Method Not Allowed: HTTP method not supported on resource
  • 5.00 Internal Server Error: Server encountered error processing request
  • 5.03 Service Unavailable: Server temporarily unable to handle request

Tools for Debugging:

Read these points as one connected sequence: start with libcoap tools: coap-client and coap-server for testing; then Copper (Cu): Firefox/Chrome plugin for CoAP browsing (deprecated but useful); then Wireshark: CoAP dissector for packet analysis (filter: coap); then tcpdump: Capture UDP packets (tcpdump -i any port 5683 -vv); then Eclipse Californium: Java-based CoAP library with extensive logging; and finish with aiocoap: Python library with good debugging output.

  • libcoap tools: coap-client and coap-server for testing
  • Copper (Cu): Firefox/Chrome plugin for CoAP browsing (deprecated but useful)
  • Wireshark: CoAP dissector for packet analysis (filter: coap)
  • tcpdump: Capture UDP packets (tcpdump -i any port 5683 -vv)
  • Eclipse Californium: Java-based CoAP library with extensive logging
  • aiocoap: Python library with good debugging output

19.10 Common Pitfalls

Common Pitfall: CoAP Message Size Exceeds MTU

A CoAP payload that works on local Wi-Fi can fail on a constrained network when its transfer exceeds the available packet budget. Fragment loss can prevent the receiver from reconstructing the complete message even though other fragments arrived. Block-wise transfer supports larger configurations, firmware, and files without treating them as one oversized payload. The acceptance test must use the actual constrained path because Wi-Fi success does not establish that path’s behavior.

The mistake: Sending CoAP payloads larger than the network MTU (typically 1280 bytes for IPv6, often much smaller for constrained networks like 6LoWPAN with 127-byte frames), causing silent packet drops or fragmentation failures.

Symptoms:

  • Large GET responses never arrive at the client
  • PUT/POST requests with substantial payloads fail intermittently
  • Works on local Wi-Fi but fails over 6LoWPAN or constrained networks
  • Wireshark shows fragmented packets but no reassembled response

Why it happens: CoAP runs over UDP, which doesn’t handle fragmentation gracefully:

  • IPv6 minimum MTU: 1280 bytes, but CoAP payload should be much smaller
  • 6LoWPAN frame: 127 bytes maximum, ~80 bytes after headers
  • UDP fragmentation: If any fragment is lost, entire message is lost
  • No automatic retransmission of individual fragments

The fix:

# Use CoAP Block-wise Transfer (RFC 7959) for large payloads
from aiocoap import Message, Context
from aiocoap.numbers.codes import GET

async def get_large_resource(uri):
    context = await Context.create_client_context()

    # Request with Block2 option - library handles chunking
    request = Message(code=GET, uri=uri)
    # Block size: 64 bytes (szx=2), 128 bytes (szx=3), 256 bytes (szx=4)
    # Smaller blocks = more round trips but works on constrained networks

    response = await context.request(request).response
    return response.payload  # Library reassembles blocks automatically
// Embedded C: Check payload size before sending
#define COAP_MAX_PAYLOAD_6LOWPAN  64   // Safe for 802.15.4
#define COAP_MAX_PAYLOAD_UDP      1024 // Safe for most networks

size_t max_payload = is_constrained_network() ?
    COAP_MAX_PAYLOAD_6LOWPAN : COAP_MAX_PAYLOAD_UDP;

if (payload_len > max_payload) {
    // Use Block1 (request) or Block2 (response) transfer
    return coap_send_blockwise(payload, payload_len, max_payload);
}

Prevention: Design payloads to fit in 64-128 bytes for 6LoWPAN networks. Use Block-wise Transfer for firmware updates, large configurations, or file downloads. Test on the actual constrained network, not just Wi-Fi.

Common Pitfall: Misusing CON vs NON Message Types

A battery sensor needs different exchange policies for routine measurements and important control messages. Repeated confirmable exchanges add acknowledgements and retry work that may be unnecessary when the next reading replaces a missed one. Non-confirmable delivery does not supply the failure-detection behavior required by the command path in this example. A hybrid policy ties confirmation to message importance instead of treating telemetry, alarms, and device commands as equivalent.

The mistake: Using Confirmable (CON) messages for all communications because “reliability is important,” leading to excessive battery drain and network congestion, or using Non-Confirmable (NON) for critical commands where delivery must be guaranteed.

Symptoms:

Read these points as one connected sequence: start with Battery-powered sensors lasting weeks instead of years; then Unnecessary retransmission storms when network is lossy; then Commands occasionally not reaching actuators (lights, locks); and finish with High latency due to waiting for ACKs on every message.

  • Battery-powered sensors lasting weeks instead of years
  • Unnecessary retransmission storms when network is lossy
  • Commands occasionally not reaching actuators (lights, locks)
  • High latency due to waiting for ACKs on every message

Why it happens: Developers often misunderstand the trade-offs: Read these points as one connected sequence: start with CON overuse: HTTP background makes developers expect reliability for everything; then NON overuse: Trying to maximize battery life without considering message importance; and finish with No hybrid strategy: Treating all messages the same regardless of criticality.

  • CON overuse: HTTP background makes developers expect reliability for everything
  • NON overuse: Trying to maximize battery life without considering message importance
  • No hybrid strategy: Treating all messages the same regardless of criticality

The fix:

from aiocoap import Message, Context
from aiocoap.numbers.codes import GET, PUT
from aiocoap.numbers.types import CON, NON

# NON for periodic telemetry (loss acceptable, battery critical)
async def send_temperature_reading(temp):
    request = Message(
        code=PUT,
        mtype=NON,  # Fire-and-forget, ~5x better battery life
        uri='coap://server/sensors/temp',
        payload=f'{temp}'.encode()
    )
    await context.request(request).response

# CON for critical commands (must know if it worked)
async def unlock_door():
    request = Message(
        code=PUT,
        mtype=CON,  # Need acknowledgment for security
        uri='coap://door/lock',
        payload=b'unlock'
    )
    try:
        response = await context.request(request).response
        return response.code.is_successful()
    except Exception:
        return False  # Command failed, alert user

# CON for configuration changes (must be applied)
async def update_reporting_interval(seconds):
    request = Message(
        code=PUT,
        mtype=CON,  # Config must be confirmed
        uri='coap://sensor/config/interval',
        payload=str(seconds).encode()
    )
    return await context.request(request).response

Decision matrix: Read these points as one connected sequence: start with Periodic sensor readings: Use NON for routine telemetry; do not use CON; then Alert or alarm notifications: Use CON so the receiver must acknowledge delivery; then Device commands (on/off): Use CON because actuation commands need confirmation; then Configuration updates: Use CON so applied settings are confirmed; then Status queries: Depends on how critical the response is; and finish with Firmware chunks: Use CON with Block2 acknowledgements.

  • Periodic sensor readings: Use NON for routine telemetry; do not use CON.
  • Alert or alarm notifications: Use CON so the receiver must acknowledge delivery.
  • Device commands (on/off): Use CON because actuation commands need confirmation.
  • Configuration updates: Use CON so applied settings are confirmed.
  • Status queries: Depends on how critical the response is.
  • Firmware chunks: Use CON with Block2 acknowledgements.

Prevention: Default to NON for periodic telemetry. Use CON only for commands, configurations, and alerts. Implement a message priority system that selects type based on criticality.

19.11 Continue to the Next Part

Carry this evidence into CoAP Security: CON and NON Trade-Offs, which begins with Try It: CON vs NON Message Type Advisor.