Skip to content
Open
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions cmd/provisioning-client/bluetooth.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
"strings"
Expand All @@ -27,6 +28,7 @@ const (
pskKey = "psk"
robotPartIDKey = "id"
robotPartSecretKey = "secret"
apiKeyCredsKey = "api_key"
appAddressKey = "app_address"
availableWiFiNetworksKey = "networks"
statusKey = "status"
Expand Down Expand Up @@ -296,6 +298,15 @@ func BTSetDeviceCreds(chars map[string]bluetooth.DeviceCharacteristic) error {
return err
}

apiKeyJSON, err := json.Marshal(opts.APIKey)
if err != nil {
return err
}
cryptAPIKey, err := encrypt(apiKeyJSON)
if err != nil {
return err
}

cryptAppAddr, err := encrypt([]byte(opts.AppAddr))
if err != nil {
return err
Expand All @@ -311,6 +322,11 @@ func BTSetDeviceCreds(chars map[string]bluetooth.DeviceCharacteristic) error {
return errw.Wrap(err, "writing secret")
}

_, err = chars[apiKeyCredsKey].WriteWithoutResponse(cryptAPIKey)
if err != nil {
return errw.Wrap(err, "writing api key")
}

_, err = chars[appAddressKey].WriteWithoutResponse(cryptAppAddr)
if err != nil {
return errw.Wrap(err, "writing app address")
Expand Down Expand Up @@ -450,6 +466,9 @@ func getCharicteristicsMap(device *bluetooth.Device) (map[string]bluetooth.Devic
case getUUID(robotPartSecretKey):
key = robotPartSecretKey
charMap[robotPartSecretKey] = char
case getUUID(apiKeyCredsKey):
key = apiKeyCredsKey
charMap[apiKeyCredsKey] = char
case getUUID(cryptoKey):
key = cryptoKey
charMap[cryptoKey] = char
Expand Down
10 changes: 8 additions & 2 deletions cmd/provisioning-client/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strings"

"github.com/viamrobotics/agent/subsystems/networking"
"github.com/viamrobotics/agent/utils"
pb "go.viam.com/api/provisioning/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
Expand Down Expand Up @@ -40,7 +41,7 @@ func grpcClient() error {
}

if opts.PartID != "" {
if err := SetDeviceCreds(ctx, client, opts.PartID, opts.Secret, opts.AppAddr); err != nil {
if err := SetDeviceCreds(ctx, client, opts.PartID, opts.Secret, opts.AppAddr, opts.APIKey); err != nil {
return err
}
}
Expand Down Expand Up @@ -91,13 +92,18 @@ func GetNetworks(ctx context.Context, client pb.ProvisioningServiceClient) error
return nil
}

func SetDeviceCreds(ctx context.Context, client pb.ProvisioningServiceClient, id, secret, appaddr string) error {
func SetDeviceCreds(ctx context.Context, client pb.ProvisioningServiceClient, id, secret, appaddr string, apiKey utils.APIKey) error {
fmt.Println("Writing device credentials...")

req := &pb.SetSmartMachineCredentialsRequest{
Cloud: &pb.CloudConfig{
Id: id,
Secret: secret,
AppAddress: appaddr,
ApiKey: &pb.APIKey{
Id: apiKey.ID,
Key: apiKey.Key,
},
},
}

Expand Down
34 changes: 28 additions & 6 deletions cmd/provisioning-client/opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"

"github.com/jessevdk/go-flags"
"github.com/viamrobotics/agent/utils"
)

var opts struct {
Expand All @@ -21,9 +22,12 @@ var opts struct {

PSK string `description:"psk for bluetooth security" long:"psk"`

AppAddr string `default:"https://app.viam.com:443" description:"Cloud address to set in viam.json" long:"app-addr"`
PartID string `description:"PartID to set in viam.json" long:"part-id"`
Secret string `description:"Device secret to set in viam.json" long:"secret"`
AppAddr string `default:"https://app.viam.com:443" description:"Cloud address to set in viam.json" long:"app-addr"`
PartID string `description:"PartID to set in viam.json" long:"part-id"`
Secret string `description:"Device secret to set in viam.json" long:"secret"`
APIKeyID string `description:"API Key ID" long:"api-key-id"`
APIKeyKey string `description:"API Key secret" long:"api-key-key"`
APIKey utils.APIKey

Exit bool `description:"Tell the device to exit provisioning mode" long:"exit" short:"e"`

Expand All @@ -33,6 +37,13 @@ var opts struct {
Help bool `description:"Show this help message" long:"help" short:"h"`
}

func SetAPIKey() {
opts.APIKey = utils.APIKey{
ID: opts.APIKeyID,
Key: opts.APIKeyKey,
}
}

func parseOpts() bool {
parser := flags.NewParser(&opts, flags.IgnoreUnknown)
parser.Usage = "runs as a background service and manages updates and the process lifecycle for viam-server."
Expand All @@ -55,9 +66,20 @@ func parseOpts() bool {
return false
}

if opts.PartID != "" || opts.Secret != "" {
if opts.PartID == "" || opts.Secret == "" || opts.AppAddr == "" {
fmt.Println("Error: Must set both Secret and PartID (and optionally AppAddr) at the same time!")
SetAPIKey()
if opts.PartID != "" || opts.Secret != "" || !opts.APIKey.IsEmpty() {
if opts.PartID == "" || opts.AppAddr == "" {
fmt.Println("Error: Must set PartID and AppAddr when configuring credentials!")
return false
}

if opts.APIKey.IsPartiallySet() {
fmt.Println("Error: API Key must have both ID and Key set, or neither!")
return false
}

if opts.Secret == "" && opts.APIKey.IsEmpty() {
fmt.Println("Error: Must provide either Secret or complete API Key!")
return false
}
}
Expand Down
44 changes: 25 additions & 19 deletions cmd/test-client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"os"

"github.com/jessevdk/go-flags"
errw "github.com/pkg/errors"
"github.com/viamrobotics/agent/utils"
pb "go.viam.com/api/app/agent/v1"
"go.viam.com/rdk/logging"
"go.viam.com/utils/rpc"
Expand Down Expand Up @@ -91,28 +93,36 @@ func loadCredentials(path string) (*logging.CloudConfig, error) {
return nil, err
}

cfg := make(map[string]map[string]string)
var cfg map[string]interface{}
err = json.Unmarshal(b, &cfg)
if err != nil {
return nil, err
}

cloud, ok := cfg["cloud"]
cloud, ok := cfg["cloud"].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("no cloud section in file %s", path)
return nil, errw.Errorf("no cloud section in file %s", path)
}

for _, req := range []string{"app_address", "id", "secret"} {
field, ok := cloud[req]
if !ok {
return nil, fmt.Errorf("no cloud config field for %s", field)
}
appAddress, ok := cloud["app_address"].(string)
if !ok || appAddress == "" {
return nil, errw.New("field 'app_address' in cloud config must be a non-empty string")
}

id, ok := cloud["id"].(string)
if !ok || id == "" {
return nil, errw.New("field 'id' in cloud config must be a non-empty string")
}

cloudCreds, err := utils.ParseCloudCreds(cloud)
if err != nil {
return nil, err
}

cloudConfig := &logging.CloudConfig{
AppAddress: cloud["app_address"],
ID: cloud["id"],
Secret: cloud["secret"],
AppAddress: appAddress,
ID: id,
CloudCred: cloudCreds,
}

return cloudConfig, nil
Expand All @@ -125,14 +135,10 @@ func dial(ctx context.Context, logger logging.Logger, cloudConfig *logging.Cloud
}

dialOpts := make([]rpc.DialOption, 0, 2)
// Only add credentials when secret is set.
if cloudConfig.Secret != "" {
dialOpts = append(dialOpts, rpc.WithEntityCredentials(cloudConfig.ID,
rpc.Credentials{
Type: "robot-secret",
Payload: cloudConfig.Secret,
},
))

// Only add credentials when they are set.
if cloudConfig.CloudCred != nil {
dialOpts = append(dialOpts, cloudConfig.CloudCred)
}

if u.Scheme == "http" {
Expand Down
13 changes: 7 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ require (
github.com/ulikunitz/xz v0.5.15
github.com/viamrobotics/gonetworkmanager/v2 v2.2.3
go.uber.org/zap v1.27.0
go.viam.com/api v0.1.503
go.viam.com/rdk v0.108.0
go.viam.com/api v0.1.508
go.viam.com/rdk v0.109.0
go.viam.com/test v1.2.4
go.viam.com/utils v0.4.3
golang.org/x/sys v0.38.0
Expand Down Expand Up @@ -72,18 +72,18 @@ require (
github.com/muhlemmer/gu v0.3.1 // indirect
github.com/pion/datachannel v1.5.10 // indirect
github.com/pion/dtls/v2 v2.2.12 // indirect
github.com/pion/interceptor v0.1.41 // indirect
github.com/pion/interceptor v0.1.42 // indirect
github.com/pion/logging v0.2.4 // indirect
github.com/pion/mdns v0.0.12 // indirect
github.com/pion/randutil v0.1.0 // indirect
github.com/pion/rtcp v1.2.16 // indirect
github.com/pion/rtp v1.8.25 // indirect
github.com/pion/sctp v1.8.40 // indirect
github.com/pion/rtp v1.8.26 // indirect
github.com/pion/sctp v1.8.41 // indirect
github.com/pion/sdp/v3 v3.0.16 // indirect
github.com/pion/srtp/v2 v2.0.20 // indirect
github.com/pion/stun v0.6.1 // indirect
github.com/pion/transport/v2 v2.2.10 // indirect
github.com/pion/transport/v3 v3.0.8 // indirect
github.com/pion/transport/v3 v3.1.1 // indirect
github.com/pion/turn/v2 v2.1.6 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
Expand Down Expand Up @@ -120,6 +120,7 @@ require (
golang.org/x/term v0.37.0 // indirect
golang.org/x/text v0.31.0 // indirect
golang.org/x/tools v0.39.0 // indirect
gonum.org/v1/gonum v0.16.0 // indirect
google.golang.org/api v0.246.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
Expand Down
Loading
Loading