veyron/tools/mgmt/device/impl/...
-Add option for a claimer to verify the public-key of the device.
Change-Id: I71b8062075262f0f12ef921ff73bd4f6bfb0de8a
diff --git a/runtimes/google/ipc/client.go b/runtimes/google/ipc/client.go
index f38c589..c3e7136 100644
--- a/runtimes/google/ipc/client.go
+++ b/runtimes/google/ipc/client.go
@@ -519,7 +519,7 @@
var err error
patterns := resolved.Servers[r.index].BlessingPatterns
if serverB, grantedB, err = c.authorizeServer(ctx, r.flow, name, method, patterns, opts); err != nil {
- r.err = verror.New(errNotTrusted, ctx, name, r.flow.RemoteBlessings(), err)
+ r.err = verror.New(verror.ErrNotTrusted, ctx, name, r.flow.RemoteBlessings(), err)
vlog.VI(2).Infof("ipc: err: %s", r.err)
r.flow.Close()
r.flow = nil
@@ -612,7 +612,7 @@
for _, r := range responses {
if r != nil && r.err != nil {
switch {
- case verror.Is(r.err, errNotTrusted.ID) || verror.Is(r.err, errAuthError.ID):
+ case verror.Is(r.err, verror.ErrNotTrusted.ID) || verror.Is(r.err, errAuthError.ID):
untrusted = append(untrusted, "("+r.err.Error()+") ")
default:
noconn = append(noconn, "("+r.err.Error()+") ")
diff --git a/services/mgmt/device/starter/starter.go b/services/mgmt/device/starter/starter.go
index 4abcebd..a6dea2e 100644
--- a/services/mgmt/device/starter/starter.go
+++ b/services/mgmt/device/starter/starter.go
@@ -3,6 +3,7 @@
package starter
import (
+ "encoding/base64"
"fmt"
"os"
"path/filepath"
@@ -129,7 +130,12 @@
shutdown()
return nil, err
}
- vlog.Infof("Unclaimed device manager (%v) published as %v", endpoints[0].Name(), claimableServerName)
+ publicKey, err := veyron2.GetPrincipal(ctx).PublicKey().MarshalBinary()
+ if err != nil {
+ shutdown()
+ return nil, err
+ }
+ vlog.Infof("Unclaimed device manager (%v) published as %v with public_key:%s", endpoints[0].Name(), claimableServerName, base64.URLEncoding.EncodeToString(publicKey))
return shutdown, nil
}
diff --git a/tools/mgmt/device/doc.go b/tools/mgmt/device/doc.go
index 2592498..9c7f62e 100644
--- a/tools/mgmt/device/doc.go
+++ b/tools/mgmt/device/doc.go
@@ -193,13 +193,19 @@
Claim the device.
Usage:
- device claim <device> <grant extension> <pairing token>
+ device claim <device> <grant extension> <pairing token> <device publickey>
<device> is the veyron object name of the device manager's device service.
<grant extension> is used to extend the default blessing of the current
principal when blessing the app instance.
+<pairing token> is a token that the device manager expects to be replayed during
+a claim operation on the device.
+
+<device publickey> is the marshalled public key of the device manager we are
+claiming.
+
Device Stop
Stop the given application instance.
diff --git a/tools/mgmt/device/impl/devicemanager_mock_test.go b/tools/mgmt/device/impl/devicemanager_mock_test.go
index 7c039e9..baadc8c 100644
--- a/tools/mgmt/device/impl/devicemanager_mock_test.go
+++ b/tools/mgmt/device/impl/devicemanager_mock_test.go
@@ -275,7 +275,7 @@
}
func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
- return device.DeviceServer(&mockDeviceInvoker{tape: d.tape, t: d.t}), nil, nil
+ return &mockDeviceInvoker{tape: d.tape, t: d.t}, nil, nil
}
func startServer(t *testing.T, ctx *context.T, tape *Tape) (ipc.Server, naming.Endpoint, error) {
diff --git a/tools/mgmt/device/impl/impl.go b/tools/mgmt/device/impl/impl.go
index 8cf1b90..6fbd325 100644
--- a/tools/mgmt/device/impl/impl.go
+++ b/tools/mgmt/device/impl/impl.go
@@ -1,6 +1,7 @@
package impl
import (
+ "encoding/base64"
"encoding/json"
"fmt"
@@ -149,16 +150,22 @@
Name: "claim",
Short: "Claim the device.",
Long: "Claim the device.",
- ArgsName: "<device> <grant extension> <pairing token>",
+ ArgsName: "<device> <grant extension> <pairing token> <device publickey>",
ArgsLong: `
<device> is the veyron object name of the device manager's device service.
<grant extension> is used to extend the default blessing of the
-current principal when blessing the app instance.`,
+current principal when blessing the app instance.
+
+<pairing token> is a token that the device manager expects to be replayed
+during a claim operation on the device.
+
+<device publickey> is the marshalled public key of the device manager we
+are claiming.`,
}
func runClaim(cmd *cmdline.Command, args []string) error {
- if expected, max, got := 2, 3, len(args); expected > got || got > max {
+ if expected, max, got := 2, 4, len(args); expected > got || got > max {
return cmd.UsageErrorf("claim: incorrect number of arguments, expected atleast %d (max: %d), got %d", expected, max, got)
}
deviceName, grant := args[0], args[1]
@@ -166,11 +173,22 @@
if len(args) > 2 {
pairingToken = args[2]
}
- principal := veyron2.GetPrincipal(gctx)
- // Skip server authorization since an unclaimed device has no
- // credentials that will be recognized by the claimer.
- if err := device.ClaimableClient(deviceName).Claim(gctx, pairingToken, &granter{p: principal, extension: grant}, options.SkipResolveAuthorization{}); err != nil {
- return fmt.Errorf("Claim failed: %v", err)
+ var serverKeyOpts ipc.CallOpt
+ if len(args) > 3 {
+ marshalledPublicKey, err := base64.URLEncoding.DecodeString(args[3])
+ if err != nil {
+ return fmt.Errorf("Failed to base64 decode publickey: %v", err)
+ }
+ if deviceKey, err := security.UnmarshalPublicKey(marshalledPublicKey); err != nil {
+ return fmt.Errorf("Failed to unmarshal device public key:%v", err)
+ } else {
+ serverKeyOpts = options.ServerPublicKey{deviceKey}
+ }
+ }
+ // Skip server resolve authorization since an unclaimed device might have
+ // roots that will not be recognized by the claimer.
+ if err := device.ClaimableClient(deviceName).Claim(gctx, pairingToken, &granter{p: veyron2.GetPrincipal(gctx), extension: grant}, serverKeyOpts, options.SkipResolveAuthorization{}); err != nil {
+ return err
}
fmt.Fprintln(cmd.Stdout(), "Successfully claimed.")
return nil
diff --git a/tools/mgmt/device/impl/impl_test.go b/tools/mgmt/device/impl/impl_test.go
index a23ab5f..da1f540 100644
--- a/tools/mgmt/device/impl/impl_test.go
+++ b/tools/mgmt/device/impl/impl_test.go
@@ -2,17 +2,20 @@
import (
"bytes"
+ "encoding/base64"
"encoding/json"
"fmt"
"reflect"
"strings"
"testing"
+ "v.io/core/veyron2"
"v.io/core/veyron2/naming"
"v.io/core/veyron2/services/mgmt/application"
"v.io/core/veyron2/services/mgmt/device"
"v.io/core/veyron2/verror"
+ "v.io/core/veyron/security"
"v.io/core/veyron/tools/mgmt/device/impl"
)
@@ -272,10 +275,6 @@
}
}
-// TODO(ashankar): Re-enable
-// The mock server must provide both the device.Claimable and
-// device.Device interfaces.
-/*
func TestClaimCommand(t *testing.T) {
shutdown := initTest()
defer shutdown()
@@ -292,34 +291,54 @@
var stdout, stderr bytes.Buffer
cmd.Init(nil, &stdout, &stderr)
deviceName := naming.JoinAddressName(endpoint.String(), "")
+ deviceKey, err := veyron2.GetPrincipal(gctx).PublicKey().MarshalBinary()
+ if err != nil {
+ t.Fatalf("Failed to marshal principal public key: %v", err)
+ }
// Confirm that we correctly enforce the number of arguments.
if err := cmd.Execute([]string{"claim", "nope"}); err == nil {
t.Fatalf("wrongly failed to receive a non-nil error.")
}
- if expected, got := "ERROR: claim: incorrect number of arguments, expected atleast 2 (max: 3), got 1", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
+ if expected, got := "ERROR: claim: incorrect number of arguments, expected atleast 2 (max: 4), got 1", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
t.Fatalf("Unexpected output from claim. Got %q, expected prefix %q", got, expected)
}
stdout.Reset()
stderr.Reset()
tape.Rewind()
- if err := cmd.Execute([]string{"claim", "nope", "nope", "nope", "nope"}); err == nil {
+ if err := cmd.Execute([]string{"claim", "nope", "nope", "nope", "nope", "nope"}); err == nil {
t.Fatalf("wrongly failed to receive a non-nil error.")
}
- if expected, got := "ERROR: claim: incorrect number of arguments, expected atleast 2 (max: 3), got 4", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
+ if expected, got := "ERROR: claim: incorrect number of arguments, expected atleast 2 (max: 4), got 5", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
t.Fatalf("Unexpected output from claim. Got %q, expected prefix %q", got, expected)
}
stdout.Reset()
stderr.Reset()
tape.Rewind()
+ // Incorrect operation
+ var pairingToken string
+ var deviceKeyWrong []byte
+ if publicKey, _, err := security.NewPrincipalKey(); err != nil {
+ t.Fatalf("NewPrincipalKey failed: %v", err)
+ } else {
+ if deviceKeyWrong, err = publicKey.MarshalBinary(); err != nil {
+ t.Fatalf("Failed to marshal principal public key: %v", err)
+ }
+ }
+ if err := cmd.Execute([]string{"claim", deviceName, "grant", pairingToken, base64.URLEncoding.EncodeToString(deviceKeyWrong)}); !verror.Is(err, verror.ErrNotTrusted.ID) {
+ t.Fatalf("wrongly failed to receive correct error on claim with incorrect device key:%v id:%v", err, verror.ErrorID(err))
+ }
+ stdout.Reset()
+ stderr.Reset()
+ tape.Rewind()
+
// Correct operation.
tape.SetResponses([]interface{}{
nil,
})
- var pairingToken string
- if err := cmd.Execute([]string{"claim", deviceName, "grant", pairingToken}); err != nil {
+ if err := cmd.Execute([]string{"claim", deviceName, "grant", pairingToken, base64.URLEncoding.EncodeToString(deviceKey)}); err != nil {
t.Fatalf("Claim(%s, %s, %s) failed: %v", deviceName, "grant", pairingToken, err)
}
if got, expected := len(tape.Play()), 1; got != expected {
@@ -354,9 +373,7 @@
tape.Rewind()
stdout.Reset()
stderr.Reset()
-
}
-*/
func TestStartCommand(t *testing.T) {
shutdown := initTest()