gomatter

package module
v0.0.0-...-88afe18 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 31, 2026 License: BSD-2-Clause Imports: 32 Imported by: 0

README

gomatter

Simple Matter protocol library for Go.

Go Reference Go Report Card

Goal

The goal is to create golang library and supporting tools to access matter devices.

Status

  • it can
    • commission devices
    • send commands to devices
    • read attributes from devices
    • subscribe and receive events or attributes
    • decode onboarding info (qr text, manual pair code)
    • discover commissionable devices
    • discover commissioned devices
    • open commissioning window
Tested devices

General info

  • it is best to understand matter to use this, but here is most important info:
    • device access is managed using certificates
    • easiest way how to talk to device is to have signed certificate of device admin user (alternative is setup ACLs and use non-admin user)
    • certificates are signed by CA
    • during commissioning procedure root CA certificate is pushed to device together with id of device admin user
    • root CA certificate is something you need to create once and store. loosing CA keys usually means that you will have to commission devices again
    • You will not be able to commission a device unless it is already on the network. This library does not support the bluetooth commissioning required.
    • to talk to device you have to commission it first
      • to commission device you usually need its pin/passcode and device be in state open for commissioning
      • device gets into commissioning window open state often by "factory reset"
      • when device is commissioned - connected to some fabric, it can be commissioned into other fabrics using api, where existing admin user sets device to be open for additional commissioning. During that device can be connected to additional fabric(s) - additional root CA installed and additional admin user configured

CLI usage

  • compile
git clone git@github.com:codegamc/gomatter.git
cd gomatter
go build -o gomatter demo/main.go
  • create directory to hold keys and certificates mkdir pem
  • generate CA key and certificate using ./gomatter ca-bootstrap
  • generate controller key and certificate using ./gomatter ca-createuser 100
    • 100 is example node-id of controller
  • find device IP
    • discover command can be used to discover matter devices and their ip address ./gomatter discover commissionable -d
  • find device commissioning passcode/pin
    • device may show it
    • it can be extracted from QR code. use decode-qr to extract passcode from text representation of QR code ./gomatter decode-qr MT:-24J0AFN00SIQ663000
    • it can be extracted from manual pairing code. use command decode-mc to extract passcode from manual pairing code ./gomatter decode-mc 35792000079
  • perform commissioning of device. This authenticates using passcode, uploads CA certificate to device, signs and uploads device's own certificate and sets admin user id.
    • required for commissioning:
      • ip address of device
      • device commissioning passcode/pin
      • ca key and certificate
      • controller node key and certificate
    • example: ./gomatter commission --ip 192.168.5.178 --pin 123456 --controller-id 100 --device-id 500
  • light on! ./gomatter cmd on --ip 192.168.5.178 --controller-id 100 --device-id 500
  • set color hue=150 saturation=200 transition_time=10 ./gomatter cmd color --ip 192.168.5.220 --controller-id 100 --device-id 500 150 200 10
  • subscribe to a Matter event ./gomatter cmd subscribe --ip 192.168.5.178 --controller-id 100 --device-id 500 1 0x101 1
  • subscribe to an attribute report ./gomatter cmd subscribe-attr --ip 192.168.5.178 --controller-id 100 --device-id 500 1 0x6 0
    • subscription commands now default to low-latency intervals: --min-interval=0 --max-interval=5
    • override them when needed, for example: ./gomatter cmd subscribe-attr --ip 192.168.5.178 --controller-id 100 --device-id 500 --min-interval 0 --max-interval 1 1 0x6 0
  • read an attribute from a device ./gomatter cmd read --ip 192.168.5.178 --controller-id 100 --device-id 500 1 0x6 0
  • list all fabrics on a device ./gomatter cmd list_fabrics --ip 192.168.5.178 --controller-id 100 --device-id 500
  • list device types on endpoint 0 ./gomatter cmd list_device_types --ip 192.168.5.178 --controller-id 100 --device-id 500
  • list clusters supported on an endpoint ./gomatter cmd list_supported_clusters --ip 192.168.5.178 --controller-id 100 --device-id 500 1
  • list network interfaces on a device ./gomatter cmd list_interfaces --ip 192.168.5.178 --controller-id 100 --device-id 500
  • retrieve diagnostic logs from a device ./gomatter cmd get_logs --ip 192.168.5.178 --controller-id 100 --device-id 500 0
  • open an additional commissioning window (to add a device to another fabric) ./gomatter cmd open_commissioning --ip 192.168.5.178 --controller-id 100 --device-id 500 123456
  • show fabric info ./gomatter fabric-info

