git » remoteu2f » commit 86fff29

Adjust to the new U2F API

author Alberto Bertogli
2017-02-23 20:27:24 UTC
committer Alberto Bertogli
2017-02-23 21:33:20 UTC
parent c4a5ad3db2556ecd921e3bc4967a1bc5d189f9e1

Adjust to the new U2F API

This patch adjusts the various components to the new U2F API, and the
corresponding github.com/tstranex/u2f backwards-incompatible changes.

- The RPC protocol doesn't need change, as we continue to pass opaque
  JSON-serialized requests.
- The proxy's javascript is adjusted, it now does a json unmarshalling
  of the request, and makes the appropriate call to the new API.
- The client library (used mainly by the CLI) got some adjustments for
  the new tstranex/u2f's changes, including API changes.

The configuration format remains unchanged and is backwards compatible,
there's no need to re-register keys.

Unfortunately, only the new CLI will work with the new proxy, and
vice-versa, due to the javascript changes. This could have been avoided
with a significant amount of work but IMO it was not worth the
additional complexity.

internal/client/config.go +14 -3
internal/client/grpc.go +17 -38
internal/proto/remoteu2f.pb.go +4 -0
internal/proto/remoteu2f.proto +4 -0
remoteu2f-cli/main.go +1 -1
remoteu2f-proxy/embedded_data.go +6 -2
remoteu2f-proxy/to_embed/authenticate.html +3 -1
remoteu2f-proxy/to_embed/register.html +3 -1

diff --git a/internal/client/config.go b/internal/client/config.go
index 372b763..51832a0 100644
--- a/internal/client/config.go
+++ b/internal/client/config.go
@@ -8,6 +8,8 @@ import (
 	"io/ioutil"
 	"math/big"
 	"os"
+
+	"github.com/tstranex/u2f"
 )
 
 // Default configuration directory, relative to the user's home directory.
@@ -109,9 +111,18 @@ func (c *Config) NewBackupCodes() error {
 	return nil
 }
 
