Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,16 @@ When running, please provide following format:
| d | data to be sent in POST or PUT | "" |
| cf | content format | 50 (JSON format) |

The cli offers DTLS support with PKI certificates.Export the path to the certificate folder with the name `CERT_PATH` before build to enable the DTLS configuration.

```bash
export CERT_PATH="../certs"
make
```
## Examples:

```bash
coap-cli get channels/0bb5ba61-a66e-4972-bab6-26f19962678f/messages/subtopic -auth 1e1017e6-dee7-45b4-8a13-00e6afeb66eb -o
coap-cli get channels/0bb5ba61-a66e-4972-bab6-26f19962678f/messages/subtopic -auth 1e1017e6-dee7-45b4-8a13-00e6afeb66eb -o -tls
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add seperate example for dtls

```
```bash
coap-cli post channels/0bb5ba61-a66e-4972-bab6-26f19962678f/messages/subtopic -auth 1e1017e6-dee7-45b4-8a13-00e6afeb66eb -d "hello world"
Expand Down
3 changes: 3 additions & 0 deletions certs/ca.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-----BEGIN CERTIFICATE-----
^
-----END CERTIFICATE-----
3 changes: 3 additions & 0 deletions certs/client.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-----BEGIN CERTIFICATE-----
^
-----END CERTIFICATE-----
3 changes: 3 additions & 0 deletions certs/client.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
^
-----END PRIVATE KEY-----
135 changes: 135 additions & 0 deletions certutil/utility.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package certutil

import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
"log"
"os"
"strings"

piondtls "github.com/pion/dtls/v2"
)

// Create pion dtls config from certificates.
func CreateClientConfig(ctx context.Context, certPath string) (*piondtls.Config, error) {
clientKeyBytes, err := os.ReadFile(certPath + "/client.key")
if err != nil {
log.Fatal(err)
}

clientCrtBytes, err := os.ReadFile(certPath + "/client.crt")
if err != nil {
log.Fatal(err)
}

caBytes, err := os.ReadFile(certPath + "/ca.crt")
if err != nil {
log.Fatal(err)
}

certificate, err := LoadKeyAndCertificate(clientKeyBytes, clientCrtBytes)
if err != nil {
return nil, err
}
// cert pool
certPool, err := LoadCertPool(caBytes)
if err != nil {
return nil, err
}

return &piondtls.Config{
Certificates: []tls.Certificate{*certificate},
ExtendedMasterSecret: piondtls.RequireExtendedMasterSecret,
RootCAs: certPool,
InsecureSkipVerify: true,
}, nil
}

func LoadCertificate(certBytes []byte) (*tls.Certificate, error) {
var certificate tls.Certificate

for {
block, rest := pem.Decode(certBytes)
if block == nil {
break
}

if block.Type != "CERTIFICATE" {
return nil, errors.New("block is not a certificate, unable to load certificates")
}

certificate.Certificate = append(certificate.Certificate, block.Bytes)
certBytes = rest
}

if len(certificate.Certificate) == 0 {
return nil, errors.New("no certificate found, unable to load certificates")
}

return &certificate, nil
}

func LoadKey(keyBytes []byte) (crypto.PrivateKey, error) {
block, _ := pem.Decode(keyBytes)
if block == nil || !strings.HasSuffix(block.Type, "PRIVATE KEY") {
return nil, errors.New("block is not a private key, unable to load key")
}

if key, err := x509.ParsePKCS1PrivateKey(block.Bytes); err == nil {
return key, nil
}

if key, err := x509.ParsePKCS8PrivateKey(block.Bytes); err == nil {
switch key := key.(type) {
case *rsa.PrivateKey, *ecdsa.PrivateKey:
return key, nil
default:
return nil, errors.New("unknown key time in PKCS#8 wrapping, unable to load key")
}
}

if key, err := x509.ParseECPrivateKey(block.Bytes); err == nil {
return key, nil
}

return nil, errors.New("no private key found, unable to load key")
}

// LoadKeyAndCertificate loads client certificate
func LoadKeyAndCertificate(keyBytes []byte, certBytes []byte) (*tls.Certificate, error) {
certificate, err := LoadCertificate(certBytes)
if err != nil {
return nil, err
}
key, err := LoadKey(keyBytes)
if err != nil {
return nil, err
}
certificate.PrivateKey = key
return certificate, nil
}

// LoadCertPool loads cert pool from ca certificate
func LoadCertPool(caBytes []byte) (*x509.CertPool, error) {
rootCertificate, err := LoadCertificate(caBytes)
if err != nil {
return nil, err
}
certPool := x509.NewCertPool()
for _, certBytes := range rootCertificate.Certificate {
cert, err := x509.ParseCertificate(certBytes)
if err != nil {
certPool = nil
return nil, err
}
certPool.AddCert(cert)
}

return certPool, nil
}
23 changes: 13 additions & 10 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ import (
"syscall"

coap "github.com/mainflux/coap-cli/coap"
"github.com/plgd-dev/go-coap/v2/message"
coapmsg "github.com/plgd-dev/go-coap/v2/message"
"github.com/plgd-dev/go-coap/v2/message/codes"
"github.com/plgd-dev/go-coap/v2/udp/message/pool"

"github.com/plgd-dev/go-coap/v3/message"
coapmsg "github.com/plgd-dev/go-coap/v3/message"
"github.com/plgd-dev/go-coap/v3/message/codes"
"github.com/plgd-dev/go-coap/v3/message/pool"
)

const (
Expand All @@ -36,7 +37,6 @@ mathod: get, put, post or delete
-p port (default: "5683")
-d data to be sent in POST or PUT (default: "")
-cf content format (default: 50 - JSON format))

Examples:
coap-cli get channels/0bb5ba61-a66e-4972-bab6-26f19962678f/messages/subtopic -auth 1e1017e6-dee7-45b4-8a13-00e6afeb66eb -o
coap-cli post channels/0bb5ba61-a66e-4972-bab6-26f19962678f/messages/subtopic -auth 1e1017e6-dee7-45b4-8a13-00e6afeb66eb -d "hello world"
Expand All @@ -55,7 +55,7 @@ func parseCode(code string) (codes.Code, error) {
case delete:
return codes.DELETE, nil
}
return 0, errors.New("Message can be GET, POST, PUT or DELETE")
return 0, errors.New("MESSAGE CAN BE GET, POST, PUT OR DELETE")
}

func printMsg(m *pool.Message) {
Expand All @@ -65,12 +65,14 @@ func printMsg(m *pool.Message) {
}

func main() {
certPath := os.Getenv("CERT_PATH")

if len(os.Args) < 2 {
log.Fatal(helpCmd)
}
help := strings.ToLower(os.Args[1])
if help == "-h" || help == "--help" {
log.Println(helpMsg)
log.Print(helpMsg)
os.Exit(0)
}

Expand All @@ -97,7 +99,7 @@ func main() {
a := flag.String("auth", "", "Auth token")
flag.Parse()

client, err := coap.New(*h + ":" + *p)
client, err := coap.New(*h+":"+*p,certPath)
if err != nil {
log.Fatal("Error creating client: ", err)
}
Expand All @@ -123,9 +125,10 @@ func main() {
if err != nil {
log.Fatal("Error observing resource: ", err)
}
errs := make(chan error, 2)
errs := make(chan error, 1)

go func() {
c := make(chan os.Signal)
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT)
errs <- fmt.Errorf("%s", <-c)
}()
Expand Down
51 changes: 37 additions & 14 deletions coap/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,48 @@ import (
"log"
"time"

"github.com/plgd-dev/go-coap/v2/message"
"github.com/plgd-dev/go-coap/v2/message/codes"
"github.com/plgd-dev/go-coap/v2/udp"
"github.com/plgd-dev/go-coap/v2/udp/client"
"github.com/plgd-dev/go-coap/v2/udp/message/pool"
certutil "github.com/mainflux/coap-cli/certutil"

"github.com/plgd-dev/go-coap/v3/dtls"
"github.com/plgd-dev/go-coap/v3/message"
"github.com/plgd-dev/go-coap/v3/message/codes"
"github.com/plgd-dev/go-coap/v3/message/pool"
"github.com/plgd-dev/go-coap/v3/udp"
"github.com/plgd-dev/go-coap/v3/udp/client"
)

// Client represents CoAP client.
type Client struct {
conn *client.ClientConn
conn *client.Conn
}

// Observation interface
type NewObservation interface {
Cancel(ctx context.Context, opts ...message.Option) error
Canceled() bool
}

// New returns new CoAP client connecting it to the server.
func New(addr string) (Client, error) {
c, err := udp.Dial(addr)
if err != nil {
log.Fatalf("Error dialing: %v", err)
}
func New(addr string, certPath string) (Client, error) {
switch {
case certPath != "":
config, err := certutil.CreateClientConfig(context.Background(), certPath)
if err != nil {
log.Fatalln(err)
}
co, err := dtls.Dial(addr, config)
if err != nil {
log.Fatalf("Error dialing: %v", err)
}
return Client{conn: co}, err
default:
c, err := udp.Dial(addr)
if err != nil {
log.Fatalf("Error dialing: %v", err)
}
return Client{conn: c}, nil

return Client{conn: c}, nil
}
}

// Send send a message.
Expand All @@ -45,11 +67,11 @@ func (c Client) Send(path string, msgCode codes.Code, cf message.MediaType, payl
case codes.DELETE:
return c.conn.Delete(ctx, path, opts...)
}
return nil, errors.New("Invalid message code")
return nil, errors.New("INVALID MESSAGE CODE")
}

// Receive receives a message.
func (c Client) Receive(path string, opts ...message.Option) (*client.Observation, error) {
func (c Client) Receive(path string, opts ...message.Option) (NewObservation, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()

Expand All @@ -64,4 +86,5 @@ func (c Client) Receive(path string, opts ...message.Option) (*client.Observatio
fmt.Println("Payload: ", string(body))
}
}, opts...)

}
24 changes: 17 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
module github.com/mainflux/coap-cli

go 1.15
go 1.21

require (
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/plgd-dev/go-coap/v2 v2.4.0
golang.org/x/net v0.0.0-20200513185701-a91f0712d120 // indirect
golang.org/x/sys v0.0.0-20201013081832-0aaa2718063a // indirect
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
gopkg.in/yaml.v2 v2.3.0 // indirect
github.com/pion/dtls/v2 v2.2.8-0.20230905141523-2b584af66577
github.com/plgd-dev/go-coap/v3 v3.1.5
)

require (
github.com/dsnet/golib/memfile v1.0.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/pion/logging v0.2.2 // indirect
github.com/pion/transport/v3 v3.0.1 // indirect
go.uber.org/atomic v1.11.0 // indirect
golang.org/x/crypto v0.13.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.12.0 // indirect
)
Loading