API usage

Example applications

For low-level subscriptions, use EncodeIMSubscribeRequest for event subscriptions and EncodeIMSubscribeAttributeRequest for attribute subscriptions. These now default to minInterval=0 and maxInterval=5 for lower-latency reporting. Use EncodeIMSubscribeRequestWithIntervals or EncodeIMSubscribeAttributeRequestWithIntervals to override the intervals explicitly.

NewFabric requires a caller-provided 16-byte IPK. You can create one with GenerateIPK(), but if you need to reload an existing fabric you must persist and reuse the same IPK yourself.

Commission device using api

create ca with root certificate, create admin user, then commission device:

package main

import (
  "context"
  "net"

  "github.com/codegamc/gomatter"
)


func main() {
  var fabricID uint64 = 0x100
  var adminUser uint64 = 5
  var deviceID uint64 = 10
  deviceIP := "192.168.5.178"
  pin := 123456
  ipk, err := gomatter.GenerateIPK()
  if err != nil {
    panic(err)
  }

  cm := gomatter.NewFileCertManager(fabricID, gomatter.FileCertManagerConfig{})
  cm.BootstrapCa()
  cm.Load()
  cm.CreateUser(adminUser)
  fabric, err := gomatter.NewFabric(fabricID, cm, ipk)
  if err != nil {
    panic(err)
  }
  gomatter.Commission(context.Background(), fabric, net.ParseIP(deviceIP), pin, adminUser, deviceID)
}
Send ON command to commissioned device using api
package main

import (
  "context"
  "net"

  "github.com/codegamc/gomatter"
)


func main() {
  var fabricID uint64 = 0x100
  var adminUser uint64 = 5
  var deviceID uint64 = 10
  deviceIP := "192.168.5.178"
  ipk, err := gomatter.GenerateIPK()
  if err != nil {
    panic(err)
  }

  cm := gomatter.NewFileCertManager(fabricID, gomatter.FileCertManagerConfig{})
  cm.Load()
  fabric, err := gomatter.NewFabric(fabricID, cm, ipk)
  if err != nil {
    panic(err)
  }

  secureChannel, err := gomatter.ConnectDevice(context.Background(), net.ParseIP(deviceIP), 5540, fabric, deviceID, adminUser)
  if err != nil {
    panic(err)
  }
  defer secureChannel.Close()

  onCommand := gomatter.EncodeIMInvokeRequest(1,        // endpoint
                                             6,        // cluster (on/off)
                                             1,        // on command
                                             []byte{}, // no payload
                                             false,    // not timed
                                             1,        // exchange ID
                                             )
  secureChannel.Send(onCommand)
  resp, err := secureChannel.Receive(context.Background())
  if err != nil {
    panic(err)
  }
  resp.Tlv.Dump(0)
}
Discover IP address of previously commissioned device using api

Device exposes its info using mdns under identifier [compressed-fabric-id]-[device-id]. For this reason to discover commissioned device fabric info is required.

package main

import (
  "encoding/hex"
  "fmt"
  "strings"

  "github.com/codegamc/gomatter"
  "github.com/codegamc/gomatter/discover"
)



func main() {
  var fabricID uint64 = 0x100
  var deviceID uint64 = 10
  ipk, err := gomatter.GenerateIPK()
  if err != nil {
    panic(err)
  }


  cm := gomatter.NewFileCertManager(fabricID, gomatter.FileCertManagerConfig{})
  cm.Load()
  fabric, err := gomatter.NewFabric(fabricID, cm, ipk)
  if err != nil {
    panic(err)
  }

  identifier := fmt.Sprintf("%s-%016X", hex.EncodeToString(fabric.CompressedFabric()), deviceID)
  identifier = strings.ToUpper(identifier)
  identifier = identifier + "._matter._tcp.local."
  fmt.Printf("%s\n", identifier)
  devices := discover.DiscoverCommissioned("", true, identifier)
  for _, d := range devices {
    fmt.Printf("host:%s ip:%v\n", d.Host, d.Addrs)
  }
}
Extract pairing passcode from QR code and manual pairing code

