Skip to content

Commit 81f6ea5

Browse files
committed
Proof of concept implementation of the 1.1 JS API
1 parent 8b89258 commit 81f6ea5

27 files changed

Lines changed: 715 additions & 357 deletions

u2f-chrome-extension/signer.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ function handleU2fSignRequest(messageSender, request, sendResponse) {
9595

9696
queuedSignRequest =
9797
validateAndEnqueueSignRequest(
98-
sender, request, 'signRequests', sendErrorResponse,
98+
sender, request, 'registeredKeys', sendErrorResponse,
9999
sendSuccessResponse);
100100
return queuedSignRequest;
101101
}
@@ -213,14 +213,14 @@ function isValidSignRequest(request, signChallengesName) {
213213
if (!request.hasOwnProperty(signChallengesName))
214214
return false;
215215
var signChallenges = request[signChallengesName];
216-
var hasDefaultChallenge = request.hasOwnProperty('challenge');
217216
var hasAppId = request.hasOwnProperty('appId');
218217
// If the sign challenge array is empty, the global appId is required.
219218
if (!hasAppId && (!signChallenges || !signChallenges.length)) {
220219
return false;
221220
}
222-
return isValidSignChallengeArray(signChallenges, hasDefaultChallenge,
223-
!hasAppId);
221+
222+
return isValidSignChallengeArray(signChallenges, false /* challengeRequired */, !hasAppId);
223+
224224
}
225225

226226
/**

u2f-chrome-extension/u2f-api.js

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,12 @@ u2f.ErrorCodes = {
5757
* A message type for registration requests
5858
* @typedef {{
5959
* type: u2f.MessageTypes,
60-
* signRequests: Array<u2f.SignRequest>,
61-
* registerRequests: ?Array<u2f.RegisterRequest>,
60+
* appId: ?string,
6261
* timeoutSeconds: ?number,
6362
* requestId: ?number
6463
* }}
6564
*/
66-
u2f.Request;
65+
u2f.U2fRequest;
6766

6867

6968
/**
@@ -74,7 +73,7 @@ u2f.Request;
7473
* requestId: ?number
7574
* }}
7675
*/
77-
u2f.Response;
76+
u2f.U2fResponse;
7877

7978

8079
/**
@@ -86,6 +85,19 @@ u2f.Response;
8685
*/
8786
u2f.Error;
8887

88+
/**
89+
* Data object for a single sign request.
90+
* @typedef {enum {BLUETOOTH_RADIO, BLUETOOTH_LOW_ENERGY, USB, NFC}}
91+
*/
92+
u2f.Transport;
93+
94+
95+
/**
96+
* Data object for a single sign request.
97+
// * @typedef {sequence<u2f.Transport>}
98+
* @typedef {Array<u2f.Transport>}
99+
*/
100+
u2f.Transports;
89101

90102
/**
91103
* Data object for a single sign request.
@@ -114,8 +126,7 @@ u2f.SignResponse;
114126
* Data object for a registration request.
115127
* @typedef {{
116128
* version: string,
117-
* challenge: string,
118-
* appId: string
129+
* challenge: string
119130
* }}
120131
*/
121132
u2f.RegisterRequest;
@@ -124,15 +135,40 @@ u2f.RegisterRequest;
124135
/**
125136
* Data object for a registration response.
126137
* @typedef {{
127-
* registrationData: string,
128-
* clientData: string
138+
* version: string,
139+
* keyHandle: string,
140+
* transports: Transports,
141+
* appId: string
129142
* }}
130143
*/
131144
u2f.RegisterResponse;
132145

146+
/**
147+
* Data object for a registered key.
148+
* @typedef {{
149+
* version: string,
150+
* keyHandle: string,
151+
* transports: ?Transports,
152+
* appId: ?string
153+
* }}
154+
*/
155+
u2f.RegisteredKey;
133156

134157
// Low level MessagePort API support
135158