-func (c *Config) RegistrationValues() [][]byte {
-	var rs [][]byte
-	for _, r := range c.Registrations {
+// RegistrationValues returns the registrations in the configuration file, as
+// a slice of u2f.Registration structures (which is a friendly form for the
+// client functions).
+func (c *Config) RegistrationValues() []u2f.Registration {
+	var rs []u2f.Registration
+	for _, binr := range c.Registrations {
+		r := u2f.Registration{}
+		if err := r.UnmarshalBinary(binr); err != nil {
+			// TODO - Should we account for this?
+			// Backwards-incompatible changes could cause this.
+			panic(err)
+		}
 		rs = append(rs, r)
 	}
 	return rs
diff --git a/internal/client/grpc.go b/internal/client/grpc.go
index e89b1cb..ea0ad90 100644
--- a/internal/client/grpc.go
+++ b/internal/client/grpc.go
@@ -66,15 +66,16 @@ type PendingRegister struct {
 	challenge *u2f.Challenge
 }
 
-func (c *RemoteU2FClient) PrepareRegister(msg, appID string) (*PendingRegister, error) {
-	var trustedFacets = []string{appID}
+func (c *RemoteU2FClient) PrepareRegister(
+	msg, appID string, regs []u2f.Registration) (*PendingRegister, error) {
 
-	challenge, err := u2f.NewChallenge(appID, trustedFacets)
+	challenge, err := u2f.NewChallenge(appID, []string{appID})
 	if err != nil {
 		return nil, fmt.Errorf("u2f.NewChallenge error: %v", err)
 	}
 
-	j, err := json.Marshal(challenge.RegisterRequest())
+	req := u2f.NewWebRegisterRequest(challenge, regs)
+	j, err := json.Marshal(req)
 	if err != nil {
 		return nil, fmt.Errorf("json marshalling error: %v", err)
 	}
@@ -124,41 +125,21 @@ type PendingAuth struct {
 	Key *pb.Url
 
 	// Registrations we sent auth requests for.
-	regs []*u2f.Registration
+	regs []u2f.Registration
 
-	// Challenges matching each registration.
-	challenges []*u2f.Challenge
+	// Challenge.
+	challenge *u2f.Challenge
 }
 
-func (c *RemoteU2FClient) PrepareAuthentication(msg, appID string, marshalledRegs [][]byte) (
-	*PendingAuth, error) {
-
-	var trustedFacets = []string{appID}
+func (c *RemoteU2FClient) PrepareAuthentication(
+	msg, appID string, regs []u2f.Registration) (*PendingAuth, error) {
 
-	pa := &PendingAuth{}
-	signReqs := []*u2f.SignRequest{}
-
-	// Generate one signature request for each registration.
-	for _, mreg := range marshalledRegs {
-		reg := &u2f.Registration{}
-		err := reg.UnmarshalBinary(mreg)
-		if err != nil {
-			return nil, fmt.Errorf("u2f.ParseRegistration: %v\n", err)
-		}
-
-		// Can/should we reuse the challenge for all the registrations?
-		challenge, err := u2f.NewChallenge(appID, trustedFacets)
-		if err != nil {
-			return nil, fmt.Errorf("u2f.NewChallenge error: %v", err)
-		}
-
-		sr := challenge.SignRequest(*reg)
-		signReqs = append(signReqs, sr)
-
-		pa.challenges = append(pa.challenges, challenge)
-		pa.regs = append(pa.regs, reg)
+	challenge, err := u2f.NewChallenge(appID, []string{appID})
+	if err != nil {
+		return nil, fmt.Errorf("u2f.NewChallenge error: %v", err)
 	}
 
+	signReqs := challenge.SignRequest(regs)
 	j, err := json.Marshal(signReqs)
 	if err != nil {
 		return nil, fmt.Errorf("json marshalling error: %v", err)
@@ -173,9 +154,7 @@ func (c *RemoteU2FClient) PrepareAuthentication(msg, appID string, marshalledReg
 		return nil, fmt.Errorf("error preparing: %v", err)
 	}
 
-	pa.Key = key
-
-	return pa, nil
+	return &PendingAuth{key, regs, challenge}, nil
 }
 
 func (c *RemoteU2FClient) CompleteAuthentication(pa *PendingAuth) error {
@@ -192,9 +171,9 @@ func (c *RemoteU2FClient) CompleteAuthentication(pa *PendingAuth) error {
 		return fmt.Errorf("invalid response: %s", err)
 	}
 
-	for i, reg := range pa.regs {
+	for _, reg := range pa.regs {
 		// TODO: support counters.
-		_, err = reg.Authenticate(signResp, *pa.challenges[i], 0)
+		_, err = reg.Authenticate(signResp, *pa.challenge, 0)
 		if err == nil {
 			return nil
 		}
diff --git a/internal/proto/remoteu2f.pb.go b/internal/proto/remoteu2f.pb.go
index eca3b5a..e34fd6d 100644
--- a/internal/proto/remoteu2f.pb.go
+++ b/internal/proto/remoteu2f.pb.go
@@ -85,6 +85,10 @@ func (*Url) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
 // Prepare an operation.
 type Prepare struct {
 	// Generic json content to return.
+	// This is a marshalled version of u2f.WebRegisterRequest or
+	// u2f.WebSignRequest, depending on the operation.
+	// We don't need to access this directly in the proxy, so we keep this
+	// opaque and decode it again at javascript.
 	Json []byte `protobuf:"bytes,1,opt,name=json,proto3" json:"json,omitempty"`
 	// User-readable message to show on the web page.
 	Msg   string        `protobuf:"bytes,2,opt,name=msg" json:"msg,omitempty"`
diff --git a/internal/proto/remoteu2f.proto b/internal/proto/remoteu2f.proto
index 16be073..3519803 100644
--- a/internal/proto/remoteu2f.proto
+++ b/internal/proto/remoteu2f.proto
@@ -16,6 +16,10 @@ message Url {
 // Prepare an operation.
 message Prepare {
 	// Generic json content to return.
+	// This is a marshalled version of u2f.WebRegisterRequest or
+	// u2f.WebSignRequest, depending on the operation.
+	// We don't need to access this directly in the proxy, so we keep this
+	// opaque and decode it again at javascript.
 	bytes json = 1;
 
 	// User-readable message to show on the web page.
diff --git a/remoteu2f-cli/main.go b/remoteu2f-cli/main.go
index 3c4a156..0eefe56 100644
--- a/remoteu2f-cli/main.go
+++ b/remoteu2f-cli/main.go
@@ -244,7 +244,7 @@ func Register(ctx *cli.Context) {
 	user, hostname := mustUserInfo()
 	msg := fmt.Sprintf("%s@%s", user, hostname)
 
-	pr, err := c.PrepareRegister(msg, conf.AppID)
+	pr, err := c.PrepareRegister(msg, conf.AppID, conf.RegistrationValues())
 	if err != nil {
 		fatalf("Error preparing registration: %v\n", err)
 	}
diff --git a/remoteu2f-proxy/embedded_data.go b/remoteu2f-proxy/embedded_data.go
index bb5bc73..4417fec 100644
--- a/remoteu2f-proxy/embedded_data.go
+++ b/remoteu2f-proxy/embedded_data.go
@@ -42,7 +42,9 @@ const authenticate_html = `<!DOCTYPE html>
     <script type="text/javascript" src="remoteu2f.js"></script>
     <script type="text/javascript" src="u2f_api.js"></script>
     <script>
-      u2f.sign({{.Request}}, handleKeyResponse, 2*60);
+        req = {{.Request}};
+        u2f.sign(req.appId, req.challenge, req.registeredKeys,
+                 handleKeyResponse, 2*60);
     </script>
 
   </body>
@@ -86,7 +88,9 @@ const register_html = `<!DOCTYPE html>
     <script type="text/javascript" src="remoteu2f.js"></script>
     <script type="text/javascript" src="u2f_api.js"></script>
     <script>
-      u2f.register([{{.Request}}], [], handleKeyResponse, 2*60)
+        req = {{.Request}};
+        u2f.register(req.appId, req.registerRequests, req.registeredKeys,
+              handleKeyResponse, 2*60)
     </script>
 
   </body>
diff --git a/remoteu2f-proxy/to_embed/authenticate.html b/remoteu2f-proxy/to_embed/authenticate.html
index e462795..385bf59 100644
--- a/remoteu2f-proxy/to_embed/authenticate.html
+++ b/remoteu2f-proxy/to_embed/authenticate.html
@@ -33,7 +33,9 @@
     <script type="text/javascript" src="remoteu2f.js"></script>
     <script type="text/javascript" src="u2f_api.js"></script>
     <script>
-      u2f.sign({{.Request}}, handleKeyResponse, 2*60);
+        req = {{.Request}};
+        u2f.sign(req.appId, req.challenge, req.registeredKeys,
+                 handleKeyResponse, 2*60);
     </script>
 
   </body>
diff --git a/remoteu2f-proxy/to_embed/register.html b/remoteu2f-proxy/to_embed/register.html
index 9b70789..8f5f968 100644
--- a/remoteu2f-proxy/to_embed/register.html
+++ b/remoteu2f-proxy/to_embed/register.html
@@ -31,7 +31,9 @@
     <script type="text/javascript" src="remoteu2f.js"></script>
     <script type="text/javascript" src="u2f_api.js"></script>
     <script>
-      u2f.register([{{.Request}}], [], handleKeyResponse, 2*60)
+        req = {{.Request}};
+        u2f.register(req.appId, req.registerRequests, req.registeredKeys,
+              handleKeyResponse, 2*60)
     </script>
 
   </body>