Following example shows how to extract passcode from textual representation of QR code or from manual pairing code. Manual pairing code can have dash characters at any position(they are discarded)

package main

import (
	"fmt"

	"github.com/codegamc/gomatter/onboarding_payload"
)


func main() {
	setup_qr_code := "MT:-24J0AFN00SIQ663000"
	qr_decoded := onboarding_payload.DecodeQrText(setup_qr_code)
	fmt.Printf("passcode: %d\n", qr_decoded.Passcode)


	manual_pair_code := "357-920-000-79"
	code_decoded := onboarding_payload.DecodeManualPairingCode(manual_pair_code)
	fmt.Printf("passcode: %d\n", code_decoded.Passcode)
}

Set color of light to specific hue color
package main

import (
	"context"
	"fmt"
	"net"

	"github.com/codegamc/gomatter"
	"github.com/codegamc/gomatter/mattertlv"
)


func main() {
	var fabricID uint64 = 0x100
	var adminUser uint64 = 5
	var deviceID uint64 = 10
	deviceIP := "192.168.5.178"
	ipk, err := gomatter.GenerateIPK()
	if err != nil {
		panic(err)
	}

	cm := gomatter.NewFileCertManager(fabricID, gomatter.FileCertManagerConfig{})
	cm.Load()
	fabric, err := gomatter.NewFabric(fabricID, cm, ipk)
	if err != nil {
		panic(err)
	}


	secureChannel, err := gomatter.ConnectDevice(context.Background(), net.ParseIP(deviceIP), 5540, fabric, deviceID, adminUser)
	if err != nil {
		panic(err)
	}
	defer secureChannel.Close()

	var tlv mattertlv.TLVBuffer
	tlv.WriteUInt8(0, byte(hue))        // hue
	tlv.WriteUInt8(1, byte(saturation)) // saturation
	tlv.WriteUInt8(2, byte(time))       // time
	to_send := gomatter.EncodeInvokeCommand(1, 0x300, 6, tlv.Bytes())
	secureChannel.Send(to_send)

	resp, err := secureChannel.Receive(context.Background())
	if err != nil {
		panic(err)
	}
	status, err := resp.Tlv.GetIntRec([]int{1,0,1,1,0})
	if err != nil {
		panic(err)
	}
	fmt.Printf("result status: %d\n", status)
}
Certificate manager

NewFabric function accepts certificate manager object as input parameter. Certificate manager must implement interface CertificateManager and user can supply own implementation. Supplied CertManager created by NewFileCertManager stores all data in .pem files under the default pem directory, and FileCertManagerConfig.Path can be used to store them somewhere else.

Documentation

Overview

Package gomatter implements matter protocol to allow talking to matter enabled devices.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Commission

func Commission(ctx context.Context, fabric *Fabric, deviceIP net.IP, pin int, controllerID, deviceID uint64, opts ...Option) error

Commission performs commissioning procedure on device with deviceIP ip address.

  • fabric is fabric object with approriate certificate authority
  • pin is passcode used for device pairing
  • controllerID is identifier of node whioch will be owner/admin of this device
  • deviceID is identifier of "new" device

func CreateRandomBytes

func CreateRandomBytes(n int) []byte

func EncodeIMInvokeRequest

func EncodeIMInvokeRequest(endpoint uint16, cluster uint32, command uint32, payload []byte, timed bool, exchange uint16) []byte

EncodeIMInvokeRequest encodes Interaction Model Invoke Request message

func EncodeIMReadRequest

func EncodeIMReadRequest(endpoint uint16, cluster uint32, attr uint32) []byte

EncodeIMInvokeRequest encodes Interaction Model Read Request message

func EncodeIMStatusResponse

func EncodeIMStatusResponse(exchangeID uint16, iflag byte) []byte

EncodeIMStatusResponse encodes success Interaction Model Invoke Response

