mirror of
https://github.com/FeitianTech/u2f-ref-code.git
synced 2026-08-28 10:40:37 -07:00
- Require explicit user approval on registration - Embed eTLD list instead of fetching remotel - Support compressed requests (single appId and challenge) - Do not reveal to webpage if that a device is enrolled without explicit user action - Smaller bugfixes
250 lines
6.5 KiB
JavaScript
250 lines
6.5 KiB
JavaScript
// Copyright 2014 Google Inc. All rights reserved
|
|
//
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file or at
|
|
// https://developers.google.com/open-source/licenses/bsd
|
|
|
|
/** @fileoverview Various string utility functions */
|
|
'use strict';
|
|
|
|
/**
|
|
* Converts a string to an array of bytes.
|
|
* @param {string} s The string to convert.
|
|
* @param {(Array|Uint8Array)=} bytes The Array-like object into which to store
|
|
* the bytes. A new Array will be created if not provided.
|
|
* @return {(Array|Uint8Array)} An array of bytes representing the string.
|
|
*/
|
|
function UTIL_StringToBytes(s, bytes) {
|
|
bytes = bytes || new Array(s.length);
|
|
for (var i = 0; i < s.length; ++i)
|
|
bytes[i] = s.charCodeAt(i);
|
|
return bytes;
|
|
}
|
|
|
|
/**
|
|
* Converts a byte array to a string.
|
|
* @param {(Uint8Array|Array.<number>)} b input byte array.
|
|
* @return {string} result.
|
|
*/
|
|
function UTIL_BytesToString(b) {
|
|
return String.fromCharCode.apply(null, b);
|
|
}
|
|
|
|
/**
|
|
* Converts a byte array to a hex string.
|
|
* @param {(Uint8Array|Array.<number>)} b input byte array.
|
|
* @return {string} result.
|
|
*/
|
|
function UTIL_BytesToHex(b) {
|
|
if (!b) return '(null)';
|
|
var hexchars = '0123456789ABCDEF';
|
|
var hexrep = new Array(b.length * 2);
|
|
|
|
for (var i = 0; i < b.length; ++i) {
|
|
hexrep[i * 2 + 0] = hexchars.charAt((b[i] >> 4) & 15);
|
|
hexrep[i * 2 + 1] = hexchars.charAt(b[i] & 15);
|
|
}
|
|
return hexrep.join('');
|
|
}
|
|
|
|
function UTIL_BytesToHexWithSeparator(b, sep) {
|
|
var hexchars = '0123456789ABCDEF';
|
|
var stride = 2 + (sep ? 1 : 0);
|
|
var hexrep = new Array(b.length * stride);
|
|
|
|
for (var i = 0; i < b.length; ++i) {
|
|
if (sep) hexrep[i * stride + 0] = sep;
|
|
hexrep[i * stride + stride - 2] = hexchars.charAt((b[i] >> 4) & 15);
|
|
hexrep[i * stride + stride - 1] = hexchars.charAt(b[i] & 15);
|
|
}
|
|
return (sep ? hexrep.slice(1) : hexrep).join('');
|
|
}
|
|
|
|
function UTIL_HexToBytes(h) {
|
|
var hexchars = '0123456789ABCDEFabcdef';
|
|
var res = new Uint8Array(h.length / 2);
|
|
for (var i = 0; i < h.length; i += 2) {
|
|
if (hexchars.indexOf(h.substring(i, i + 1)) == -1) break;
|
|
res[i / 2] = parseInt(h.substring(i, i + 2), 16);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
function UTIL_HexToArray(h) {
|
|
var hexchars = '0123456789ABCDEFabcdef';
|
|
var res = new Array(h.length / 2);
|
|
for (var i = 0; i < h.length; i += 2) {
|
|
if (hexchars.indexOf(h.substring(i, i + 1)) == -1) break;
|
|
res[i / 2] = parseInt(h.substring(i, i + 2), 16);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
function UTIL_equalArrays(a, b) {
|
|
if (!a || !b) return false;
|
|
if (a.length != b.length) return false;
|
|
var accu = 0;
|
|
for (var i = 0; i < a.length; ++i)
|
|
accu |= a[i] ^ b[i];
|
|
return accu === 0;
|
|
}
|
|
|
|
function UTIL_ltArrays(a, b) {
|
|
if (a.length < b.length) return true;
|
|
if (a.length > b.length) return false;
|
|
for (var i = 0; i < a.length; ++i) {
|
|
if (a[i] < b[i]) return true;
|
|
if (a[i] > b[i]) return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function UTIL_gtArrays(a, b) {
|
|
return UTIL_ltArrays(b, a);
|
|
}
|
|
|
|
function UTIL_geArrays(a, b) {
|
|
return !UTIL_ltArrays(a, b);
|
|
}
|
|
|
|
function UTIL_unionArrays(a, b) {
|
|
var obj = {};
|
|
for (var i = 0; i < a.length; i++) {
|
|
obj[a[i]] = a[i];
|
|
}
|
|
for (var i = 0; i < b.length; i++) {
|
|
obj[b[i]] = b[i];
|
|
}
|
|
var union = [];
|
|
for (var k in obj) {
|
|
union.push(obj[k]);
|
|
}
|
|
return union;
|
|
}
|
|
|
|
function UTIL_getRandom(a) {
|
|
var tmp = new Array(a);
|
|
var rnd = new Uint8Array(a);
|
|
window.crypto.getRandomValues(rnd); // Yay!
|
|
for (var i = 0; i < a; ++i) tmp[i] = rnd[i] & 255;
|
|
return tmp;
|
|
}
|
|
|
|
function UTIL_setFavicon(icon) {
|
|
// Construct a new favion link tag
|
|
var faviconLink = document.createElement('link');
|
|
faviconLink.rel = 'Shortcut Icon';
|
|
faviconLink.type = 'image/x-icon';
|
|
faviconLink.href = icon;
|
|
|
|
// Remove the old favion, if it exists
|
|
var head = document.getElementsByTagName('head')[0];
|
|
var links = head.getElementsByTagName('link');
|
|
for (var i = 0; i < links.length; i++) {
|
|
var link = links[i];
|
|
if (link.type == faviconLink.type && link.rel == faviconLink.rel) {
|
|
head.removeChild(link);
|
|
}
|
|
}
|
|
|
|
// Add in the new one
|
|
head.appendChild(faviconLink);
|
|
}
|
|
|
|
// Erase all entries in array
|
|
function UTIL_clear(a) {
|
|
if (a instanceof Array) {
|
|
for (var i = 0; i < a.length; ++i)
|
|
a[i] = 0;
|
|
}
|
|
}
|
|
|
|
// Type tags used for ASN.1 encoding of ECDSA signatures
|
|
/** @const */
|
|
var UTIL_ASN_INT = 0x02;
|
|
/** @const */
|
|
var UTIL_ASN_SEQUENCE = 0x30;
|
|
|
|
/**
|
|
* Parse SEQ(INT, INT) from ASN1 byte array.
|
|
* @param {(Uint8Array|Array.<number>)} a input to parse from.
|
|
* @return {{'r': !Array.<number>, 's': !Array.<number>}|null}
|
|
*/
|
|
function UTIL_Asn1SignatureToJson(a) {
|
|
if (a.length < 6) return null; // Too small to be valid
|
|
if (a[0] != UTIL_ASN_SEQUENCE) return null;
|
|
var l = a[1] & 255;
|
|
if (l & 0x80) return null; // SEQ.size too large
|
|
if (a.length != 2 + l) return null; // SEQ size does not match input
|
|
|
|
function parseInt(off) {
|
|
if (a[off] != UTIL_ASN_INT) return null;
|
|
var l = a[off + 1] & 255;
|
|
if (l & 0x80) return null; // INT.size too large
|
|
if (off + 2 + l > a.length) return null; // Out of bounds
|
|
return a.slice(off + 2, off + 2 + l);
|
|
}
|
|
|
|
var r = parseInt(2);
|
|
if (!r) return null;
|
|
|
|
var s = parseInt(2 + 2 + r.length);
|
|
if (!s) return null;
|
|
|
|
return {'r': r, 's': s};
|
|
}
|
|
|
|
/**
|
|
* Encode a JSON signature {r,s} as an ASN1 SEQ(INT, INT). May modify sig
|
|
* @param {{'r': (!Array.<number>|undefined), 's': !Array.<number>}} sig
|
|
* @return {!Uint8Array}
|
|
*/
|
|
function UTIL_JsonSignatureToAsn1(sig) {
|
|
var rbytes = sig.r;
|
|
var sbytes = sig.s;
|
|
|
|
// ASN.1 integers are arbitrary length msb first and signed.
|
|
// sig.r and sig.s are 256 bits msb first but _unsigned_, so we must
|
|
// prepend a zero byte in case their high bit is set.
|
|
if (rbytes[0] & 0x80)
|
|
rbytes.unshift(0);
|
|
if (sbytes[0] & 0x80)
|
|
sbytes.unshift(0);
|
|
|
|
var len = 4 + rbytes.length + sbytes.length;
|
|
var buf = new Uint8Array(2 + len);
|
|
var i = 0;
|
|
buf[i++] = UTIL_ASN_SEQUENCE;
|
|
buf[i++] = len;
|
|
|
|
buf[i++] = UTIL_ASN_INT;
|
|
buf[i++] = rbytes.length;
|
|
buf.set(rbytes, i);
|
|
i += rbytes.length;
|
|
|
|
buf[i++] = UTIL_ASN_INT;
|
|
buf[i++] = sbytes.length;
|
|
buf.set(sbytes, i);
|
|
|
|
return buf;
|
|
}
|
|
|
|
// hr:min:sec.milli string
|
|
function UTIL_time() {
|
|
var d = new Date();
|
|
var m = '000' + d.getMilliseconds();
|
|
var s = d.toTimeString().substring(0, 8) + '.' + m.substring(m.length - 3);
|
|
return s;
|
|
}
|
|
var UTIL_events = [];
|
|
var UTIL_max_events = 500;
|
|
|
|
function UTIL_fmt(s) {
|
|
var line = UTIL_time() + ' ' + s;
|
|
if (UTIL_events.push(line) > UTIL_max_events) {
|
|
// Drop from head.
|
|
UTIL_events.splice(0, UTIL_events.length - UTIL_max_events);
|
|
}
|
|
return line;
|
|
}
|