diff options
13 files changed, 155 insertions, 118 deletions
diff --git a/chrome/browser/resources/component_extension_resources.grd b/chrome/browser/resources/component_extension_resources.grd index 80e80afc..4f02a86 100644 --- a/chrome/browser/resources/component_extension_resources.grd +++ b/chrome/browser/resources/component_extension_resources.grd @@ -205,9 +205,7 @@ <include name="IDR_CRYPTOTOKEN_GNUBBIES_JS" file="cryptotoken/gnubbies.js" type="BINDATA" /> <include name="IDR_CRYPTOTOKEN_GNUBBY_JS" file="cryptotoken/gnubby.js" type="BINDATA" /> <include name="IDR_CRYPTOTOKEN_GNUBBY_U2F_JS" file="cryptotoken/gnubby-u2f.js" type="BINDATA" /> - <include name="IDR_CRYPTOTOKEN_GNUBBYCODETYPES_JS" file="cryptotoken/gnubbycodetypes.js" type="BINDATA" /> <include name="IDR_CRYPTOTOKEN_GNUBBYFACTORY_JS" file="cryptotoken/gnubbyfactory.js" type="BINDATA" /> - <include name="IDR_CRYPTOTOKEN_GNUBBYMSGTYPES_JS" file="cryptotoken/gnubbymsgtypes.js" type="BINDATA" /> <include name="IDR_CRYPTOTOKEN_USBGNUBBYFACTORY_JS" file="cryptotoken/usbgnubbyfactory.js" type="BINDATA" /> <include name="IDR_CRYPTOTOKEN_DEVICESTATUSCODES_JS" file="cryptotoken/devicestatuscodes.js" type="BINDATA" /> <include name="IDR_CRYPTOTOKEN_ENROLLER_JS" file="cryptotoken/enroller.js" type="BINDATA" /> diff --git a/chrome/browser/resources/cryptotoken/gnubbies.js b/chrome/browser/resources/cryptotoken/gnubbies.js index 8cd2528..86eb6e6 100644 --- a/chrome/browser/resources/cryptotoken/gnubbies.js +++ b/chrome/browser/resources/cryptotoken/gnubbies.js @@ -16,11 +16,23 @@ var GnubbyDeviceId; /** + * Ways in which gnubby devices are enumerated. + * @const + * @enum {number} + */ +var GnubbyEnumerationTypes = { + ANY: 0, + VID_PID: 1, + FIDO_U2F: 2 +}; + +/** * @typedef {{ * isSharedAccess: boolean, - * enumerate: function(function(Array)), + * enumerate: function(function(Array), GnubbyEnumerationTypes=), * deviceToDeviceId: function(*): GnubbyDeviceId, - * open: function(Gnubbies, number, *, function(number, GnubbyDevice=)) + * open: function(Gnubbies, number, *, function(number, GnubbyDevice=)), + * cancelOpen: (undefined|function(Gnubbies, number, *)) * }} */ var GnubbyNamespaceImpl; @@ -41,7 +53,7 @@ function Gnubbies() { this.namespaces_ = []; /** @private {Object<string, GnubbyNamespaceImpl>} */ this.impl_ = {}; - /** @private {Object<string, Object<number, !GnubbyDevice>>} */ + /** @private {Object<string, Object<number|string, !GnubbyDevice>>} */ this.openDevs_ = {}; /** @private {Object<string, Object<number, *>>} */ this.pendingOpens_ = {}; // clients awaiting an open @@ -97,10 +109,20 @@ Gnubbies.prototype.closeAll = function() { }; /** + * @param {string} namespace + * @return {function(*)} deviceToDeviceId method associated with given namespace + * @private + */ +Gnubbies.prototype.getDeviceToDeviceId_ = function(namespace) { + return this.impl_[namespace].deviceToDeviceId; +}; + +/** * @param {function(number, Array<GnubbyDeviceId>)} cb Called back with the * result of enumerating. + * @param {GnubbyEnumerationTypes=} opt_type Which type of enumeration to do. */ -Gnubbies.prototype.enumerate = function(cb) { +Gnubbies.prototype.enumerate = function(cb, opt_type) { if (!cb) { cb = function(rc, indexes) { var msg = 'defaultEnumerateCallback(' + rc; @@ -145,7 +167,7 @@ Gnubbies.prototype.enumerate = function(cb) { var presentDevs = {}; var deviceIds = []; - var deviceToDeviceId = self.impl_[namespace].deviceToDeviceId; + var deviceToDeviceId = self.getDeviceToDeviceId_(namespace); for (var i = 0; i < devs.length; ++i) { var deviceId = deviceToDeviceId(devs[i]); deviceIds.push(deviceId); @@ -189,7 +211,7 @@ Gnubbies.prototype.enumerate = function(cb) { for (var i = 0; i < this.namespaces_.length; i++) { var namespace = this.namespaces_[i]; var enumerator = this.impl_[namespace].enumerate; - enumerator(makeEnumerateCb(namespace)); + enumerator(makeEnumerateCb(namespace), opt_type); } } }; @@ -274,7 +296,7 @@ Gnubbies.prototype.addClient = function(which, who, cb) { } var dev = null; - var deviceToDeviceId = this.impl_[which.namespace].deviceToDeviceId; + var deviceToDeviceId = this.getDeviceToDeviceId_(which.namespace); if (this.devs_[which.namespace]) { for (var i = 0; i < this.devs_[which.namespace].length; i++) { var device = this.devs_[which.namespace][i]; @@ -332,6 +354,35 @@ Gnubbies.prototype.addClient = function(which, who, cb) { }; /** + * Called to cancel add client operation + * @param {GnubbyDeviceId} which Which device to cancel open. + */ +Gnubbies.prototype.cancelAddClient = function(which) { + var dev = null; + var deviceToDeviceId = this.getDeviceToDeviceId_(which.namespace); + if (this.devs_[which.namespace]) { + for (var i = 0; i < this.devs_[which.namespace].length; i++) { + var device = this.devs_[which.namespace][i]; + if (deviceToDeviceId(device).device == which.device) { + dev = device; + break; + } + } + } + + if (!dev) { + return; + } + + if (this.pendingOpens_[which.namespace] && + this.pendingOpens_[which.namespace][which.device]) { + var cancelOpenImpl = this.impl_[which.namespace].cancelOpen; + if (cancelOpenImpl) + cancelOpenImpl(this, which.device, dev); + } +}; + +/** * Removes a client from a low-level gnubby. * @param {GnubbyDevice} whichDev The gnubby. * @param {*} who The client. diff --git a/chrome/browser/resources/cryptotoken/gnubby-u2f.js b/chrome/browser/resources/cryptotoken/gnubby-u2f.js index a5ae8b5..47ac6f6 100644 --- a/chrome/browser/resources/cryptotoken/gnubby-u2f.js +++ b/chrome/browser/resources/cryptotoken/gnubby-u2f.js @@ -134,19 +134,33 @@ Gnubby.prototype.version = function(cb) { return; } var self = this; + + function gotResponse(rc, data) { + if (!rc) { + self.version_ = data; + } + cb(rc, data); + } + var apdu = new Uint8Array([0x00, Gnubby.U2F_VERSION, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00]); + 0x00, 0x00]); this.apduReply(apdu.buffer, function(rc, data) { if (rc == 0x6d00) { // Command not implemented. Pretend this is v1. var v1 = new Uint8Array(UTIL_StringToBytes(Gnubby.U2F_V1)); self.version_ = v1.buffer; cb(-GnubbyDevice.OK, v1.buffer); - } else { - if (!rc) { - self.version_ = data; - } - cb(rc, data); + return; + } + if (rc == 0x6700) { + // Wrong length. Try with non-ISO 7816-4-conforming layout defined in + // earlier U2F drafts. + apdu = new Uint8Array([0x00, Gnubby.U2F_VERSION, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00]); + self.apduReply(apdu.buffer, gotResponse); + return; } + // Any other response: handle as final result. + gotResponse(rc, data); }); }; diff --git a/chrome/browser/resources/cryptotoken/gnubby.js b/chrome/browser/resources/cryptotoken/gnubby.js index b283ee9..197f9fb 100644 --- a/chrome/browser/resources/cryptotoken/gnubby.js +++ b/chrome/browser/resources/cryptotoken/gnubby.js @@ -57,20 +57,33 @@ Gnubby.hexCid = function(cid) { }; /** + * Cancels open attempt for this gnubby, if available. + */ +Gnubby.prototype.cancelOpen = function() { + if (this.which) + Gnubby.gnubbies_.cancelAddClient(this.which); +}; + +/** * Opens the gnubby with the given index, or the first found gnubby if no * index is specified. * @param {GnubbyDeviceId} which The device to open. If null, the first * gnubby found is opened. + * @param {GnubbyEnumerationTypes=} opt_type Which type of device to enumerate. * @param {function(number)|undefined} opt_cb Called with result of opening the * gnubby. + * @param {string=} opt_caller Identifier for the caller. */ -Gnubby.prototype.open = function(which, opt_cb) { +Gnubby.prototype.open = function(which, opt_type, opt_cb, opt_caller) { var cb = opt_cb ? opt_cb : Gnubby.defaultCallback; if (this.closed) { cb(-GnubbyDevice.NODEVICE); return; } this.closingWhenIdle = false; + if (opt_caller) { + this.caller_ = opt_caller; + } var self = this; @@ -94,7 +107,7 @@ Gnubby.prototype.open = function(which, opt_cb) { if (rc == -GnubbyDevice.NODEVICE && enumerateRetriesRemaining-- > 0) { // We were trying to open the first device, but now it's not there? // Do over. - Gnubby.gnubbies_.enumerate(enumerated); + Gnubby.gnubbies_.enumerate(enumerated, opt_type); return; } self.dev = device; @@ -110,7 +123,7 @@ Gnubby.prototype.open = function(which, opt_cb) { cb(rc); }); } else { - Gnubby.gnubbies_.enumerate(enumerated); + Gnubby.gnubbies_.enumerate(enumerated, opt_type); } }; diff --git a/chrome/browser/resources/cryptotoken/gnubbycodetypes.js b/chrome/browser/resources/cryptotoken/gnubbycodetypes.js deleted file mode 100644 index 94b8ab5..0000000 --- a/chrome/browser/resources/cryptotoken/gnubbycodetypes.js +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview This provides the different code types for the gnubby - * operations. - */ - -/** - * @const - * @enum {number} - */ -var GnubbyCodeTypes = { - /** Request succeeded. */ - 'OK': 0, - - /** All plugged in devices are already enrolled. */ - 'ALREADY_ENROLLED': 2, - - /** None of the plugged in devices are enrolled. */ - 'NONE_PLUGGED_ENROLLED': 3, - - /** One or more devices are waiting for touch. */ - 'WAIT_TOUCH': 4, - - /** Unknown error. */ - 'UNKNOWN_ERROR': 7, - - /** Bad request. */ - 'BAD_REQUEST': 12 -}; diff --git a/chrome/browser/resources/cryptotoken/gnubbyfactory.js b/chrome/browser/resources/cryptotoken/gnubbyfactory.js index ffe386c..5be99d6 100644 --- a/chrome/browser/resources/cryptotoken/gnubbyfactory.js +++ b/chrome/browser/resources/cryptotoken/gnubbyfactory.js @@ -31,9 +31,13 @@ var FactoryOpenCallback; * @param {string=} opt_appIdHash The base64-encoded hash of the app id for * which the gnubby being opened. * @param {string=} opt_logMsgUrl The url to post log messages to. + * @param {string=} opt_caller Identifier for the caller. + * @return {(function ()|undefined)} Some implementations might return function + * that can be used to cancel this pending open operation. Opening device + * might take long time or be resource-hungry. */ GnubbyFactory.prototype.openGnubby = - function(which, forEnroll, cb, opt_appIdHash, opt_logMsgUrl) { + function(which, forEnroll, cb, opt_appIdHash, opt_logMsgUrl, opt_caller) { }; /** diff --git a/chrome/browser/resources/cryptotoken/gnubbymsgtypes.js b/chrome/browser/resources/cryptotoken/gnubbymsgtypes.js deleted file mode 100644 index 8695d06..0000000 --- a/chrome/browser/resources/cryptotoken/gnubbymsgtypes.js +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview This provides the different message types for the gnubby - * operations. - */ - -var GnubbyMsgTypes = {}; - -/** - * Enroll request message type. - * @const - */ -GnubbyMsgTypes.ENROLL_WEB_REQUEST = 'enroll_web_request'; - -/** - * Enroll reply message type. - * @const - */ -GnubbyMsgTypes.ENROLL_WEB_REPLY = 'enroll_web_reply'; - -/** - * Enroll notification message type. - * @const - */ -GnubbyMsgTypes.ENROLL_WEB_NOTIFICATION = 'enroll_web_notification'; - -/** - * Sign request message type. - * @const - */ -GnubbyMsgTypes.SIGN_WEB_REQUEST = 'sign_web_request'; - -/** - * Sign reply message type. - * @const - */ -GnubbyMsgTypes.SIGN_WEB_REPLY = 'sign_web_reply'; - -/** - * Sign notification message type. - * @const - */ -GnubbyMsgTypes.SIGN_WEB_NOTIFICATION = 'sign_web_notification'; diff --git a/chrome/browser/resources/cryptotoken/hidgnubbydevice.js b/chrome/browser/resources/cryptotoken/hidgnubbydevice.js index 1ad59d1..80c40c0 100644 --- a/chrome/browser/resources/cryptotoken/hidgnubbydevice.js +++ b/chrome/browser/resources/cryptotoken/hidgnubbydevice.js @@ -416,8 +416,9 @@ HidGnubbyDevice.HID_VID_PIDS = [ /** * @param {function(Array)} cb Enumeration callback + * @param {GnubbyEnumerationTypes=} opt_type Which type of enumeration to do. */ -HidGnubbyDevice.enumerate = function(cb) { +HidGnubbyDevice.enumerate = function(cb, opt_type) { /** * One pass using getDevices, and one for each of the hardcoded vid/pids. * @const @@ -452,13 +453,24 @@ HidGnubbyDevice.enumerate = function(cb) { } } - // Pass 1: usagePage-based enumeration. - chrome.hid.getDevices({filters: [{usagePage: 0xf1d0}]}, - enumerated.bind(null, true)); - // Pass 2: vid/pid-based enumeration, for legacy devices. - for (var i = 0; i < HidGnubbyDevice.HID_VID_PIDS.length; i++) { - var dev = HidGnubbyDevice.HID_VID_PIDS[i]; - chrome.hid.getDevices({filters: [dev]}, enumerated.bind(null, false)); + // Pass 1: usagePage-based enumeration, for FIDO U2F devices. If non-FIDO + // devices are asked for, "implement" this pass by providing it the empty + // list. (enumerated requires that it's called once per pass.) + if (opt_type == GnubbyEnumerationTypes.VID_PID) { + enumerated(true, []); + } else { + chrome.hid.getDevices({filters: [{usagePage: 0xf1d0}]}, + enumerated.bind(null, true)); + } + // Pass 2: vid/pid-based enumeration, for legacy devices. If FIDO devices + // are asked for, "implement" this pass by providing it the empty list. + if (opt_type == GnubbyEnumerationTypes.FIDO_U2F) { + enumerated(false, []); + } else { + for (var i = 0; i < HidGnubbyDevice.HID_VID_PIDS.length; i++) { + var dev = HidGnubbyDevice.HID_VID_PIDS[i]; + chrome.hid.getDevices({filters: [dev]}, enumerated.bind(null, false)); + } } }; diff --git a/chrome/browser/resources/cryptotoken/manifest.json b/chrome/browser/resources/cryptotoken/manifest.json index 0ba3a03..6e4c46d 100644 --- a/chrome/browser/resources/cryptotoken/manifest.json +++ b/chrome/browser/resources/cryptotoken/manifest.json @@ -1,7 +1,7 @@ { "name": "CryptoTokenExtension", "description": "CryptoToken Component Extension", - "version": "0.9.31", + "version": "0.9.38", "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq7zRobvA+AVlvNqkHSSVhh1sEWsHSqz4oR/XptkDe/Cz3+gW9ZGumZ20NCHjaac8j1iiesdigp8B1LJsd/2WWv2Dbnto4f8GrQ5MVphKyQ9WJHwejEHN2K4vzrTcwaXqv5BSTXwxlxS/mXCmXskTfryKTLuYrcHEWK8fCHb+0gvr8b/kvsi75A1aMmb6nUnFJvETmCkOCPNX5CHTdy634Ts/x0fLhRuPlahk63rdf7agxQv5viVjQFk+tbgv6aa9kdSd11Js/RZ9yZjrFgHOBWgP4jTBqud4+HUglrzu8qynFipyNRLCZsaxhm+NItTyNgesxLdxZcwOz56KD1Q4IQIDAQAB", "manifest_version": 2, "permissions": [ diff --git a/chrome/browser/resources/cryptotoken/singlesigner.js b/chrome/browser/resources/cryptotoken/singlesigner.js index 445f96d..b76cc99 100644 --- a/chrome/browser/resources/cryptotoken/singlesigner.js +++ b/chrome/browser/resources/cryptotoken/singlesigner.js @@ -74,6 +74,9 @@ function SingleGnubbySigner(gnubbyId, forEnroll, completeCb, timer, /** @private {!Object<string, number>} */ this.cachedError_ = []; + + /** @private {(function()|undefined)} */ + this.openCanceller_; } /** @enum {number} */ @@ -107,6 +110,11 @@ SingleGnubbySigner.prototype.getDeviceId = function() { * Closes this signer's gnubby, if it's held. */ SingleGnubbySigner.prototype.close = function() { + if (this.state_ == SingleGnubbySigner.State.OPENING) { + if (this.openCanceller_) + this.openCanceller_(); + } + if (!this.gnubby_) return; this.state_ = SingleGnubbySigner.State.CLOSING; this.gnubby_.closeWhenIdle(this.closed_.bind(this)); @@ -185,12 +193,13 @@ SingleGnubbySigner.prototype.open_ = function() { } if (this.state_ == SingleGnubbySigner.State.INIT) { this.state_ = SingleGnubbySigner.State.OPENING; - DEVICE_FACTORY_REGISTRY.getGnubbyFactory().openGnubby( + this.openCanceller_ = DEVICE_FACTORY_REGISTRY.getGnubbyFactory().openGnubby( this.gnubbyId_, this.forEnroll_, this.openCallback_.bind(this), appIdHash, - this.logMsgUrl_); + this.logMsgUrl_, + 'singlesigner.js:SingleGnubbySigner.prototype.open_'); } }; @@ -233,11 +242,13 @@ SingleGnubbySigner.prototype.openCallback_ = function(rc, gnubby) { var self = this; window.setTimeout(function() { if (self.gnubby_) { - DEVICE_FACTORY_REGISTRY.getGnubbyFactory().openGnubby( + this.openCanceller_ = DEVICE_FACTORY_REGISTRY + .getGnubbyFactory().openGnubby( self.gnubbyId_, self.forEnroll_, self.openCallback_.bind(self), - self.logMsgUrl_); + self.logMsgUrl_, + 'singlesigner.js:SingleGnubbySigner.prototype.openCallback_'); } }, SingleGnubbySigner.OPEN_DELAY_MILLIS); } else { diff --git a/chrome/browser/resources/cryptotoken/usbgnubbydevice.js b/chrome/browser/resources/cryptotoken/usbgnubbydevice.js index 723d125..e37f9b3 100644 --- a/chrome/browser/resources/cryptotoken/usbgnubbydevice.js +++ b/chrome/browser/resources/cryptotoken/usbgnubbydevice.js @@ -396,8 +396,16 @@ UsbGnubbyDevice.WINUSB_VID_PIDS = [ /** * @param {function(Array)} cb Enumerate callback + * @param {GnubbyEnumerationTypes=} opt_type Which type of enumeration to do. */ -UsbGnubbyDevice.enumerate = function(cb) { +UsbGnubbyDevice.enumerate = function(cb, opt_type) { + // UsbGnubbyDevices are all non-FIDO devices, so return an empty list if + // FIDO is what's wanted. + if (opt_type == GnubbyEnumerationTypes.FIDO_U2F) { + cb([]); + return; + } + var numEnumerated = 0; var allDevs = []; diff --git a/chrome/browser/resources/cryptotoken/usbgnubbyfactory.js b/chrome/browser/resources/cryptotoken/usbgnubbyfactory.js index 9b110a6c..20c6410 100644 --- a/chrome/browser/resources/cryptotoken/usbgnubbyfactory.js +++ b/chrome/browser/resources/cryptotoken/usbgnubbyfactory.js @@ -27,12 +27,14 @@ function UsbGnubbyFactory(gnubbies) { * @param {string=} opt_appIdHash The base64-encoded hash of the app id for * which the gnubby being opened. * @param {string=} opt_logMsgUrl The url to post log messages to. + * @param {string=} opt_caller Identifier for the caller. + * @return {undefined} no open canceller needed for this type of gnubby * @override */ UsbGnubbyFactory.prototype.openGnubby = - function(which, forEnroll, cb, opt_appIdHash, opt_logMsgUrl) { + function(which, forEnroll, cb, opt_appIdHash, opt_logMsgUrl, opt_caller) { var gnubby = new Gnubby(); - gnubby.open(which, function(rc) { + gnubby.open(which, GnubbyEnumerationTypes.ANY, function(rc) { if (rc) { cb(rc, gnubby); return; @@ -40,7 +42,7 @@ UsbGnubbyFactory.prototype.openGnubby = gnubby.sync(function(rc) { cb(rc, gnubby); }); - }); + }, opt_caller); }; /** diff --git a/chrome/browser/resources/cryptotoken/webrequest.js b/chrome/browser/resources/cryptotoken/webrequest.js index 0564559..6c77e3f 100644 --- a/chrome/browser/resources/cryptotoken/webrequest.js +++ b/chrome/browser/resources/cryptotoken/webrequest.js @@ -237,7 +237,7 @@ function sendResponseOnce(sentResponse, closeable, response, sendResponse) { console.warn('sendResponse failed: ' + exception); } } else { - console.warn(UTIL_fmt('Tried to reply more than once! Juan, FIX ME')); + console.warn(UTIL_fmt('Tried to reply more than once!')); } } @@ -251,6 +251,8 @@ function sha256HashOfString(string) { return s.digest(); } +var UNUSED_CID_PUBKEY_VALUE = 'unused'; + /** * Normalizes the TLS channel ID value: * 1. Converts semantically empty values (undefined, null, 0) to the empty @@ -262,9 +264,9 @@ function sha256HashOfString(string) { */ function tlsChannelIdValue(opt_tlsChannelId) { if (!opt_tlsChannelId) { - // Case 1: Always set some value for TLS channel ID, even if it's the empty + // Case 1: Always set some value for TLS channel ID, even if it's the empty // string: this browser definitely supports them. - return ''; + return UNUSED_CID_PUBKEY_VALUE; } if (typeof opt_tlsChannelId === 'string') { try { @@ -273,7 +275,7 @@ function tlsChannelIdValue(opt_tlsChannelId) { // Case 1: The string value 'null' parses as the Javascript object null, // so return an empty string: the browser definitely supports TLS // channel id. - return ''; + return UNUSED_CID_PUBKEY_VALUE; } // Case 2: return the value as a JS object. return /** @type {Object} */ (obj); |