func EncodeIMSubscribeAttributeRequest

func EncodeIMSubscribeAttributeRequest(endpoint uint16, cluster uint32, attr uint32, opts ...SubscribeOption) []byte

EncodeIMSubscribeAttributeRequest encodes an Interaction Model Subscribe Request for a single attribute path. Use SubscribeOption to configure intervals and filtering behavior.

func EncodeIMSubscribeRequest

func EncodeIMSubscribeRequest(endpoint uint16, cluster uint32, event uint32, opts ...SubscribeOption) []byte

EncodeIMSubscribeRequest encodes an Interaction Model Subscribe Request for a single event path. Use SubscribeOption to configure intervals and filtering behavior.

func EncodeIMTimedRequest

func EncodeIMTimedRequest(exchange uint16, timeout uint16) []byte

EncodeIMInvokeRequest encodes Interaction Model Timed Request message

func EncodeStatusReport

func EncodeStatusReport(code StatusReportElements) []byte

func GenerateIPK

func GenerateIPK() ([]byte, error)

GenerateIPK creates a cryptographically secure random 16-byte IPK for callers that want helper generation without embedding the policy in NewFabric.

func ParseImInvokeResponse

func ParseImInvokeResponse(resp *mattertlv.TlvItem) (int, error)

ParseImInvokeResponse parses IM InvokeResponse TLV. Returns (0, nil) on success, (clusterStatusCode, nil) on cluster error, or (0, err) when the response cannot be parsed.

func SerializeCertificateIntoMatter

func SerializeCertificateIntoMatter(fabric *Fabric, in *x509.Certificate) []byte

SerializeCertificateIntoMatter serializes x509 certificate into matter certificate format. Matter certificate format is way how to make matter even more weird and complicated. Signature of matter vertificate must match signature of certificate reencoded to DER encoding. This requires to handle very carefully order and presence of all elements in original x509.

Types

type CertificateManager

type CertificateManager interface {
	GetCaPublicKey() ecdsa.PublicKey
	GetCaCertificate() *x509.Certificate

	// CreateUser creates keys and certificate for node with specific id
	// it must be possible to later retrieve node keys using GetPrivkey and certificate using GetCertificate
	CreateUser(node_id uint64) error

	// retrieve certificate of specified node (previously created by CreateUser)
	GetCertificate(id uint64) (*x509.Certificate, error)

	// retrieve key of specified node (previously created by CreateUser)
	GetPrivkey(id uint64) (*ecdsa.PrivateKey, error)

	// create and sign certificate using local CA keys
	SignCertificate(user_pubkey *ecdsa.PublicKey, node_id uint64) (*x509.Certificate, error)
}

matter certificate manager interface all generated certificates must be compatible with matter

  • this means that after they are reencoded to matter format and back their signature must match

type DecodedGeneric

type DecodedGeneric struct {
	MessageHeader  MessageHeader
	ProtocolHeader ProtocolMessageHeader
	Tlv            mattertlv.TlvItem
	Payload        []byte
	StatusReport   StatusReportElements
}

type Fabric

type Fabric struct {
	CertificateManager CertificateManager
	// contains filtered or unexported fields
}

Fabric structure represents matter Fabric. Its main parameters are Id of fabric and certificate manager.

func NewFabric

func NewFabric(id uint64, certman CertificateManager, ipk []byte) (*Fabric, error)

NewFabric constructs new Fabric object and requires a caller-provided IPK. Callers must persist and pass the same 16-byte IPK to reload an existing fabric.

func (Fabric) CompressedFabric

func (fabric Fabric) CompressedFabric() []byte

CompressedFabric returns Compressed Fabric Identifier which is used to identify fabric in matter protocol.

func (Fabric) GetOperationalDeviceId

func (fabric Fabric) GetOperationalDeviceId(in uint64) string

func (Fabric) Id

func (fabric Fabric) Id() uint64

type FileCertManager

type FileCertManager struct {
	// contains filtered or unexported fields
}

PEM file backed certiticate manager

func NewFileCertManager

func NewFileCertManager(fabric uint64, config FileCertManagerConfig) *FileCertManager

