Skip to content

Commit 27902be

Browse files
committed
Support both API versions
2 parents 3c48a78 + 81f6ea5 commit 27902be

25 files changed

Lines changed: 477 additions & 345 deletions

u2f-chrome-extension/signer.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ function isValidSignRequest(request) {
164164
return false;
165165
}
166166
var hasDefaultChallenge = request.hasOwnProperty('challenge');
167+
167168
var hasAppId = request.hasOwnProperty('appId');
168169
// If the sign challenge array is empty, the global appId is required.
169170
if (!hasAppId && (!signChallenges || !signChallenges.length)) {

u2f-chrome-extension/u2f-api.js

Lines changed: 105 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717
*/
1818
var u2f = u2f || {};
1919

20+
/**
21+
* FIDO U2F Javascript API Version
22+
* @number
23+
*/
24+
var JS_API_VERSION;
2025

2126
/**
2227
* The U2F extension id
@@ -59,13 +64,12 @@ u2f.ErrorCodes = {
5964
* A message type for registration requests
6065
* @typedef {{
6166
* type: u2f.MessageTypes,
62-
* signRequests: Array<u2f.SignRequest>,
63-
* registerRequests: ?Array<u2f.RegisterRequest>,
67+
* appId: ?string,
6468
* timeoutSeconds: ?number,
6569
* requestId: ?number
6670
* }}
6771
*/
68-
u2f.Request;
72+
u2f.U2fRequest;
6973

7074

7175
/**
@@ -76,7 +80,7 @@ u2f.Request;
7680
* requestId: ?number
7781
* }}
7882
*/
79-
u2f.Response;
83+
u2f.U2fResponse;
8084

8185

8286
/**
@@ -88,6 +92,19 @@ u2f.Response;
8892
*/
8993
u2f.Error;
9094

95+
/**
96+
* Data object for a single sign request.
97+
* @typedef {enum {BLUETOOTH_RADIO, BLUETOOTH_LOW_ENERGY, USB, NFC}}
98+
*/
99+
u2f.Transport;
100+
101+
102+
/**
103+
* Data object for a single sign request.
104+
// * @typedef {sequence<u2f.Transport>}
105+
* @typedef {Array<u2f.Transport>}
106+
*/
107+
u2f.Transports;
91108

92109
/**
93110
* Data object for a single sign request.
@@ -116,8 +133,7 @@ u2f.SignResponse;
116133
* Data object for a registration request.
117134
* @typedef {{
118135
* version: string,
119-
* challenge: string,
120-
* appId: string
136+
* challenge: string
121137
* }}
122138
*/
123139
u2f.RegisterRequest;
@@ -126,12 +142,24 @@ u2f.RegisterRequest;
126142
/**
127143
* Data object for a registration response.
128144
* @typedef {{
129-
* registrationData: string,
130-
* clientData: string
145+
* version: string,
146+
* keyHandle: string,
147+
* transports: Transports,
148+
* appId: string
131149
* }}
132150
*/
133151
u2f.RegisterResponse;
134152

153+
/**
154+
* Data object for a registered key.
155+
* @typedef {{
156+
* version: string,
157+
* keyHandle: string,
158+
* transports: ?Transports,
159+
* appId: ?string
160+
* }}
161+
*/
162+
u2f.RegisteredKey;
135163

136164
/**
137165
* Data object for a get API register response.
@@ -144,6 +172,19 @@ u2f.GetJsApiVersionResponse;
144172

145173
// Low level MessagePort API support
146174

175+
/**
176+
* A message type for low-level MessagePort API registration requests
177+
* @typedef {{
178+
* type: u2f.MessageTypes,
179+
* appId: ?string,
180+
* timeoutSeconds: ?number,
181+
* requestId: ?number,
182+
* registerRequests: Array<u2f.RegisterRequest>,
183+
* registeredKeys: Array<u2f.RegisteredKey>
184+
* }}
185+
*/
186+
u2f.U2fRegisterRequest;
187+
147188

148189
/**
149190
* Sets up a MessagePort to the U2F extension using the
@@ -331,7 +372,7 @@ u2f.getPortSingleton_ = function(callback) {
331372

332373
/**
333374
* Handles response messages from the extension.
334-
* @param {MessageEvent.<u2f.Response>} message
375+
* @param {MessageEvent.<u2f.U2fResponse>} message
335376
* @private
336377
*/
337378
u2f.responseHandler_ = function(message) {
@@ -349,47 +390,80 @@ u2f.responseHandler_ = function(message) {
349390

350391
/**
351392
* Dispatches an array of sign requests to available U2F tokens.
352-
* @param {Array<u2f.SignRequest>} signRequests
393+
* @param {string=} appId
394+
* @param {string=} challenge
395+
* @param {Array<u2f.RegisteredKey>} registeredKeys
396+
// * @param {sequence<u2f.RegisteredKey>} registeredKeys
353397
* @param {function((u2f.Error|u2f.SignResponse))} callback
354398
* @param {number=} opt_timeoutSeconds
355399
*/
356-
u2f.sign = function(signRequests, callback, opt_timeoutSeconds) {
400+
u2f.sign = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds) {
401+
console.log("JS_API_VERSION: " + JS_API_VERSION);
357402
u2f.getPortSingleton_(function(port) {
358403
var reqId = ++u2f.reqCounter_;
359404
u2f.callbackMap_[reqId] = callback;
405+
if (JS_API_VERSION == 'undefined' || JS_API_VERSION < 1.1) {
406+
// Adapt request to the 1.0 JS API
407+
for (var i = 0; i < registeredKeys.length; i++) {
408+
registeredKeys[i].challenge = challenge;
409+
registeredKeys[i].appId = appId;
410+
}
411+
}
412+
360413
var req = {
361414
type: u2f.MessageTypes.U2F_SIGN_REQUEST,
362-
signRequests: signRequests,
415+
appId: appId,
416+
challenge: challenge,
417+
registeredKeys: registeredKeys,
363418
timeoutSeconds: (typeof opt_timeoutSeconds !== 'undefined' ?
364419
opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC),
365420
requestId: reqId
366421
};
367422
port.postMessage(req);
368423
});
424+
369425
};
370426

371427

372428
/**
373429
* Dispatches register requests to available U2F tokens. An array of sign
374430
* requests identifies already registered tokens.
431+
* @param {string=} appId
375432
* @param {Array<u2f.RegisterRequest>} registerRequests
376-
* @param {Array<u2f.SignRequest>} signRequests
433+
* @param {Array<u2f.RegisteredKey>} registeredKeys
434+
* @param {sequence<u2f.RegisterRequest>} registerRequests
435+
* @param {sequence<u2f.RegisteredKey>} registeredKeys
377436
* @param {function((u2f.Error|u2f.RegisterResponse))} callback
378437
* @param {number=} opt_timeoutSeconds
379438
*/
380-
u2f.register = function(registerRequests, signRequests,
439+
u2f.register = function(appId, registerRequests, registeredKeys,
381440
callback, opt_timeoutSeconds) {
441+
JS_API_VERSION = 1;
442+
console.log("JS_API_VERSION: " + JS_API_VERSION);
382443
u2f.getPortSingleton_(function(port) {
383444
var reqId = ++u2f.reqCounter_;
384445
u2f.callbackMap_[reqId] = callback;
446+
447+
if (JS_API_VERSION == 'undefined' || JS_API_VERSION < 1.1) {
448+
// Adapt request to the 1.0 JS API
449+
for (var i = 0; i < registerRequests.length; i++) {
450+
registerRequests[i].appId = appId;
451+
}
452+
for (var i = 0; i < registeredKeys.length; i++) {
453+
registeredKeys[i].challenge = registerRequest[0].challenge;
454+
registeredKeys[i].appId = appId;
455+
}
456+
}
457+
385458
var req = {
386-
type: u2f.MessageTypes.U2F_REGISTER_REQUEST,
387-
signRequests: signRequests,
388-
registerRequests: registerRequests,
389-
timeoutSeconds: (typeof opt_timeoutSeconds !== 'undefined' ?
390-
opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC),
391-
requestId: reqId
392-
};
459+
type: u2f.MessageTypes.U2F_REGISTER_REQUEST,
460+
appId: appId,
461+
registerRequests: registerRequests,
462+
registeredKeys: registeredKeys,
463+
timeoutSeconds: (typeof opt_timeoutSeconds !== 'undefined' ?
464+
opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC),
465+
requestId: reqId
466+
};
393467
port.postMessage(req);
394468
});
395469
};
@@ -413,3 +487,13 @@ u2f.getApiVersion = function(callback, opt_timeoutSeconds) {
413487
port.postMessage(req);
414488
});
415489
};
490+
491+
492+
493+
function getApiVersion() {
494+
u2f.getApiVersion(
495+
function (response) {
496+
JS_API_VERSION = response['js_api_version'];
497+
console.log("Extension JS API Version: ", JS_API_VERSION);
498+
});
499+
}

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+
}

0 commit comments

Comments
 (0)