OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * @fileoverview Does common handling for requests coming from web pages and | 6 * @fileoverview Does common handling for requests coming from web pages and |
7 * routes them to the provided handler. | 7 * routes them to the provided handler. |
8 */ | 8 */ |
9 | 9 |
10 /** | 10 /** |
11 * FIDO U2F Javascript API Version | 11 * FIDO U2F Javascript API Version |
12 * @const | 12 * @const |
13 * @type {number} | 13 * @type {number} |
14 */ | 14 */ |
15 var JS_API_VERSION = 1.1; | 15 var JS_API_VERSION = 1.1; |
16 | 16 |
17 /** | 17 /** |
18 * Gets the scheme + origin from a web url. | 18 * Gets the scheme + origin from a web url. |
19 * @param {string} url Input url | 19 * @param {string} url Input url |
20 * @return {?string} Scheme and origin part if url parses | 20 * @return {?string} Scheme and origin part if url parses |
21 */ | 21 */ |
22 function getOriginFromUrl(url) { | 22 function getOriginFromUrl(url) { |
23 var re = new RegExp('^(https?://)[^/]*/?'); | 23 var re = new RegExp('^(https?://)[^/]*/?'); |
24 var originarray = re.exec(url); | 24 var originarray = re.exec(url); |
25 if (originarray == null) return originarray; | 25 if (originarray == null) |
| 26 return originarray; |
26 var origin = originarray[0]; | 27 var origin = originarray[0]; |
27 while (origin.charAt(origin.length - 1) == '/') { | 28 while (origin.charAt(origin.length - 1) == '/') { |
28 origin = origin.substring(0, origin.length - 1); | 29 origin = origin.substring(0, origin.length - 1); |
29 } | 30 } |
30 if (origin == 'http:' || origin == 'https:') | 31 if (origin == 'http:' || origin == 'https:') |
31 return null; | 32 return null; |
32 return origin; | 33 return origin; |
33 } | 34 } |
34 | 35 |
35 /** | 36 /** |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
88 | 89 |
89 /** | 90 /** |
90 * Returns whether the array of SignChallenges appears to be valid. | 91 * Returns whether the array of SignChallenges appears to be valid. |
91 * @param {Array<SignChallenge>} signChallenges The array of sign challenges. | 92 * @param {Array<SignChallenge>} signChallenges The array of sign challenges. |
92 * @param {boolean} challengeValueRequired Whether each challenge object | 93 * @param {boolean} challengeValueRequired Whether each challenge object |
93 * requires a challenge value. | 94 * requires a challenge value. |
94 * @param {boolean} appIdRequired Whether the appId property is required on | 95 * @param {boolean} appIdRequired Whether the appId property is required on |
95 * each challenge. | 96 * each challenge. |
96 * @return {boolean} Whether the array appears valid. | 97 * @return {boolean} Whether the array appears valid. |
97 */ | 98 */ |
98 function isValidSignChallengeArray(signChallenges, challengeValueRequired, | 99 function isValidSignChallengeArray( |
99 appIdRequired) { | 100 signChallenges, challengeValueRequired, appIdRequired) { |
100 for (var i = 0; i < signChallenges.length; i++) { | 101 for (var i = 0; i < signChallenges.length; i++) { |
101 var incomingChallenge = signChallenges[i]; | 102 var incomingChallenge = signChallenges[i]; |
102 if (challengeValueRequired && | 103 if (challengeValueRequired && |
103 !incomingChallenge.hasOwnProperty('challenge')) | 104 !incomingChallenge.hasOwnProperty('challenge')) |
104 return false; | 105 return false; |
105 if (!isValidRegisteredKey(incomingChallenge, appIdRequired)) { | 106 if (!isValidRegisteredKey(incomingChallenge, appIdRequired)) { |
106 return false; | 107 return false; |
107 } | 108 } |
108 } | 109 } |
109 return true; | 110 return true; |
110 } | 111 } |
111 | 112 |
112 /** | 113 /** |
113 * @param {Object} request Request object | 114 * @param {Object} request Request object |
114 * @param {MessageSender} sender Sender frame | 115 * @param {MessageSender} sender Sender frame |
115 * @param {Function} sendResponse Response callback | 116 * @param {Function} sendResponse Response callback |
116 * @return {?Closeable} Optional handler object that should be closed when port | 117 * @return {?Closeable} Optional handler object that should be closed when port |
117 * closes | 118 * closes |
118 */ | 119 */ |
119 function handleWebPageRequest(request, sender, sendResponse) { | 120 function handleWebPageRequest(request, sender, sendResponse) { |
120 switch (request.type) { | 121 switch (request.type) { |
121 case MessageTypes.U2F_REGISTER_REQUEST: | 122 case MessageTypes.U2F_REGISTER_REQUEST: |
122 return handleU2fEnrollRequest(sender, request, sendResponse); | 123 return handleU2fEnrollRequest(sender, request, sendResponse); |
123 | 124 |
124 case MessageTypes.U2F_SIGN_REQUEST: | 125 case MessageTypes.U2F_SIGN_REQUEST: |
125 return handleU2fSignRequest(sender, request, sendResponse); | 126 return handleU2fSignRequest(sender, request, sendResponse); |
126 | 127 |
127 case MessageTypes.U2F_GET_API_VERSION_REQUEST: | 128 case MessageTypes.U2F_GET_API_VERSION_REQUEST: |
128 sendResponse( | 129 sendResponse(makeU2fGetApiVersionResponse( |
129 makeU2fGetApiVersionResponse(request, JS_API_VERSION, | 130 request, JS_API_VERSION, MessageTypes.U2F_GET_API_VERSION_RESPONSE)); |
130 MessageTypes.U2F_GET_API_VERSION_RESPONSE)); | |
131 return null; | 131 return null; |
132 | 132 |
133 default: | 133 default: |
134 sendResponse( | 134 sendResponse(makeU2fErrorResponse( |
135 makeU2fErrorResponse(request, ErrorCodes.BAD_REQUEST, undefined, | 135 request, ErrorCodes.BAD_REQUEST, undefined, |
136 MessageTypes.U2F_REGISTER_RESPONSE)); | 136 MessageTypes.U2F_REGISTER_RESPONSE)); |
137 return null; | 137 return null; |
138 } | 138 } |
139 } | 139 } |
140 | 140 |
141 /** | 141 /** |
142 * Makes a response to a request. | 142 * Makes a response to a request. |
143 * @param {Object} request The request to make a response to. | 143 * @param {Object} request The request to make a response to. |
144 * @param {string} responseSuffix How to name the response's type. | 144 * @param {string} responseSuffix How to name the response's type. |
145 * @param {string=} opt_defaultType The default response type, if none is | 145 * @param {string=} opt_defaultType The default response type, if none is |
146 * present in the request. | 146 * present in the request. |
147 * @return {Object} The response object. | 147 * @return {Object} The response object. |
148 */ | 148 */ |
149 function makeResponseForRequest(request, responseSuffix, opt_defaultType) { | 149 function makeResponseForRequest(request, responseSuffix, opt_defaultType) { |
150 var type; | 150 var type; |
151 if (request && request.type) { | 151 if (request && request.type) { |
152 type = request.type.replace(/_request$/, responseSuffix); | 152 type = request.type.replace(/_request$/, responseSuffix); |
153 } else { | 153 } else { |
154 type = opt_defaultType; | 154 type = opt_defaultType; |
155 } | 155 } |
156 var reply = { 'type': type }; | 156 var reply = {'type': type}; |
157 if (request && request.requestId) { | 157 if (request && request.requestId) { |
158 reply.requestId = request.requestId; | 158 reply.requestId = request.requestId; |
159 } | 159 } |
160 return reply; | 160 return reply; |
161 } | 161 } |
162 | 162 |
163 /** | 163 /** |
164 * Makes a response to a U2F request with an error code. | 164 * Makes a response to a U2F request with an error code. |
165 * @param {Object} request The request to make a response to. | 165 * @param {Object} request The request to make a response to. |
166 * @param {ErrorCodes} code The error code to return. | 166 * @param {ErrorCodes} code The error code to return. |
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
292 * @param {!string} type A string representing the "type" of this browser data | 292 * @param {!string} type A string representing the "type" of this browser data |
293 * object. | 293 * object. |
294 * @param {!string} serverChallenge The server's challenge, as a base64- | 294 * @param {!string} serverChallenge The server's challenge, as a base64- |
295 * encoded string. | 295 * encoded string. |
296 * @param {!string} origin The server's origin, as seen by the browser. | 296 * @param {!string} origin The server's origin, as seen by the browser. |
297 * @param {Object|string|undefined} opt_tlsChannelId TLS Channel Id | 297 * @param {Object|string|undefined} opt_tlsChannelId TLS Channel Id |
298 * @return {string} A string representation of the browser data object. | 298 * @return {string} A string representation of the browser data object. |
299 */ | 299 */ |
300 function makeBrowserData(type, serverChallenge, origin, opt_tlsChannelId) { | 300 function makeBrowserData(type, serverChallenge, origin, opt_tlsChannelId) { |
301 var browserData = { | 301 var browserData = { |
302 'typ' : type, | 302 'typ': type, |
303 'challenge' : serverChallenge, | 303 'challenge': serverChallenge, |
304 'origin' : origin | 304 'origin': origin |
305 }; | 305 }; |
306 if (BROWSER_SUPPORTS_TLS_CHANNEL_ID) { | 306 if (BROWSER_SUPPORTS_TLS_CHANNEL_ID) { |
307 browserData['cid_pubkey'] = tlsChannelIdValue(opt_tlsChannelId); | 307 browserData['cid_pubkey'] = tlsChannelIdValue(opt_tlsChannelId); |
308 } | 308 } |
309 return JSON.stringify(browserData); | 309 return JSON.stringify(browserData); |
310 } | 310 } |
311 | 311 |
312 /** | 312 /** |
313 * Creates a browser data object for an enroll request with the given values. | 313 * Creates a browser data object for an enroll request with the given values. |
314 * @param {!string} serverChallenge The server's challenge, as a base64- | 314 * @param {!string} serverChallenge The server's challenge, as a base64- |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
357 * @param {string|undefined} opt_defaultChallenge A default sign challenge | 357 * @param {string|undefined} opt_defaultChallenge A default sign challenge |
358 * value, if a request does not provide one. | 358 * value, if a request does not provide one. |
359 * @param {string=} opt_defaultAppId The app id to use for each challenge, if | 359 * @param {string=} opt_defaultAppId The app id to use for each challenge, if |
360 * the challenge contains none. | 360 * the challenge contains none. |
361 * @param {function(string, string): string=} opt_challengeHashFunction | 361 * @param {function(string, string): string=} opt_challengeHashFunction |
362 * A function that produces, from a key handle and a raw challenge, a hash | 362 * A function that produces, from a key handle and a raw challenge, a hash |
363 * of the raw challenge. If none is provided, a default hash function is | 363 * of the raw challenge. If none is provided, a default hash function is |
364 * used. | 364 * used. |
365 * @return {!Array<SignHelperChallenge>} The sign challenges, encoded. | 365 * @return {!Array<SignHelperChallenge>} The sign challenges, encoded. |
366 */ | 366 */ |
367 function encodeSignChallenges(signChallenges, opt_defaultChallenge, | 367 function encodeSignChallenges( |
368 opt_defaultAppId, opt_challengeHashFunction) { | 368 signChallenges, opt_defaultChallenge, opt_defaultAppId, |
| 369 opt_challengeHashFunction) { |
369 function encodedSha256(keyHandle, challenge) { | 370 function encodedSha256(keyHandle, challenge) { |
370 return B64_encode(sha256HashOfString(challenge)); | 371 return B64_encode(sha256HashOfString(challenge)); |
371 } | 372 } |
372 var challengeHashFn = opt_challengeHashFunction || encodedSha256; | 373 var challengeHashFn = opt_challengeHashFunction || encodedSha256; |
373 var encodedSignChallenges = []; | 374 var encodedSignChallenges = []; |
374 if (signChallenges) { | 375 if (signChallenges) { |
375 for (var i = 0; i < signChallenges.length; i++) { | 376 for (var i = 0; i < signChallenges.length; i++) { |
376 var challenge = signChallenges[i]; | 377 var challenge = signChallenges[i]; |
377 var keyHandle = challenge['keyHandle']; | 378 var keyHandle = challenge['keyHandle']; |
378 var challengeValue; | 379 var challengeValue; |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
412 'type': 'sign_helper_request', | 413 'type': 'sign_helper_request', |
413 'signData': challenges, | 414 'signData': challenges, |
414 'timeout': opt_timeoutSeconds || 0, | 415 'timeout': opt_timeoutSeconds || 0, |
415 'timeoutSeconds': opt_timeoutSeconds || 0 | 416 'timeoutSeconds': opt_timeoutSeconds || 0 |
416 }; | 417 }; |
417 if (opt_logMsgUrl !== undefined) { | 418 if (opt_logMsgUrl !== undefined) { |
418 request.logMsgUrl = opt_logMsgUrl; | 419 request.logMsgUrl = opt_logMsgUrl; |
419 } | 420 } |
420 return request; | 421 return request; |
421 } | 422 } |
OLD | NEW |