Index: chrome/browser/resources/cryptotoken/gnubby-u2f.js |
diff --git a/chrome/browser/resources/cryptotoken/gnubby-u2f.js b/chrome/browser/resources/cryptotoken/gnubby-u2f.js |
index 653b957d01175acdafa007f8471eea9742a6df47..39a73d6a8a75e4d108149811b61fa7483ff59d47 100644 |
--- a/chrome/browser/resources/cryptotoken/gnubby-u2f.js |
+++ b/chrome/browser/resources/cryptotoken/gnubby-u2f.js |
@@ -7,7 +7,6 @@ |
*/ |
'use strict'; |
-// Commands and flags of the Gnubby applet at |
/** Enroll */ |
usbGnubby.U2F_ENROLL = 0x01; |
/** Request signature */ |
@@ -28,6 +27,12 @@ usbGnubby.P1_TUP_TESTONLY = 0x04; |
/** Attest with device key */ |
usbGnubby.P1_INDIVIDUAL_KEY = 0x80; |
+// Version values |
+/** V1 of the applet. */ |
+usbGnubby.U2F_V1 = 'U2F_V1'; |
+/** V2 of the applet. */ |
+usbGnubby.U2F_V2 = 'U2F_V2'; |
+ |
/** Perform enrollment |
* @param {ArrayBuffer|Uint8Array} challenge Enrollment challenge |
* @param {ArrayBuffer|Uint8Array} appIdHash Hashed application id |
@@ -63,33 +68,51 @@ usbGnubby.prototype.enroll = function(challenge, appIdHash, cb) { |
*/ |
usbGnubby.prototype.sign = function(challengeHash, appIdHash, keyHandle, cb, |
opt_nowink) { |
- var apdu = new Uint8Array( |
- [0x00, |
- usbGnubby.U2F_SIGN, |
- usbGnubby.P1_TUP_REQUIRED | usbGnubby.P1_TUP_CONSUME, |
- 0x00, 0x00, 0x00, |
- challengeHash.length + appIdHash.length + keyHandle.length]); |
- if (opt_nowink) { |
- // A signature request that does not want winking. |
- // These are used during enroll to figure out whether a gnubby was already |
- // enrolled. |
- // Tell applet to not actually produce a signature, even |
- // if already touched. |
- apdu[2] |= usbGnubby.P1_TUP_TESTONLY; |
- } |
- var u8 = new Uint8Array(apdu.length + challengeHash.length + |
- appIdHash.length + keyHandle.length + 2); |
- for (var i = 0; i < apdu.length; ++i) u8[i] = apdu[i]; |
- for (var i = 0; i < challengeHash.length; ++i) u8[i + apdu.length] = |
- challengeHash[i]; |
- for (var i = 0; i < appIdHash.length; ++i) { |
- u8[i + apdu.length + challengeHash.length] = appIdHash[i]; |
- } |
- for (var i = 0; i < keyHandle.length; ++i) { |
- u8[i + apdu.length + challengeHash.length + appIdHash.length] = |
- keyHandle[i]; |
- } |
- this.apduReply_(u8.buffer, cb, opt_nowink); |
+ var self = this; |
+ // The sign command's format is ever-so-slightly different between V1 and V2, |
+ // so get this gnubby's version prior to sending it. |
+ this.version(function(rc, opt_data) { |
+ if (rc) { |
+ cb(rc); |
+ return; |
+ } |
+ var version = UTIL_BytesToString(new Uint8Array(opt_data || [])); |
+ var apduDataLen = |
+ challengeHash.length + appIdHash.length + keyHandle.length; |
+ if (version != usbGnubby.U2F_V1) { |
+ // The V2 sign command includes a length byte for the key handle. |
+ apduDataLen++; |
+ } |
+ var apdu = new Uint8Array( |
+ [0x00, |
+ usbGnubby.U2F_SIGN, |
+ usbGnubby.P1_TUP_REQUIRED | usbGnubby.P1_TUP_CONSUME, |
+ 0x00, 0x00, 0x00, |
+ apduDataLen]); |
+ if (opt_nowink) { |
+ // A signature request that does not want winking. |
+ // These are used during enroll to figure out whether a gnubby was already |
+ // enrolled. |
+ // Tell applet to not actually produce a signature, even |
+ // if already touched. |
+ apdu[2] |= usbGnubby.P1_TUP_TESTONLY; |
+ } |
+ var u8 = new Uint8Array(apdu.length + apduDataLen + 2); |
+ for (var i = 0; i < apdu.length; ++i) u8[i] = apdu[i]; |
+ for (var i = 0; i < challengeHash.length; ++i) u8[i + apdu.length] = |
+ challengeHash[i]; |
+ for (var i = 0; i < appIdHash.length; ++i) { |
+ u8[i + apdu.length + challengeHash.length] = appIdHash[i]; |
+ } |
+ var keyHandleOffset = apdu.length + challengeHash.length + appIdHash.length; |
+ if (version != usbGnubby.U2F_V1) { |
+ u8[keyHandleOffset++] = keyHandle.length; |
+ } |
+ for (var i = 0; i < keyHandle.length; ++i) { |
+ u8[i + keyHandleOffset] = keyHandle[i]; |
+ } |
+ self.apduReply_(u8.buffer, cb, opt_nowink); |
+ }); |
}; |
/** Request version information |
@@ -97,14 +120,23 @@ usbGnubby.prototype.sign = function(challengeHash, appIdHash, keyHandle, cb, |
*/ |
usbGnubby.prototype.version = function(cb) { |
if (!cb) cb = usbGnubby.defaultCallback; |
+ if (this.version_) { |
+ cb(-llGnubby.OK, this.version_); |
+ return; |
+ } |
+ var self = this; |
var apdu = new Uint8Array([0x00, usbGnubby.U2F_VERSION, 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('U2F_V1')); |
+ var v1 = new Uint8Array(UTIL_StringToBytes(usbGnubby.U2F_V1)); |
+ self.version_ = v1.buffer; |
cb(-llGnubby.OK, v1.buffer); |
} else { |
+ if (!rc) { |
+ self.version_ = data; |
+ } |
cb(rc, data); |
} |
}); |