Skip to content

Commit

Permalink
Add PSK support
Browse files Browse the repository at this point in the history
  • Loading branch information
janvrska committed May 14, 2024
1 parent 677ce13 commit 15219f7
Show file tree
Hide file tree
Showing 12 changed files with 556 additions and 142 deletions.
83 changes: 66 additions & 17 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// Use ClientConfig struct to set various client's options and features.
//
// Example:
//
// import (
// "fmt"
// "time"
Expand Down Expand Up @@ -64,6 +65,7 @@ import (
"sync"
"time"

"github.com/patrickmn/go-cache"
"github.com/pion/dtls/v2"
"github.com/pion/dtls/v2/pkg/crypto/selfsign"
"golang.org/x/sync/errgroup"
Expand All @@ -78,10 +80,24 @@ import (
type ClientConfig struct {
// UseDTLS controls whether DTLS should be used to secure the connection
// to the MQTT-SN gateway.
UseDTLS bool
Certificate *tls.Certificate
PrivateKey crypto.PrivateKey
CACertificates []*x509.Certificate
UseDTLS bool
Certificate *tls.Certificate
PrivateKey crypto.PrivateKey
// UsePSK controls whether pre-shared key should be used to secure the
// connection to the MQTT-SN gateway. If UsePSK is true, you must provide
// PSKIdentityHint, PSKApiBasicAuthUsername, PSKApiBasicAuthPassword and
// PSKApiEndpoint.
// If UsePSK is true, the client will use PSK instead of the certificate
// and private key.
UsePSK bool
PSK *cache.Cache
PSKCacheExpiration time.Duration
PSKIdentityHint string
PSKApiTimeout time.Duration
PSKApiBasicAuthUsername string
PSKApiBasicAuthPassword string
PSKApiEndpoint string
CACertificates []*x509.Certificate
// SelfSigned controls whether the client should use a self-signed
// certificate and key. If SelfSigned is false and UseDTLS is true, you
// must provide CertFile and KeyFile.
Expand Down Expand Up @@ -147,21 +163,24 @@ func (c *Client) connectDTLS(ctx context.Context, address string) (net.Conn, err
var certificate *tls.Certificate
var err error

if c.cfg.SelfSigned {
var cert tls.Certificate
cert, err = selfsign.GenerateSelfSigned()
certificate = &cert
} else {
privateKey := c.cfg.PrivateKey
if privateKey == nil {
err = errors.New("private key is missing")
}
if certificate = c.cfg.Certificate; certificate != nil {
certificate.PrivateKey = privateKey
if !c.cfg.UsePSK && c.cfg.UseDTLS {
if c.cfg.SelfSigned {
var cert tls.Certificate
cert, err = selfsign.GenerateSelfSigned()
certificate = &cert
} else {
err = errors.New("TLS certificate is missing")
privateKey := c.cfg.PrivateKey
if privateKey == nil {
err = errors.New("private key is missing")
}
if certificate = c.cfg.Certificate; certificate != nil {
certificate.PrivateKey = privateKey
} else {
err = errors.New("TLS certificate is missing")
}
}
}

if err != nil {
return nil, err
}
Expand All @@ -182,12 +201,42 @@ func (c *Client) connectDTLS(ctx context.Context, address string) (net.Conn, err

// Prepare the configuration of the DTLS connection
config := &dtls.Config{
Certificates: []tls.Certificate{*certificate},
InsecureSkipVerify: c.cfg.Insecure,
ExtendedMasterSecret: dtls.RequireExtendedMasterSecret,
RootCAs: certPool,
}

if !c.cfg.UsePSK && c.cfg.UseDTLS && certificate != nil {
config.Certificates = []tls.Certificate{*certificate}
}

if c.cfg.UsePSK && c.cfg.UseDTLS {
config.CipherSuites = []dtls.CipherSuiteID{dtls.TLS_PSK_WITH_AES_128_GCM_SHA256}
config.PSK = func(hint []byte) ([]byte, error) {
psk, ok := c.cfg.PSK.Get(string(hint))
if ok {
return psk.([]byte), nil
}

psk, ok = util.GetPSK(
string(hint),
c.cfg.PSKApiEndpoint,
c.cfg.PSKApiBasicAuthUsername,
c.cfg.PSKApiBasicAuthPassword,
c.cfg.PSKApiTimeout,
c.log,
)

if ok {
c.cfg.PSK.Set(string(hint), psk, c.cfg.PSKCacheExpiration)
return psk.([]byte), nil
}

return nil, errors.New("PSK not found")
}
config.PSKIdentityHint = []byte(c.cfg.PSKIdentityHint)
}

// Connect to a DTLS server
ctx2, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
Expand Down
49 changes: 33 additions & 16 deletions cmd/bisquitt-pub/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"syscall"
"time"

"github.com/patrickmn/go-cache"
"github.com/urfave/cli/v2"

snClient "github.com/energomonitor/bisquitt/client"
Expand All @@ -33,13 +34,21 @@ func handleAction() cli.ActionFunc {

useDTLS := c.Bool(DtlsFlag)
useSelfSigned := c.Bool(SelfSignedFlag)
usePSK := c.Bool(PskFlag)
pskCacheExpiration := c.Duration(PskCacheExpirationFlag)

pskIdentity := c.String(PskIdentityFlag)
pskApiTimeout := c.Duration(PskApiTimeoutFlag)
pskApiBasicAuthUsername := c.String(PskApiBasicAuthUsernameFlag)
pskApiBasicAuthPassword := c.String(PskApiBasicAuthPasswordFlag)
pskApiEndpoint := c.String(PskApiEndpointFlag)
certFile := c.Path(CertFlag)
keyFile := c.Path(KeyFlag)
caFile := c.Path(CAFileFlag)
caPath := c.Path(CAPathFlag)
debug := c.Bool(DebugFlag)

if useDTLS && (certFile == "" || keyFile == "") && !useSelfSigned {
if useDTLS && ((certFile == "" || keyFile == "") && !useSelfSigned) && !usePSK {
return fmt.Errorf(`options "--%s" and "--%s" are mandatory when using DTLS. Use "--%s" to generate self-signed certificate.`,
CertFlag, KeyFlag, SelfSignedFlag)
}
Expand Down Expand Up @@ -132,21 +141,29 @@ func handleAction() cli.ActionFunc {
password := []byte(c.String(PasswordFlag))

clientCfg := &snClient.ClientConfig{
ClientID: clientID,
UseDTLS: useDTLS,
SelfSigned: useSelfSigned,
Insecure: insecure,
Certificate: certificate,
PrivateKey: privateKey,
CACertificates: caCertificates,
RetryDelay: 10 * time.Second,
RetryCount: 4,
ConnectTimeout: 20 * time.Second,
KeepAlive: 60 * time.Second,
CleanSession: true,
PredefinedTopics: predefinedTopics,
User: user,
Password: password,
ClientID: clientID,
UseDTLS: useDTLS,
UsePSK: usePSK,
PSK: cache.New(pskCacheExpiration, 5*time.Minute),
PSKCacheExpiration: pskCacheExpiration,
PSKIdentityHint: pskIdentity,
PSKApiTimeout: pskApiTimeout,
PSKApiBasicAuthUsername: pskApiBasicAuthUsername,
PSKApiBasicAuthPassword: pskApiBasicAuthPassword,
PSKApiEndpoint: pskApiEndpoint,
SelfSigned: useSelfSigned,
Insecure: insecure,
Certificate: certificate,
PrivateKey: privateKey,
CACertificates: caCertificates,
RetryDelay: 10 * time.Second,
RetryCount: 4,
ConnectTimeout: 20 * time.Second,
KeepAlive: 60 * time.Second,
CleanSession: true,
PredefinedTopics: predefinedTopics,
User: user,
Password: password,
}

var logger util.Logger
Expand Down
97 changes: 78 additions & 19 deletions cmd/bisquitt-pub/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,40 @@ package main

import (
"fmt"
"time"

"github.com/urfave/cli/v2"

"github.com/energomonitor/bisquitt"
)

const (
HostFlag = "host"
PortFlag = "port"
DtlsFlag = "dtls"
SelfSignedFlag = "self-signed"
CertFlag = "cert"
KeyFlag = "key"
CAFileFlag = "cafile"
CAPathFlag = "capath"
InsecureFlag = "insecure"
DebugFlag = "debug"
TopicFlag = "topic"
MessageFlag = "message"
RetainFlag = "retain"
PredefinedTopicFlag = "predefined-topic"
PredefinedTopicsFileFlag = "predefined-topics-file"
QOSFlag = "qos"
ClientIDFlag = "client-id"
UserFlag = "user"
PasswordFlag = "password"
HostFlag = "host"
PortFlag = "port"
DtlsFlag = "dtls"
SelfSignedFlag = "self-signed"
PskFlag = "psk"
PskCacheExpirationFlag = "psk-cache-expiration"
PskIdentityFlag = "psk-identity"
PskApiTimeoutFlag = "psk-api-timeout"
PskApiBasicAuthUsernameFlag = "psk-api-basic-auth-username"
PskApiBasicAuthPasswordFlag = "psk-api-basic-auth-password"
PskApiEndpointFlag = "psk-api-endpoint"
CertFlag = "cert"
KeyFlag = "key"
CAFileFlag = "cafile"
CAPathFlag = "capath"
InsecureFlag = "insecure"
DebugFlag = "debug"
TopicFlag = "topic"
MessageFlag = "message"
RetainFlag = "retain"
PredefinedTopicFlag = "predefined-topic"
PredefinedTopicsFileFlag = "predefined-topics-file"
QOSFlag = "qos"
ClientIDFlag = "client-id"
UserFlag = "user"
PasswordFlag = "password"
)

func init() {
Expand Down Expand Up @@ -76,6 +84,57 @@ var Application = cli.App{
"SELF_SIGNED",
},
},
&cli.BoolFlag{
Name: PskFlag,
Usage: "use PSK",
EnvVars: []string{
"PSK_ENABLED",
},
},
&cli.DurationFlag{
Name: PskCacheExpirationFlag,
Usage: "PSK cache expiration",
Value: 5 * time.Minute,
EnvVars: []string{
"PSK_CACHE_EXPIRATION",
},
},
&cli.StringFlag{
Name: PskIdentityFlag,
Usage: "PSK identity",
EnvVars: []string{
"PSK_IDENTITY",
},
},
&cli.DurationFlag{
Name: PskApiTimeoutFlag,
Usage: "PSK API timeout",
Value: 5 * time.Second,
EnvVars: []string{
"PSK_API_TIMEOUT",
},
},
&cli.StringFlag{
Name: PskApiBasicAuthUsernameFlag,
Usage: "PSK API basic auth username",
EnvVars: []string{
"PSK_API_BASIC_AUTH_USERNAME",
},
},
&cli.StringFlag{
Name: PskApiBasicAuthPasswordFlag,
Usage: "PSK API basic auth password",
EnvVars: []string{
"PSK_API_BASIC_AUTH_PASSWORD",
},
},
&cli.StringFlag{
Name: PskApiEndpointFlag,
Usage: "PSK API endpoint",
EnvVars: []string{
"PSK_API_ENDPOINT",
},
},
&cli.PathFlag{
Name: CertFlag,
Usage: "DTLS certificate file",
Expand Down
48 changes: 32 additions & 16 deletions cmd/bisquitt-sub/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"syscall"
"time"

"github.com/patrickmn/go-cache"
"github.com/urfave/cli/v2"

snClient "github.com/energomonitor/bisquitt/client"
Expand All @@ -34,13 +35,20 @@ func handleAction() cli.ActionFunc {

useDTLS := c.Bool(DtlsFlag)
useSelfSigned := c.Bool(SelfSignedFlag)
usePSK := c.Bool(PskFlag)
pskCacheExpiration := c.Duration(PskCacheExpirationFlag)
pskIdentity := c.String(PskIdentityFlag)
pskApiTimeout := c.Duration(PskApiTimeoutFlag)
pskApiBasicAuthUsername := c.String(PskApiBasicAuthUsernameFlag)
pskApiBasicAuthPassword := c.String(PskApiBasicAuthPasswordFlag)
pskApiEndpoint := c.String(PskApiEndpointFlag)
certFile := c.Path(CertFlag)
keyFile := c.Path(KeyFlag)
caFile := c.Path(CAFileFlag)
caPath := c.Path(CAPathFlag)
debug := c.Bool(DebugFlag)

if useDTLS && (certFile == "" || keyFile == "") && !useSelfSigned {
if useDTLS && ((certFile == "" || keyFile == "") && !useSelfSigned) && !usePSK {
return fmt.Errorf(`options "--%s" and "--%s" are mandatory when using DTLS. Use "--%s" to generate self-signed certificate.`,
CertFlag, KeyFlag, SelfSignedFlag)
}
Expand Down Expand Up @@ -133,21 +141,29 @@ func handleAction() cli.ActionFunc {
password := []byte(c.String(PasswordFlag))

clientCfg := &snClient.ClientConfig{
ClientID: clientID,
UseDTLS: useDTLS,
SelfSigned: useSelfSigned,
Insecure: insecure,
Certificate: certificate,
PrivateKey: privateKey,
CACertificates: caCertificates,
PredefinedTopics: predefinedTopics,
RetryDelay: 10 * time.Second,
RetryCount: 4,
ConnectTimeout: 20 * time.Second,
KeepAlive: 60 * time.Second,
CleanSession: true,
User: user,
Password: password,
ClientID: clientID,
UseDTLS: useDTLS,
SelfSigned: useSelfSigned,
UsePSK: usePSK,
PSK: cache.New(pskCacheExpiration, 5*time.Minute),
PSKCacheExpiration: pskCacheExpiration,
PSKIdentityHint: pskIdentity,
PSKApiTimeout: pskApiTimeout,
PSKApiBasicAuthUsername: pskApiBasicAuthUsername,
PSKApiBasicAuthPassword: pskApiBasicAuthPassword,
PSKApiEndpoint: pskApiEndpoint,
Insecure: insecure,
Certificate: certificate,
PrivateKey: privateKey,
CACertificates: caCertificates,
PredefinedTopics: predefinedTopics,
RetryDelay: 10 * time.Second,
RetryCount: 4,
ConnectTimeout: 20 * time.Second,
KeepAlive: 60 * time.Second,
CleanSession: true,
User: user,
Password: password,
}

if c.IsSet(WillTopicFlag) {
Expand Down
Loading

0 comments on commit 15219f7

Please sign in to comment.