func (*FileCertManager) BootstrapCa

func (cm *FileCertManager) BootstrapCa() error

BootstrapCa initializes CA - creates CA keys and certificate

func (*FileCertManager) CreateUser

func (cm *FileCertManager) CreateUser(node_id uint64) error

func (*FileCertManager) GetCaCertificate

func (cm *FileCertManager) GetCaCertificate() *x509.Certificate

func (*FileCertManager) GetCaPublicKey

func (cm *FileCertManager) GetCaPublicKey() ecdsa.PublicKey

func (*FileCertManager) GetCertificate

func (cm *FileCertManager) GetCertificate(id uint64) (*x509.Certificate, error)

func (*FileCertManager) GetPrivkey

func (cm *FileCertManager) GetPrivkey(id uint64) (*ecdsa.PrivateKey, error)

func (*FileCertManager) Load

func (cm *FileCertManager) Load() error

Load initializes CA. It loads required state from files.

func (*FileCertManager) SignCertificate

func (cm *FileCertManager) SignCertificate(user_pubkey *ecdsa.PublicKey, node_id uint64) (*x509.Certificate, error)

type FileCertManagerConfig

type FileCertManagerConfig struct {
	Path string
}

type MessageHeader

type MessageHeader struct {
	// contains filtered or unexported fields
}

func (*MessageHeader) Decode

func (m *MessageHeader) Decode(data *bytes.Buffer) error

func (*MessageHeader) Dump

func (m *MessageHeader) Dump()

func (*MessageHeader) Encode

func (m *MessageHeader) Encode(data *bytes.Buffer)

type Opcode

type Opcode byte
const InteractionOpcodeInvokeReq Opcode = 0x8
const InteractionOpcodeInvokeRsp Opcode = 0x9
const InteractionOpcodeReadReq Opcode = 0x2
const InteractionOpcodeReportData Opcode = 0x5
const InteractionOpcodeStatusRsp Opcode = 0x1
const InteractionOpcodeSubscReq Opcode = 0x3
const InteractionOpcodeSubscRsp Opcode = 0x4
const InteractionOpcodeTimedReq Opcode = 0xa
const SecChanOpcodeAck Opcode = 0x10
const SecChanOpcodePAKE1 Opcode = 0x22
const SecChanOpcodePAKE2 Opcode = 0x23
const SecChanOpcodePAKE3 Opcode = 0x24
const SecChanOpcodePBKDFReq Opcode = 0x20
const SecChanOpcodePBKDFResp Opcode = 0x21
const SecChanOpcodeStatusRep Opcode = 0x40

type Option

type Option func(*channelOptions)

Option configures behavior of Commission and ConnectDevice.

func WithLocalPort

func WithLocalPort(port int) Option

WithLocalPort sets the local UDP port. Defaults to 0 (OS-assigned).

func WithReceiveTimeout

func WithReceiveTimeout(d time.Duration) Option

WithReceiveTimeout sets the per-receive deadline. Defaults to 3 seconds. Use 0 for no timeout.

func WithRemotePort

func WithRemotePort(port int) Option

WithRemotePort sets the remote UDP port. Defaults to 5540 (standard Matter port). Applies to Commission only; ConnectDevice takes the remote port as an explicit argument.

type ProtocolId

type ProtocolId uint16
const ProtocolIdInteraction ProtocolId = 1
const ProtocolIdSecureChannel ProtocolId = 0

type ProtocolMessageHeader

type ProtocolMessageHeader struct {
	Opcode     Opcode
	ExchangeId uint16
	ProtocolId ProtocolId
	// contains filtered or unexported fields
}

func (*ProtocolMessageHeader) Decode

func (m *ProtocolMessageHeader) Decode(data *bytes.Buffer)

func (*ProtocolMessageHeader) Dump

func (m *ProtocolMessageHeader) Dump()

func (*ProtocolMessageHeader) Encode

func (m *ProtocolMessageHeader) Encode(data *bytes.Buffer)

type SecureChannel

type SecureChannel struct {
	Udp *udpChannel

	Counter uint32
	// contains filtered or unexported fields
}

func ConnectDevice