159+
/**
160+
* A message type for low-level MessagePort API registration requests
161+
* @typedef {{
162+
* type: u2f.MessageTypes,
163+
* appId: ?string,
164+
* timeoutSeconds: ?number,
165+
* requestId: ?number,
166+
* registerRequests: Array<u2f.RegisterRequest>,
167+
* registeredKeys: Array<u2f.RegisteredKey>
168+
* }}
169+
*/
170+
u2f.U2fRegisterRequest;
171+
136172

137173
/**
138174
* Sets up a MessagePort to the U2F extension using the
@@ -320,7 +356,7 @@ u2f.getPortSingleton_ = function(callback) {
320356

321357
/**
322358
* Handles response messages from the extension.
323-
* @param {MessageEvent.<u2f.Response>} message
359+
* @param {MessageEvent.<u2f.U2fResponse>} message
324360
* @private
325361
*/
326362
u2f.responseHandler_ = function(message) {
@@ -338,17 +374,22 @@ u2f.responseHandler_ = function(message) {
338374

339375
/**
340376
* Dispatches an array of sign requests to available U2F tokens.
341-
* @param {Array<u2f.SignRequest>} signRequests
377+
* @param {string=} appId
378+
* @param {string=} challenge
379+
* @param {Array<u2f.RegisteredKey>} registeredKeys
380+
// * @param {sequence<u2f.RegisteredKey>} registeredKeys
342381
* @param {function((u2f.Error|u2f.SignResponse))} callback
343382
* @param {number=} opt_timeoutSeconds
344383
*/
345-
u2f.sign = function(signRequests, callback, opt_timeoutSeconds) {
384+
u2f.sign = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds) {
346385
u2f.getPortSingleton_(function(port) {
347386
var reqId = ++u2f.reqCounter_;
348387
u2f.callbackMap_[reqId] = callback;
349388
var req = {
350389
type: u2f.MessageTypes.U2F_SIGN_REQUEST,
351-
signRequests: signRequests,
390+
appId: appId,
391+
challenge: challenge,
392+
registeredKeys: registeredKeys,
352393
timeoutSeconds: (typeof opt_timeoutSeconds !== 'undefined' ?
353394
opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC),
354395
requestId: reqId
@@ -361,20 +402,24 @@ u2f.sign = function(signRequests, callback, opt_timeoutSeconds) {
361402
/**
362403
* Dispatches register requests to available U2F tokens. An array of sign
363404
* requests identifies already registered tokens.
405+
* @param {string=} appId
364406
* @param {Array<u2f.RegisterRequest>} registerRequests
365-
* @param {Array<u2f.SignRequest>} signRequests
407+
* @param {Array<u2f.RegisteredKey>} registeredKeys
408+
// * @param {sequence<u2f.RegisterRequest>} registerRequests
409+
// * @param {sequence<u2f.RegisteredKey>} registeredKeys
366410
* @param {function((u2f.Error|u2f.RegisterResponse))} callback
367411
* @param {number=} opt_timeoutSeconds
368412
*/
369-
u2f.register = function(registerRequests, signRequests,
413+
u2f.register = function(appId, registerRequests, registeredKeys,
370414
callback, opt_timeoutSeconds) {
371415
u2f.getPortSingleton_(function(port) {
372416
var reqId = ++u2f.reqCounter_;
373417
u2f.callbackMap_[reqId] = callback;
374418
var req = {
375419
type: u2f.MessageTypes.U2F_REGISTER_REQUEST,
376-
signRequests: signRequests,
420+
appId: appId,
377421
registerRequests: registerRequests,
422+
registeredKeys: registeredKeys,
378423
timeoutSeconds: (typeof opt_timeoutSeconds !== 'undefined' ?
379424
opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC),
380425
requestId: reqId

u2f-gae-demo/src/com/google/u2f/gaedemo/servlets/BeginEnrollServlet.java

Lines changed: 26 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
package com.google.u2f.gaedemo.servlets;
88

99
import java.io.IOException;
10-
import java.util.List;
1110

1211
import javax.servlet.ServletException;
1312
import javax.servlet.http.HttpServlet;
@@ -17,15 +16,14 @@
1716
import com.google.appengine.api.users.User;
1817
import com.google.appengine.api.users.UserService;
1918
import com.google.appengine.api.users.UserServiceFactory;
20-
import com.google.common.collect.ImmutableList;
2119
import com.google.gson.JsonArray;
2220
import com.google.gson.JsonObject;
2321
import com.google.inject.Inject;
2422
import com.google.inject.Singleton;
2523
import com.google.u2f.U2FException;
2624
import com.google.u2f.server.U2FServer;
2725
import com.google.u2f.server.messages.RegistrationRequest;
28-
import com.google.u2f.server.messages.SignRequest;
26+
import com.google.u2f.server.messages.U2fSignRequest;
2927

3028
@SuppressWarnings("serial")
3129
@Singleton
@@ -41,47 +39,34 @@ public BeginEnrollServlet(U2FServer u2fServer) {
4139

4240
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
4341
User user = userService.getCurrentUser();
44-
45-
boolean singleEnrollment = !Boolean.valueOf(req.getParameter("reregistration"));
46-
42+
boolean allowReregistration = Boolean.valueOf(req.getParameter("reregistration"));
4743
RegistrationRequest registrationRequest;
48-
List<SignRequest> signRequests;
44+
U2fSignRequest signRequest;
45+
String appId = (req.isSecure() ? "https://" : "http://") + req.getHeader("Host");
46+
4947
try {
50-
registrationRequest = u2fServer.getRegistrationRequest(user.getUserId(),
51-
(req.isSecure() ? "https://" : "http://") + req.getHeader("Host"));
52-
53-
if (singleEnrollment) {
54-
signRequests = u2fServer.getSignRequest(user.getUserId(),
55-
(req.isSecure() ? "https://" : "http://") + req.getHeader("Host"));
56-
} else {
57-
signRequests = ImmutableList.of();
58-
}
59-
} catch (U2FException e) {
60-
throw new ServletException("couldn't get registration request", e);
48+
registrationRequest = u2fServer.getRegistrationRequest(user.getUserId(), appId);
49+
signRequest = u2fServer.getSignRequest(user.getUserId(), appId);
50+
} catch (U2FException e) {
51+
throw new ServletException("couldn't get registration request", e);
52+
}
53+
54+
JsonObject result = new JsonObject();
55+
result.addProperty("appId", registrationRequest.getAppId());
56+
result.addProperty("sessionId", registrationRequest.getSessionId());
57+
58+
JsonObject registerRequests = new JsonObject();
59+
registerRequests.addProperty("challenge", registrationRequest.getChallenge());
60+
registerRequests.addProperty("version", registrationRequest.getVersion());
61+
result.add("registerRequests", registerRequests);
62+
63+
if(allowReregistration) {
64+
result.add("registeredKeys", new JsonArray());
65+
} else {
66+
result.add("registeredKeys", signRequest.getRegisteredKeysAsJson());
6167
}
62-
63-
JsonArray signData = new JsonArray();
64-
65-
for (SignRequest signRequest : signRequests) {
66-
JsonObject signServerData = new JsonObject();
67-
signServerData.addProperty("appId", signRequest.getAppId());
68-
signServerData.addProperty("challenge", signRequest.getChallenge());
69-
signServerData.addProperty("version", signRequest.getVersion());
70-
signServerData.addProperty("keyHandle", signRequest.getKeyHandle());
71-
signData.add(signServerData);
72-
}
73-
74-
JsonObject enrollData = new JsonObject();
75-
enrollData.addProperty("appId", registrationRequest.getAppId());
76-
enrollData.addProperty("challenge", registrationRequest.getChallenge());
77-
enrollData.addProperty("version", registrationRequest.getVersion());
78-
79-
JsonObject result = new JsonObject();
80-
result.add("enroll_data", enrollData);
81-
result.add("sign_data", signData);
82-
result.addProperty("sessionId", registrationRequest.getSessionId());
83-
68+
8469
resp.setContentType("application/json");
8570
resp.getWriter().println(result.toString());
8671
}
87-
}
72+
}

u2f-gae-demo/src/com/google/u2f/gaedemo/servlets/BeginSignServlet.java

Lines changed: 29 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
package com.google.u2f.gaedemo.servlets;
88

99
import java.io.IOException;
10-
import java.util.List;
1110

1211
import javax.servlet.ServletException;
1312
import javax.servlet.http.HttpServlet;
@@ -17,50 +16,42 @@
1716
import com.google.appengine.api.users.User;
1817
import com.google.appengine.api.users.UserService;
1918
import com.google.appengine.api.users.UserServiceFactory;
20-
import com.google.gson.JsonArray;
2119
import com.google.gson.JsonObject;
2220
import com.google.inject.Inject;
2321
import com.google.inject.Singleton;
2422
import com.google.u2f.U2FException;
2523
import com.google.u2f.server.U2FServer;
26-
import com.google.u2f.server.messages.SignRequest;
24+
import com.google.u2f.server.messages.U2fSignRequest;
2725

2826
@SuppressWarnings("serial")
2927
@Singleton
3028
public class BeginSignServlet extends HttpServlet {
31-
32-
private final UserService userService = UserServiceFactory.getUserService();
33-
private final U2FServer u2fServer;
34-
35-
@Inject
36-
public BeginSignServlet(U2FServer u2fServer) {
37-
this.u2fServer = u2fServer;
38-
}
39-
40-
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
41-
User user = userService.getCurrentUser();
42-
43-
List<SignRequest> signRequests;
44-
try {
45-
signRequests = u2fServer.getSignRequest(user.getUserId(),
46-
(req.isSecure() ? "https://" : "http://") + req.getHeader("Host"));
47-
} catch (U2FException e) {
48-
throw new ServletException("couldn't get registration request", e);
49-
}
50-
51-
JsonArray result = new JsonArray();
52-
53-
for (SignRequest signRequest : signRequests) {
54-
JsonObject signServerData = new JsonObject();
55-
signServerData.addProperty("appId", signRequest.getAppId());
56-
signServerData.addProperty("challenge", signRequest.getChallenge());
57-
signServerData.addProperty("version", signRequest.getVersion());
58-
signServerData.addProperty("keyHandle", signRequest.getKeyHandle());
59-
signServerData.addProperty("sessionId", signRequest.getSessionId());
60-
result.add(signServerData);
61-
}
62-
63-
resp.setContentType("application/json");
64-
resp.getWriter().println(result.toString());
29+
30+
private final UserService userService = UserServiceFactory.getUserService();
31+
private final U2FServer u2fServer;
32+
33+
@Inject
34+
public BeginSignServlet(U2FServer u2fServer) {
35+
this.u2fServer = u2fServer;
36+
}
37+
38+
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
39+
User user = userService.getCurrentUser();
40+
41+
U2fSignRequest signRequest;
42+
String appId = (req.isSecure() ? "https://" : "http://") + req.getHeader("Host");
43+
try {
44+
signRequest = u2fServer.getSignRequest(user.getUserId(), appId);
45+
} catch (U2FException e) {
46+
throw new ServletException("couldn't get registration request", e);
6547
}
66-
}
48+
49+
JsonObject result = new JsonObject();
50+
result.addProperty("challenge", signRequest.getChallenge());
51+
result.addProperty("appId", appId);
52+
result.add("registeredKeys", signRequest.getRegisteredKeysAsJson());
53+
54+
resp.setContentType("application/json");
55+
resp.getWriter().println(result.toString());
56+
}
57+
}

0 commit comments

Comments
 (0)