Skip to content

Commit

Permalink
Support adding keys with different roles
Browse files Browse the repository at this point in the history
Previously, the API only allowed adding keys with the Owner or Driver
roles. This expands the API to allow keys with other roles. See
pkg/protocol/protocol.md for documentation on the different roles.
  • Loading branch information
Seth Terashima authored and sethterashima committed Apr 11, 2024
1 parent 281db4f commit 9d11f68
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 17 deletions.
17 changes: 9 additions & 8 deletions cmd/tesla-control/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/teslamotors/vehicle-command/pkg/account"
"github.com/teslamotors/vehicle-command/pkg/cli"
"github.com/teslamotors/vehicle-command/pkg/protocol"
"github.com/teslamotors/vehicle-command/pkg/protocol/protobuf/keys"
"github.com/teslamotors/vehicle-command/pkg/protocol/protobuf/vcsec"
"github.com/teslamotors/vehicle-command/pkg/vehicle"
)
Expand Down Expand Up @@ -229,12 +230,12 @@ var commands = map[string]*Command{
requiresFleetAPI: false,
args: []Argument{
Argument{name: "PUBLIC_KEY", help: "file containing public key (or corresponding private key)"},
Argument{name: "ROLE", help: "One of: owner, driver"},
Argument{name: "ROLE", help: "One of: owner, driver, fm (fleet manager), vehicle_monitor, charging_manager"},
Argument{name: "FORM_FACTOR", help: "One of: nfc_card, ios_device, android_device, cloud_key"},
},
handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error {
role := strings.ToUpper(args["ROLE"])
if role != "OWNER" && role != "DRIVER" {
role, ok := keys.Role_value["ROLE_"+strings.ToUpper(args["ROLE"])]
if !ok {
return fmt.Errorf("%w: invalid ROLE", ErrCommandLineArgs)
}
formFactor, ok := vcsec.KeyFormFactor_value["KEY_FORM_FACTOR_"+strings.ToUpper(args["FORM_FACTOR"])]
Expand All @@ -245,7 +246,7 @@ var commands = map[string]*Command{
if err != nil {
return fmt.Errorf("invalid public key: %s", err)
}
return car.AddKey(ctx, publicKey, role == "OWNER", vcsec.KeyFormFactor(formFactor))
return car.AddKeyWithRole(ctx, publicKey, keys.Role(role), vcsec.KeyFormFactor(formFactor))
},
},
"add-key-request": &Command{
Expand All @@ -254,12 +255,12 @@ var commands = map[string]*Command{
requiresFleetAPI: false,
args: []Argument{
Argument{name: "PUBLIC_KEY", help: "file containing public key (or corresponding private key)"},
Argument{name: "ROLE", help: "One of: owner, driver"},
Argument{name: "ROLE", help: "One of: owner, driver, fm (fleet manager), vehicle_monitor, charging_manager"},
Argument{name: "FORM_FACTOR", help: "One of: nfc_card, ios_device, android_device, cloud_key"},
},
handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error {
role := strings.ToUpper(args["ROLE"])
if role != "OWNER" && role != "DRIVER" {
role, ok := keys.Role_value["ROLE_"+strings.ToUpper(args["ROLE"])]
if !ok {
return fmt.Errorf("%w: invalid ROLE", ErrCommandLineArgs)
}
formFactor, ok := vcsec.KeyFormFactor_value["KEY_FORM_FACTOR_"+strings.ToUpper(args["FORM_FACTOR"])]
Expand All @@ -270,7 +271,7 @@ var commands = map[string]*Command{
if err != nil {
return fmt.Errorf("invalid public key: %s", err)
}
if err := car.SendAddKeyRequest(ctx, publicKey, role == "OWNER", vcsec.KeyFormFactor(formFactor)); err != nil {
if err := car.SendAddKeyRequestWithRole(ctx, publicKey, keys.Role(role), vcsec.KeyFormFactor(formFactor)); err != nil {
return err
}
fmt.Printf("Sent add-key request to %s. Confirm by tapping NFC card on center console.\n", car.VIN())
Expand Down
27 changes: 25 additions & 2 deletions pkg/vehicle/security.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/teslamotors/vehicle-command/pkg/connector"
"github.com/teslamotors/vehicle-command/pkg/protocol"
carserver "github.com/teslamotors/vehicle-command/pkg/protocol/protobuf/carserver"
"github.com/teslamotors/vehicle-command/pkg/protocol/protobuf/keys"
"github.com/teslamotors/vehicle-command/pkg/protocol/protobuf/vcsec"
)

Expand Down Expand Up @@ -178,10 +179,21 @@ func (v *Vehicle) TriggerHomelink(ctx context.Context, latitude float32, longitu
// AddKey adds a public key to the vehicle's whitelist. If isOwner is true, the new key can
// authorize changes to vehicle access controls, such as adding/removing other keys.
func (v *Vehicle) AddKey(ctx context.Context, publicKey *ecdh.PublicKey, isOwner bool, formFactor vcsec.KeyFormFactor) error {
if isOwner {
return v.AddKeyWithRole(ctx, publicKey, keys.Role_ROLE_OWNER, formFactor)
}
return v.AddKeyWithRole(ctx, publicKey, keys.Role_ROLE_DRIVER, formFactor)
}

// AddKeyWithRole adds a public key to the vehicle's whitelist. See [Protocol Specification] for
// more information on roles.
//
// [Protocol Specification]: https://github.com/teslamotors/vehicle-command/blob/main/pkg/protocol/protocol.md#roles
func (v *Vehicle) AddKeyWithRole(ctx context.Context, publicKey *ecdh.PublicKey, role keys.Role, formFactor vcsec.KeyFormFactor) error {
if publicKey.Curve() != ecdh.P256() {
return protocol.ErrInvalidPublicKey
}
payload := addKeyPayload(publicKey, isOwner, formFactor)
payload := addKeyPayload(publicKey, role, formFactor)
encodedPayload, err := proto.Marshal(payload)
if err != nil {
return err
Expand Down Expand Up @@ -276,13 +288,24 @@ func (v *Vehicle) Unlock(ctx context.Context) error {
// attempting to call v.SessionInfo with the domain argument set to
// [universal.Domain_DOMAIN_INFOTAINMENT].
func (v *Vehicle) SendAddKeyRequest(ctx context.Context, publicKey *ecdh.PublicKey, isOwner bool, formFactor vcsec.KeyFormFactor) error {
if isOwner {
return v.SendAddKeyRequestWithRole(ctx, publicKey, keys.Role_ROLE_OWNER, formFactor)
}
return v.SendAddKeyRequestWithRole(ctx, publicKey, keys.Role_ROLE_DRIVER, formFactor)
}

// SendAddKeyRequestWithRole behaves like [SendAddKeyRequest] except the new key's role can be
// specified explicitly. See [Protocol Specification] for more information on roles.
//
// [Protocol Specification]: https://github.com/teslamotors/vehicle-command/blob/main/pkg/protocol/protocol.md#roles
func (v *Vehicle) SendAddKeyRequestWithRole(ctx context.Context, publicKey *ecdh.PublicKey, role keys.Role, formFactor vcsec.KeyFormFactor) error {
if publicKey.Curve() != ecdh.P256() {
return protocol.ErrInvalidPublicKey
}
if _, ok := v.conn.(connector.FleetAPIConnector); ok {
return protocol.ErrRequiresBLE
}
encodedPayload, err := proto.Marshal(addKeyPayload(publicKey, isOwner, formFactor))
encodedPayload, err := proto.Marshal(addKeyPayload(publicKey, role, formFactor))
if err != nil {
return err
}
Expand Down
8 changes: 1 addition & 7 deletions pkg/vehicle/vcsec.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,7 @@ func (v *Vehicle) executeWhitelistOperation(ctx context.Context, payload []byte)
return err
}

func addKeyPayload(publicKey *ecdh.PublicKey, isOwner bool, formFactor vcsec.KeyFormFactor) *vcsec.UnsignedMessage {
var role keys.Role
if isOwner {
role = keys.Role_ROLE_OWNER
} else {
role = keys.Role_ROLE_DRIVER
}
func addKeyPayload(publicKey *ecdh.PublicKey, role keys.Role, formFactor vcsec.KeyFormFactor) *vcsec.UnsignedMessage {
return &vcsec.UnsignedMessage{
SubMessage: &vcsec.UnsignedMessage_WhitelistOperation{
WhitelistOperation: &vcsec.WhitelistOperation{
Expand Down

0 comments on commit 9d11f68

Please sign in to comment.