go-coap

repository·master·Indexed 19 days ago

https://github.com/plgd-dev/go-coap

A Go implementation of the Constrained Application Protocol (CoAP) designed for IoT machine-to-machine (M2M) applications. It provides servers and clients supporting UDP, TCP, DTLS, and TCP-TLS transports. The library includes a request multiplexer (mux) for mapping URI paths to handlers, support for blockwise transfers, and DTLS authentication via Public Key Infrastructure (PKI) or Pre-Shared Keys (PSK). Requires Go 1.25 or higher.

Tokens
9.7K
Snippets
46
Records
54
Agent score
62%

What's inside go-coap

  1. Generate a server certificate signed by a CA

    master

    To generate a server certificate, create an EC private key, generate a Certificate Signing Request (CSR) using the CA's subject information, and then sign that CSR using the CA's certificate and key.

    # Note: $CERT_SUBJ must be defined (e.g., from the CA generation step)
    openssl ecparam -name secp224r1 -genkey -noout -out server_key.pem
    openssl req -new -sha256 -key server_key.pem -subj $CERT_SUBJ -out server.csr
    openssl x509 -req -in server.csr  -CA root_ca_cert.pem -CAkey root_ca_key.pem -CAcreateserial -out server_cert.pem -days 500 -sha256
  2. Generate a self-signed CA using OpenSSL

    master

    To set up a Public Key Infrastructure (PKI) for DTLS examples, you can first generate a self-signed Certificate Authority (CA). This involves generating an Elliptic Curve (EC) key, extracting the public key, and creating a self-signed certificate using openssl.

    CERT_SUBJ="/C=BR/ST=Parana/L=Curitiba/O=Dis/CN=example.com"
    openssl ecparam -name secp224r1 -genkey -noout -out root_ca_key.pem
    openssl ec -in root_ca_key.pem -pubout -out root_ca_pubkey.pem
    openssl req -new -key root_ca_key.pem -x509 -nodes -days 365 -out root_ca_cert.pem -subj $CERT_SUBJ
  3. Implement a simple CoAP UDP/TCP Client

    master

    To interact with a CoAP server, use the Dial functions provided by the transport-specific packages (e.g., udp.Dial or tcp.Dial). Once a connection is established, you can perform standard CoAP operations like Get, Post, Put, or Delete using a context.Context for timeout control.

    func main() {
        // Dial a UDP server
        co, err := udp.Dial("localhost:5688")
        if err != nil {
            log.Fatalf("Error dialing: %v", err)
        }
        defer co.Close()
    
        ctx, cancel := context.WithTimeout(context.Background(), time.Second)
        defer cancel()
    
        // Perform a GET request
        resp, err := co.Get(ctx, "/a")
        if err != nil {
            log.Fatalf("Cannot get response: %v", err)
        }
        log.Printf("Response: %+v", resp)
    }
  4. Generate a client certificate signed by a CA

    master

    To generate a client certificate, create an EC private key and a CSR with the client's specific subject information (such as an email address), then sign it using the CA's certificate and key.

    CERT_SUBJ="/C=BR/ST=Parana/L=Curitiba/O=Dis/CN=example.com/emailAddress=client1@example.com"
    openssl ecparam -name secp224r1 -genkey -noout -out client_key.pem
    openssl req -new -sha256 -key client_key.pem -subj $CERT_SUBJ -out client.csr
    openssl x509 -req -in client.csr  -CA root_ca_cert.pem -CAkey root_ca_key.pem -CAcreateserial -out client_cert.pem -days 500 -sha256
  5. Implement a simple CoAP UDP/TCP Server

    master

    To create a CoAP server, use a router to map URI paths to handlers. You can also use middleware to intercept requests (e.g., for logging). The server can be started for UDP, TCP, or TLS using coap.ListenAndServe or coap.ListenAndServeTLS.

    Key components:

    • mux.NewRouter(): Creates a new request multiplexer.
    • r.Handle(path, handler): Maps a URI path to a mux.HandlerFunc.
    • r.Use(middleware): Registers middleware to be called for every request.
    • w.SetResponse(code, content_type, reader): Sends a response to the client.
    // Middleware function
    func loggingMiddleware(next mux.Handler) mux.Handler {
        return mux.HandlerFunc(func(w mux.ResponseWriter, r *mux.Message) {
            log.Printf("ClientAddress %v, %v\n", w.Conn().RemoteAddr(), r.String())
            next.ServeCOAP(w, r)
        })
    }
    
    // Handler function
    func handleA(w mux.ResponseWriter, req *mux.Message) {
        err := w.SetResponse(codes.GET, message.TextPlain, bytes.NewReader([]byte("hello world")))
        if err != nil {
            log.Printf("cannot set response: %v", err)
        }
    }
    
    func main() {
        r := mux.NewRouter()
        r.Use(loggingMiddleware)
        r.Handle("/a", mux.HandlerFunc(handleA))
    
        // Start UDP server
        log.Fatal(coap.ListenAndServe("udp", ":5688", r))
    }
  6. Use the DTLS Options-based API (pion/dtls v3)

    master

    The legacy *dtls.Config is deprecated in favor of an immutable options-based API introduced in pion/dtls v3. You should use coapnet.NewDTLSServerOptions for servers and coapdtls.NewDTLSClientOptions for clients to compose configurations using piondtls options (like WithPSK or WithCertificates).

    Both the legacy config and the new options-based API are accepted by the same generic functions like ListenAndServeDTLS and Dial.

    import (
        coap   "github.com/plgd-dev/go-coap/v3"
        coapnet "github.com/plgd-dev/go-coap/v3/net"
        coapdtls "github.com/plgd-dev/go-coap/v3/dtls"
        piondtls "github.com/pion/dtls/v3"
    )
    
    // Server setup with Options
    serverOpts := coapnet.NewDTLSServerOptions(
        piondtls.WithPSK(func(hint []byte) ([]byte, error) {
            return []byte{0xAB, 0xC1, 0x23}, nil
        }),
        piondtls.WithCipherSuites(piondtls.TLS_PSK_WITH_AES_128_CCM_8),
    )
    log.Fatal(coap.ListenAndServeDTLS("udp", ":5688", serverOpts, r))
    
    // Client setup with Options
    clientOpts := coapdtls.NewDTLSClientOptions(
        piondtls.WithPSK(func(hint []byte) ([]byte, error) {
            return []byte{0xAB, 0xC1, 0x23}, nil
        }),
        piondtls.WithCipherSuites(piondtls.TLS_PSK_WITH_AES_128_CCM_8),
    )
    co, err := coapdtls.Dial("localhost:5688", clientOpts)
  7. Configure DTLS client options

    master

    The dtls package uses DTLSClientOptions to configure secure connections. While the specific constructor NewDTLSClientOptions is not defined in this file, Dial accepts it as the preferred configuration type.

    Common configuration behaviors managed by the client include:

    • Blockwise Transfer: Can be enabled via configuration to handle large payloads.
    • Inactivity Monitoring: Uses an InactivityMonitor to manage connection lifecycles.
    • Error Handling: A custom Errors function can be provided to intercept and process errors.
  8. Configure the UDP server using Options

    master

    The Option interface allows you to customize the server's behavior during initialization. Any type implementing UDPServerApply(cfg *Config) can be passed to New() to modify the Config object used by the server.

    type MyOption struct{}
    
    func (o MyOption) UDPServerApply(cfg *server.Config) {
        // Modify cfg here
    }
    
    // Usage
    srv := server.New(MyOption{})
  9. Configure the UDP client using Options

    master

    The udp package uses the Option interface to configure client behavior. An Option is any type that implements the UDPClientApply(cfg *client.Config) UDPClientApply method.

    Common configuration areas include:

    • Credentials and security settings
    • Keepalive and inactivity parameters
    • Blockwise transfer settings
    • Error handling callbacks
  10. Use DTLS with Connection ID (CID)

    master

    The CID example demonstrates how to use DTLS with Connection ID (CID). This allows the client and server to identify the connection via a unique ID, enabling the connection to be resumed even if the client's network address changes.

    # run the server
    go run examples/dtls/cid/server/main.go
    
    # run the client
    go run examples/dtls/cid/client/main.go
  11. Use DTLS with Pre-Shared Key (PSK)

    master

    The PSK example demonstrates how to implement DTLS authentication using a Pre-Shared Key. In this mode, the client and server use a shared secret key to authenticate each other instead of certificates.

    # run the server
    go run examples/dtls/psk/server/main.go
    
    # run the client
    go run examples/dtls/psk/client/main.go