func ConnectDevice(ctx context.Context, deviceIP net.IP, port int, fabric *Fabric, deviceID, adminID uint64, opts ...Option) (*SecureChannel, error)

func SigmaExchange

func SigmaExchange(ctx context.Context, fabric *Fabric, controllerID uint64, deviceID uint64, secureChannel *SecureChannel) (*SecureChannel, error)

SigmaExhange establishes secure session using CASE (Certificate Authenticated Session Establishment)

func StartSecureChannel

func StartSecureChannel(remoteIP net.IP, remotePort, localPort int) (*SecureChannel, error)

StartSecureChannel initializes secure channel for plain unencrypted communication. It initializes UDP interface and blocks local udp port. Secure channel becomes encrypted after encryption keys are supplied.

func (*SecureChannel) Close

func (sc *SecureChannel) Close()

Close secure channel. Send close session message to remote end and relase UDP port.

func (*SecureChannel) Receive

func (sc *SecureChannel) Receive(ctx context.Context) (DecodedGeneric, error)

func (*SecureChannel) ReceiveBlocking

func (sc *SecureChannel) ReceiveBlocking() (DecodedGeneric, error)

func (*SecureChannel) ReceiveWithTimeout

func (sc *SecureChannel) ReceiveWithTimeout(timeout time.Duration) (DecodedGeneric, error)

func (*SecureChannel) Send

func (sc *SecureChannel) Send(data []byte) error

Send sends Protocol Message via secure channel. It creates Matter Message by adding Message Header. Protocol Message is aes-ccm encrypted when channel does have encryption keys. When encryption keys are empty plain Message is sent.

func (*SecureChannel) SetReceiveTimeout

func (sc *SecureChannel) SetReceiveTimeout(timeout time.Duration)

type SpakeCtx

type SpakeCtx struct {
	W0 []byte
	W1 []byte

	X point
	Y point
	Z point
	V point
	L point

	Ke []byte
	Ka []byte
	// contains filtered or unexported fields
}

func NewSpaceCtx deprecated

func NewSpaceCtx() SpakeCtx

Deprecated: use NewSpakeCtx.

func NewSpakeCtx

func NewSpakeCtx() SpakeCtx

func (*SpakeCtx) CalculateX

func (ctx *SpakeCtx) CalculateX()

func (*SpakeCtx) CalculateZVb

func (ctx *SpakeCtx) CalculateZVb()

func (*SpakeCtx) GenerateRandomX

func (ctx *SpakeCtx) GenerateRandomX()

func (*SpakeCtx) GenerateRandomY

func (ctx *SpakeCtx) GenerateRandomY()

func (*SpakeCtx) GenerateW

func (ctx *SpakeCtx) GenerateW(passcode int, salt []byte, iterations int)

type StatusReportElements

type StatusReportElements struct {
	GeneralCode  uint16
	ProtocolId   uint32
	ProtocolCode uint16
}

func (StatusReportElements) Dump

func (sr StatusReportElements) Dump()

func (StatusReportElements) IsOk

func (sr StatusReportElements) IsOk() bool

type SubscribeOption

type SubscribeOption func(*subscribeOptions)

SubscribeOption configures behavior of EncodeIMSubscribeRequest and EncodeIMSubscribeAttributeRequest.

func WithFabricFiltered

func WithFabricFiltered(filtered bool) SubscribeOption

WithFabricFiltered limits attribute and event reports to data visible to the requesting fabric only. Defaults to false.

func WithKeepSubscriptions

func WithKeepSubscriptions(keep bool) SubscribeOption

WithKeepSubscriptions controls whether existing subscriptions on the device are preserved when a new subscription is established. Defaults to false.

func WithMaxInterval

func WithMaxInterval(s uint16) SubscribeOption

WithMaxInterval sets the maximum reporting interval in seconds. Defaults to 5.

func WithMinInterval

func WithMinInterval(s uint16) SubscribeOption

WithMinInterval sets the minimum reporting interval in seconds. Defaults to 0.

Directories

Path Synopsis
Package ccm implements a CCM, Counter with CBC-MAC as per RFC 3610.
Package ccm implements a CCM, Counter with CBC-MAC as per RFC 3610.
examples
basic command
gen command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL