From 6436bb65e2c14fd50c1946a9553e6f8585900f14 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Thu, 7 Jun 2018 23:38:17 -0400 Subject: [PATCH] IE fixes and polyfills --- jslib | 2 +- locales/en/messages.json | 389 ---------------------- locales/es/messages.json | 5 - package-lock.json | 5 + package.json | 1 + src/app/app.module.ts | 1 - src/app/main.ts | 2 - src/app/polyfills.ts | 36 +- src/locales/en/messages.json | 388 +++++++++++++++++++++- src/scripts/webcrypto-shim.js | 602 ---------------------------------- src/scss/styles.scss | 7 +- webpack.config.js | 3 +- 12 files changed, 415 insertions(+), 1026 deletions(-) delete mode 100644 locales/en/messages.json delete mode 100644 locales/es/messages.json delete mode 100644 src/scripts/webcrypto-shim.js diff --git a/jslib b/jslib index 8211e19db0..f40451ecc5 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit 8211e19db02a350606a970b828dbb16bc83788bb +Subproject commit f40451ecc5b891139347c51d804ab314502d98e7 diff --git a/locales/en/messages.json b/locales/en/messages.json deleted file mode 100644 index 82a1f2cdcf..0000000000 --- a/locales/en/messages.json +++ /dev/null @@ -1,389 +0,0 @@ -{ - "whatTypeOfItem": { - "message": "What type of item is this?" - }, - "name": { - "message": "Name" - }, - "uri": { - "message": "URI" - }, - "uriPosition": { - "message": "URI $POSITION$", - "description": "A listing of URIs. Ex: URI 1, URI 2, URI 3, etc.", - "placeholders": { - "position": { - "content": "$1", - "example": "2" - } - } - }, - "newUri": { - "message": "New URI" - }, - "username": { - "message": "Username" - }, - "password": { - "message": "Password" - }, - "notes": { - "message": "Notes" - }, - "customFields": { - "message": "Custom Fields" - }, - "cardholderName": { - "message": "Cardholder Name" - }, - "number": { - "message": "Number" - }, - "brand": { - "message": "Brand" - }, - "expiration": { - "message": "Expiration" - }, - "securityCode": { - "message": "Security Code" - }, - "identityName": { - "message": "Identity Name" - }, - "company": { - "message": "Company" - }, - "ssn": { - "message": "Social Security Number" - }, - "passportNumber": { - "message": "Passport Number" - }, - "licenseNumber": { - "message": "License Number" - }, - "email": { - "message": "Email" - }, - "phone": { - "message": "Phone" - }, - "january": { - "message": "January" - }, - "february": { - "message": "February" - }, - "march": { - "message": "March" - }, - "april": { - "message": "April" - }, - "may": { - "message": "May" - }, - "june": { - "message": "June" - }, - "july": { - "message": "July" - }, - "august": { - "message": "August" - }, - "september": { - "message": "September" - }, - "october": { - "message": "October" - }, - "november": { - "message": "November" - }, - "december": { - "message": "December" - }, - "title": { - "message": "Title" - }, - "mr": { - "message": "Mr" - }, - "mrs": { - "message": "Mrs" - }, - "ms": { - "message": "Ms" - }, - "dr": { - "message": "Dr" - }, - "expirationMonth": { - "message": "Expiration Month" - }, - "expirationYear": { - "message": "Expiration Year" - }, - "authenticatorKeyTotp": { - "message": "Authenticator Key (TOTP)" - }, - "folder": { - "message": "Folder" - }, - "newCustomField": { - "message": "New Custom Field" - }, - "value": { - "message": "Value" - }, - "cfTypeText": { - "message": "Text" - }, - "cfTypeHidden": { - "message": "Hidden" - }, - "cfTypeBoolean": { - "message": "Boolean" - }, - "remove": { - "message": "Remove" - }, - "noneFolder": { - "message": "No Folder", - "description": "This is the folder for uncategorized items" - }, - "addFolder": { - "message": "Add Folder" - }, - "editFolder": { - "message": "Edit Folder" - }, - "baseDomain": { - "message": "Base domain" - }, - "host": { - "message": "Host", - "description": "A URL's host value. For example, the host of https://sub.domain.com:443 is 'sub.domain.com:443'." - }, - "exact": { - "message": "Exact" - }, - "startsWith": { - "message": "Starts with" - }, - "regEx": { - "message": "Regular expression", - "description": "A programming term, also known as 'RegEx'." - }, - "matchDetection": { - "message": "Match Detection", - "description": "URI match detection for auto-fill." - }, - "defaultMatchDetection": { - "message": "Default match detection", - "description": "Default URI match detection for auto-fill." - }, - "never": { - "message": "Never" - }, - "toggleVisibility": { - "message": "Toggle Visibility" - }, - "generatePassword": { - "message": "Generate Password" - }, - "checkPassword": { - "message": "Check if password has been exposed." - }, - "passwordExposed": { - "message": "This password has been exposed $VALUE$ time(s) in data breaches. You should change it.", - "placeholders": { - "value": { - "content": "$1", - "example": "2" - } - } - }, - "passwordSafe": { - "message": "This password was not found in any known data breaches. It should be safe to use." - }, - "save": { - "message": "Save" - }, - "cancel": { - "message": "Cancel" - }, - "close": { - "message": "Close" - }, - "delete": { - "message": "Delete" - }, - "favorite": { - "message": "Favorite" - }, - "unfavorite": { - "message": "Unfavorite" - }, - "edit": { - "message": "Edit" - }, - "searchCollection": { - "message": "Search Collection" - }, - "searchFolder": { - "message": "Search Folder" - }, - "searchFavorites": { - "message": "Search Favorites" - }, - "searchType": { - "message": "Search Type", - "description": "Search item type" - }, - "searchVault": { - "message": "Search Vault" - }, - "allItems": { - "message": "All Items" - }, - "favorites": { - "message": "Favorites" - }, - "types": { - "message": "Types" - }, - "typeLogin": { - "message": "Login" - }, - "typeCard": { - "message": "Card" - }, - "typeIdentity": { - "message": "Identity" - }, - "typeSecureNote": { - "message": "Secure Note" - }, - "folders": { - "message": "Folders" - }, - "collections": { - "message": "Collections" - }, - "firstName": { - "message": "First Name" - }, - "middleName": { - "message": "Middle Name" - }, - "lastName": { - "message": "Last Name" - }, - "address1": { - "message": "Address 1" - }, - "address2": { - "message": "Address 2" - }, - "address3": { - "message": "Address 3" - }, - "cityTown": { - "message": "City / Town" - }, - "stateProvince": { - "message": "State / Province" - }, - "zipPostalCode": { - "message": "Zip / Postal Code" - }, - "country": { - "message": "Country" - }, - "shared": { - "message": "Shared" - }, - "attachments": { - "message": "Attachments" - }, - "select": { - "message": "Select" - }, - "addItem": { - "message": "Add Item" - }, - "editItem": { - "message": "Edit Item" - }, - "ex": { - "message": "ex.", - "description": "Short abbreviation for 'example'." - }, - "other": { - "message": "Other" - }, - "share": { - "message": "Share" - }, - "valueCopied": { - "message": "$VALUE$ copied", - "description": "Value has been copied to the clipboard.", - "placeholders": { - "value": { - "content": "$1", - "example": "Password" - } - } - }, - "copyValue": { - "message": "Copy Value", - "description": "Copy value to clipboard" - }, - "copyPassword": { - "message": "Copy Password", - "description": "Copy password to clipboard" - }, - "copyUsername": { - "message": "Copy Username", - "description": "Copy username to clipboard" - }, - "copyNumber": { - "message": "Copy Number", - "description": "Copy credit card number" - }, - "copySecurityCode": { - "message": "Copy Security Code", - "description": "Copy credit card security code (CVV)" - }, - "copyUri": { - "message": "Copy URI", - "description": "Copy URI to clipboard" - }, - "myVault": { - "message": "My Vault" - }, - "shareSelected": { - "message": "Share Selected" - }, - "deleteSelected": { - "message": "Delete Selected" - }, - "moveSelected": { - "message": "Move Selected" - }, - "selectAll": { - "message": "Select All" - }, - "unselectAll": { - "message": "Unselect All" - }, - "value": { - "message": "Value" - }, - "launch": { - "message": "Launch" - } -} diff --git a/locales/es/messages.json b/locales/es/messages.json deleted file mode 100644 index 54364e7310..0000000000 --- a/locales/es/messages.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "hello": { - "message": "hola mundo" - } -} diff --git a/package-lock.json b/package-lock.json index 85f3155a8f..e88ac7464c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9585,6 +9585,11 @@ "resolved": "https://registry.npmjs.org/web-animations-js/-/web-animations-js-2.3.1.tgz", "integrity": "sha1-Om2bwVGWN3qQ+OKAP6UmIWWwRRA=" }, + "webcrypto-shim": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/webcrypto-shim/-/webcrypto-shim-0.1.4.tgz", + "integrity": "sha512-I2lnL+K2oPNE9ryVHwo42oDnt8XQ9E1KKMGCmcT7OXaAKPmUeCi/G0nUgLR6M6Ztj05ZCxLMGf5bXNaSo+wURg==" + }, "webpack": { "version": "4.10.2", "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.10.2.tgz", diff --git a/package.json b/package.json index 7f9c5c67fa..5b48742394 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "sweetalert": "2.1.0", "tldjs": "2.0.0", "web-animations-js": "2.3.1", + "webcrypto-shim": "0.1.4", "whatwg-fetch": "^2.0.4", "zone.js": "0.8.19" } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index ef309fcfd9..61d24feac5 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,5 +1,4 @@ import 'core-js'; -import 'zone.js/dist/zone'; import { ToasterModule } from 'angular2-toaster'; import { Angulartics2Module } from 'angulartics2'; diff --git a/src/app/main.ts b/src/app/main.ts index 249e91fbeb..23ad91fa6d 100644 --- a/src/app/main.ts +++ b/src/app/main.ts @@ -1,8 +1,6 @@ import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -import './polyfills'; - import 'bootstrap'; import 'jquery'; import 'popper.js'; diff --git a/src/app/polyfills.ts b/src/app/polyfills.ts index f9972be113..c0d0ee3685 100644 --- a/src/app/polyfills.ts +++ b/src/app/polyfills.ts @@ -1,23 +1,17 @@ -import 'whatwg-fetch'; -import '../scripts/webcrypto-shim'; - -// IE9, IE10 and IE11 requires all of the following polyfills. -import 'core-js/es6/symbol'; -import 'core-js/es6/object'; -import 'core-js/es6/function'; -import 'core-js/es6/parse-int'; -import 'core-js/es6/parse-float'; -import 'core-js/es6/number'; -import 'core-js/es6/math'; -import 'core-js/es6/string'; -import 'core-js/es6/date'; -import 'core-js/es6/array'; -import 'core-js/es6/regexp'; -import 'core-js/es6/map'; -import 'core-js/es6/weak-map'; -import 'core-js/es6/set'; - -import 'core-js/es6/reflect'; +/* tslint:disable */ +import 'core-js/es6'; import 'core-js/es7/reflect'; +require('zone.js/dist/zone'); -import 'zone.js/dist/zone'; +if (process.env.ENV === 'production') { + // Production +} else { + // Development and test + Error['stackTraceLimit'] = Infinity; + require('zone.js/dist/long-stack-trace-zone'); +} + +// Other polyfills +require('whatwg-fetch'); +require('webcrypto-shim'); +/* tslint:enable */ diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index a02b3399a4..82a1f2cdcf 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -1,5 +1,389 @@ { - "hello": { - "message": "hello world" + "whatTypeOfItem": { + "message": "What type of item is this?" + }, + "name": { + "message": "Name" + }, + "uri": { + "message": "URI" + }, + "uriPosition": { + "message": "URI $POSITION$", + "description": "A listing of URIs. Ex: URI 1, URI 2, URI 3, etc.", + "placeholders": { + "position": { + "content": "$1", + "example": "2" + } + } + }, + "newUri": { + "message": "New URI" + }, + "username": { + "message": "Username" + }, + "password": { + "message": "Password" + }, + "notes": { + "message": "Notes" + }, + "customFields": { + "message": "Custom Fields" + }, + "cardholderName": { + "message": "Cardholder Name" + }, + "number": { + "message": "Number" + }, + "brand": { + "message": "Brand" + }, + "expiration": { + "message": "Expiration" + }, + "securityCode": { + "message": "Security Code" + }, + "identityName": { + "message": "Identity Name" + }, + "company": { + "message": "Company" + }, + "ssn": { + "message": "Social Security Number" + }, + "passportNumber": { + "message": "Passport Number" + }, + "licenseNumber": { + "message": "License Number" + }, + "email": { + "message": "Email" + }, + "phone": { + "message": "Phone" + }, + "january": { + "message": "January" + }, + "february": { + "message": "February" + }, + "march": { + "message": "March" + }, + "april": { + "message": "April" + }, + "may": { + "message": "May" + }, + "june": { + "message": "June" + }, + "july": { + "message": "July" + }, + "august": { + "message": "August" + }, + "september": { + "message": "September" + }, + "october": { + "message": "October" + }, + "november": { + "message": "November" + }, + "december": { + "message": "December" + }, + "title": { + "message": "Title" + }, + "mr": { + "message": "Mr" + }, + "mrs": { + "message": "Mrs" + }, + "ms": { + "message": "Ms" + }, + "dr": { + "message": "Dr" + }, + "expirationMonth": { + "message": "Expiration Month" + }, + "expirationYear": { + "message": "Expiration Year" + }, + "authenticatorKeyTotp": { + "message": "Authenticator Key (TOTP)" + }, + "folder": { + "message": "Folder" + }, + "newCustomField": { + "message": "New Custom Field" + }, + "value": { + "message": "Value" + }, + "cfTypeText": { + "message": "Text" + }, + "cfTypeHidden": { + "message": "Hidden" + }, + "cfTypeBoolean": { + "message": "Boolean" + }, + "remove": { + "message": "Remove" + }, + "noneFolder": { + "message": "No Folder", + "description": "This is the folder for uncategorized items" + }, + "addFolder": { + "message": "Add Folder" + }, + "editFolder": { + "message": "Edit Folder" + }, + "baseDomain": { + "message": "Base domain" + }, + "host": { + "message": "Host", + "description": "A URL's host value. For example, the host of https://sub.domain.com:443 is 'sub.domain.com:443'." + }, + "exact": { + "message": "Exact" + }, + "startsWith": { + "message": "Starts with" + }, + "regEx": { + "message": "Regular expression", + "description": "A programming term, also known as 'RegEx'." + }, + "matchDetection": { + "message": "Match Detection", + "description": "URI match detection for auto-fill." + }, + "defaultMatchDetection": { + "message": "Default match detection", + "description": "Default URI match detection for auto-fill." + }, + "never": { + "message": "Never" + }, + "toggleVisibility": { + "message": "Toggle Visibility" + }, + "generatePassword": { + "message": "Generate Password" + }, + "checkPassword": { + "message": "Check if password has been exposed." + }, + "passwordExposed": { + "message": "This password has been exposed $VALUE$ time(s) in data breaches. You should change it.", + "placeholders": { + "value": { + "content": "$1", + "example": "2" + } + } + }, + "passwordSafe": { + "message": "This password was not found in any known data breaches. It should be safe to use." + }, + "save": { + "message": "Save" + }, + "cancel": { + "message": "Cancel" + }, + "close": { + "message": "Close" + }, + "delete": { + "message": "Delete" + }, + "favorite": { + "message": "Favorite" + }, + "unfavorite": { + "message": "Unfavorite" + }, + "edit": { + "message": "Edit" + }, + "searchCollection": { + "message": "Search Collection" + }, + "searchFolder": { + "message": "Search Folder" + }, + "searchFavorites": { + "message": "Search Favorites" + }, + "searchType": { + "message": "Search Type", + "description": "Search item type" + }, + "searchVault": { + "message": "Search Vault" + }, + "allItems": { + "message": "All Items" + }, + "favorites": { + "message": "Favorites" + }, + "types": { + "message": "Types" + }, + "typeLogin": { + "message": "Login" + }, + "typeCard": { + "message": "Card" + }, + "typeIdentity": { + "message": "Identity" + }, + "typeSecureNote": { + "message": "Secure Note" + }, + "folders": { + "message": "Folders" + }, + "collections": { + "message": "Collections" + }, + "firstName": { + "message": "First Name" + }, + "middleName": { + "message": "Middle Name" + }, + "lastName": { + "message": "Last Name" + }, + "address1": { + "message": "Address 1" + }, + "address2": { + "message": "Address 2" + }, + "address3": { + "message": "Address 3" + }, + "cityTown": { + "message": "City / Town" + }, + "stateProvince": { + "message": "State / Province" + }, + "zipPostalCode": { + "message": "Zip / Postal Code" + }, + "country": { + "message": "Country" + }, + "shared": { + "message": "Shared" + }, + "attachments": { + "message": "Attachments" + }, + "select": { + "message": "Select" + }, + "addItem": { + "message": "Add Item" + }, + "editItem": { + "message": "Edit Item" + }, + "ex": { + "message": "ex.", + "description": "Short abbreviation for 'example'." + }, + "other": { + "message": "Other" + }, + "share": { + "message": "Share" + }, + "valueCopied": { + "message": "$VALUE$ copied", + "description": "Value has been copied to the clipboard.", + "placeholders": { + "value": { + "content": "$1", + "example": "Password" + } + } + }, + "copyValue": { + "message": "Copy Value", + "description": "Copy value to clipboard" + }, + "copyPassword": { + "message": "Copy Password", + "description": "Copy password to clipboard" + }, + "copyUsername": { + "message": "Copy Username", + "description": "Copy username to clipboard" + }, + "copyNumber": { + "message": "Copy Number", + "description": "Copy credit card number" + }, + "copySecurityCode": { + "message": "Copy Security Code", + "description": "Copy credit card security code (CVV)" + }, + "copyUri": { + "message": "Copy URI", + "description": "Copy URI to clipboard" + }, + "myVault": { + "message": "My Vault" + }, + "shareSelected": { + "message": "Share Selected" + }, + "deleteSelected": { + "message": "Delete Selected" + }, + "moveSelected": { + "message": "Move Selected" + }, + "selectAll": { + "message": "Select All" + }, + "unselectAll": { + "message": "Unselect All" + }, + "value": { + "message": "Value" + }, + "launch": { + "message": "Launch" } } diff --git a/src/scripts/webcrypto-shim.js b/src/scripts/webcrypto-shim.js deleted file mode 100644 index 256c7efe17..0000000000 --- a/src/scripts/webcrypto-shim.js +++ /dev/null @@ -1,602 +0,0 @@ -/** - * @file Web Cryptography API shim - * @author Artem S Vybornov - * @license MIT - */ -!function (global) { - 'use strict'; - - // We are using an angular promise polyfill which is loaded after this script - //if (typeof Promise !== 'function') - // throw "Promise support required"; - - var _crypto = global.crypto || global.msCrypto; - if (!_crypto) return; - - var _subtle = _crypto.subtle || _crypto.webkitSubtle; - if (!_subtle) return; - - var _Crypto = global.Crypto || _crypto.constructor || Object, - _SubtleCrypto = global.SubtleCrypto || _subtle.constructor || Object, - _CryptoKey = global.CryptoKey || global.Key || Object; - - var isIE = !!global.msCrypto, - // ref PR: https://github.com/vibornoff/webcrypto-shim/pull/15 - isWebkit = !_crypto.subtle && !!_crypto.webkitSubtle; - if (!isIE && !isWebkit) return; - - // Added - global.cryptoShimmed = true; - - function s2a(s) { - return btoa(s).replace(/\=+$/, '').replace(/\+/g, '-').replace(/\//g, '_'); - } - - function a2s(s) { - s += '===', s = s.slice(0, -s.length % 4); - return atob(s.replace(/-/g, '+').replace(/_/g, '/')); - } - - function s2b(s) { - var b = new Uint8Array(s.length); - for (var i = 0; i < s.length; i++) b[i] = s.charCodeAt(i); - return b; - } - - function b2s(b) { - if (b instanceof ArrayBuffer) b = new Uint8Array(b); - return String.fromCharCode.apply(String, b); - } - - function alg(a) { - var r = { 'name': (a.name || a || '').toUpperCase().replace('V', 'v') }; - switch (r.name) { - case 'SHA-1': - case 'SHA-256': - case 'SHA-384': - case 'SHA-512': - break; - case 'AES-CBC': - case 'AES-GCM': - case 'AES-KW': - if (a.length) r['length'] = a.length; - break; - case 'HMAC': - if (a.hash) r['hash'] = alg(a.hash); - if (a.length) r['length'] = a.length; - break; - case 'RSAES-PKCS1-v1_5': - if (a.publicExponent) r['publicExponent'] = new Uint8Array(a.publicExponent); - if (a.modulusLength) r['modulusLength'] = a.modulusLength; - break; - case 'RSASSA-PKCS1-v1_5': - case 'RSA-OAEP': - if (a.hash) r['hash'] = alg(a.hash); - if (a.publicExponent) r['publicExponent'] = new Uint8Array(a.publicExponent); - if (a.modulusLength) r['modulusLength'] = a.modulusLength; - break; - default: - throw new SyntaxError("Bad algorithm name"); - } - return r; - }; - - function jwkAlg(a) { - return { - 'HMAC': { - 'SHA-1': 'HS1', - 'SHA-256': 'HS256', - 'SHA-384': 'HS384', - 'SHA-512': 'HS512', - }, - 'RSASSA-PKCS1-v1_5': { - 'SHA-1': 'RS1', - 'SHA-256': 'RS256', - 'SHA-384': 'RS384', - 'SHA-512': 'RS512', - }, - 'RSAES-PKCS1-v1_5': { - '': 'RSA1_5', - }, - 'RSA-OAEP': { - 'SHA-1': 'RSA-OAEP', - 'SHA-256': 'RSA-OAEP-256', - }, - 'AES-KW': { - '128': 'A128KW', - '192': 'A192KW', - '256': 'A256KW', - }, - 'AES-GCM': { - '128': 'A128GCM', - '192': 'A192GCM', - '256': 'A256GCM', - }, - 'AES-CBC': { - '128': 'A128CBC', - '192': 'A192CBC', - '256': 'A256CBC', - }, - }[a.name][(a.hash || {}).name || a.length || '']; - } - - function b2jwk(k) { - if (k instanceof ArrayBuffer || k instanceof Uint8Array) k = JSON.parse(decodeURIComponent(escape(b2s(k)))); - var jwk = { 'kty': k.kty, 'alg': k.alg, 'ext': k.ext || k.extractable }; - switch (jwk.kty) { - case 'oct': - jwk.k = k.k; - case 'RSA': - ['n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi', 'oth'].forEach(function (x) { if (x in k) jwk[x] = k[x] }); - break; - default: - throw new TypeError("Unsupported key type"); - } - return jwk; - } - - function jwk2b(k) { - var jwk = b2jwk(k); - if (isIE) jwk['extractable'] = jwk.ext, delete jwk.ext; - return s2b(unescape(encodeURIComponent(JSON.stringify(jwk)))).buffer; - } - - function pkcs2jwk(k) { - var info = b2der(k), prv = false; - if (info.length > 2) prv = true, info.shift(); // remove version from PKCS#8 PrivateKeyInfo structure - var jwk = { 'ext': true }; - switch (info[0][0]) { - case '1.2.840.113549.1.1.1': - var rsaComp = ['n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi'], - rsaKey = b2der(info[1]); - if (prv) rsaKey.shift(); // remove version from PKCS#1 RSAPrivateKey structure - for (var i = 0; i < rsaKey.length; i++) { - if (!rsaKey[i][0]) rsaKey[i] = rsaKey[i].subarray(1); - jwk[rsaComp[i]] = s2a(b2s(rsaKey[i])); - } - jwk['kty'] = 'RSA'; - break; - default: - throw new TypeError("Unsupported key type"); - } - return jwk; - } - - function jwk2pkcs(k) { - var key, info = [['', null]], prv = false; - switch (k.kty) { - case 'RSA': - var rsaComp = ['n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi'], - rsaKey = []; - for (var i = 0; i < rsaComp.length; i++) { - if (!(rsaComp[i] in k)) break; - var b = rsaKey[i] = s2b(a2s(k[rsaComp[i]])); - if (b[0] & 0x80) rsaKey[i] = new Uint8Array(b.length + 1), rsaKey[i].set(b, 1); - } - if (rsaKey.length > 2) prv = true, rsaKey.unshift(new Uint8Array([0])); // add version to PKCS#1 RSAPrivateKey structure - info[0][0] = '1.2.840.113549.1.1.1'; - key = rsaKey; - break; - default: - throw new TypeError("Unsupported key type"); - } - info.push(new Uint8Array(der2b(key)).buffer); - if (!prv) info[1] = { 'tag': 0x03, 'value': info[1] }; - else info.unshift(new Uint8Array([0])); // add version to PKCS#8 PrivateKeyInfo structure - return new Uint8Array(der2b(info)).buffer; - } - - var oid2str = { 'KoZIhvcNAQEB': '1.2.840.113549.1.1.1' }, - str2oid = { '1.2.840.113549.1.1.1': 'KoZIhvcNAQEB' }; - - function b2der(buf, ctx) { - if (buf instanceof ArrayBuffer) buf = new Uint8Array(buf); - if (!ctx) ctx = { pos: 0, end: buf.length }; - - if (ctx.end - ctx.pos < 2 || ctx.end > buf.length) throw new RangeError("Malformed DER"); - - var tag = buf[ctx.pos++], - len = buf[ctx.pos++]; - - if (len >= 0x80) { - len &= 0x7f; - if (ctx.end - ctx.pos < len) throw new RangeError("Malformed DER"); - for (var xlen = 0; len--;) xlen <<= 8, xlen |= buf[ctx.pos++]; - len = xlen; - } - - if (ctx.end - ctx.pos < len) throw new RangeError("Malformed DER"); - - var rv; - - switch (tag) { - case 0x02: // Universal Primitive INTEGER - rv = buf.subarray(ctx.pos, ctx.pos += len); - break; - case 0x03: // Universal Primitive BIT STRING - if (buf[ctx.pos++]) throw new Error("Unsupported bit string"); - len--; - case 0x04: // Universal Primitive OCTET STRING - rv = new Uint8Array(buf.subarray(ctx.pos, ctx.pos += len)).buffer; - break; - case 0x05: // Universal Primitive NULL - rv = null; - break; - case 0x06: // Universal Primitive OBJECT IDENTIFIER - var oid = btoa(b2s(buf.subarray(ctx.pos, ctx.pos += len))); - if (!(oid in oid2str)) throw new Error("Unsupported OBJECT ID " + oid); - rv = oid2str[oid]; - break; - case 0x30: // Universal Constructed SEQUENCE - rv = []; - for (var end = ctx.pos + len; ctx.pos < end;) rv.push(b2der(buf, ctx)); - break; - default: - throw new Error("Unsupported DER tag 0x" + tag.toString(16)); - } - - return rv; - } - - function der2b(val, buf) { - if (!buf) buf = []; - - var tag = 0, len = 0, - pos = buf.length + 2; - - buf.push(0, 0); // placeholder - - if (val instanceof Uint8Array) { // Universal Primitive INTEGER - tag = 0x02, len = val.length; - for (var i = 0; i < len; i++) buf.push(val[i]); - } - else if (val instanceof ArrayBuffer) { // Universal Primitive OCTET STRING - tag = 0x04, len = val.byteLength, val = new Uint8Array(val); - for (var i = 0; i < len; i++) buf.push(val[i]); - } - else if (val === null) { // Universal Primitive NULL - tag = 0x05, len = 0; - } - else if (typeof val === 'string' && val in str2oid) { // Universal Primitive OBJECT IDENTIFIER - var oid = s2b(atob(str2oid[val])); - tag = 0x06, len = oid.length; - for (var i = 0; i < len; i++) buf.push(oid[i]); - } - else if (val instanceof Array) { // Universal Constructed SEQUENCE - for (var i = 0; i < val.length; i++) der2b(val[i], buf); - tag = 0x30, len = buf.length - pos; - } - else if (typeof val === 'object' && val.tag === 0x03 && val.value instanceof ArrayBuffer) { // Tag hint - val = new Uint8Array(val.value), tag = 0x03, len = val.byteLength; - buf.push(0); for (var i = 0; i < len; i++) buf.push(val[i]); - len++; - } - else { - throw new Error("Unsupported DER value " + val); - } - - if (len >= 0x80) { - var xlen = len, len = 4; - buf.splice(pos, 0, (xlen >> 24) & 0xff, (xlen >> 16) & 0xff, (xlen >> 8) & 0xff, xlen & 0xff); - while (len > 1 && !(xlen >> 24)) xlen <<= 8, len--; - if (len < 4) buf.splice(pos, 4 - len); - len |= 0x80; - } - - buf.splice(pos - 2, 2, tag, len); - - return buf; - } - - function CryptoKey(key, alg, ext, use) { - Object.defineProperties(this, { - _key: { - value: key - }, - type: { - value: key.type, - enumerable: true, - }, - extractable: { - value: (ext === undefined) ? key.extractable : ext, - enumerable: true, - }, - algorithm: { - value: (alg === undefined) ? key.algorithm : alg, - enumerable: true, - }, - usages: { - value: (use === undefined) ? key.usages : use, - enumerable: true, - }, - }); - } - - function isPubKeyUse(u) { - return u === 'verify' || u === 'encrypt' || u === 'wrapKey'; - } - - function isPrvKeyUse(u) { - return u === 'sign' || u === 'decrypt' || u === 'unwrapKey'; - } - - ['generateKey', 'importKey', 'unwrapKey'] - .forEach(function (m) { - var _fn = _subtle[m]; - - _subtle[m] = function (a, b, c) { - var args = [].slice.call(arguments), - ka, kx, ku; - - switch (m) { - case 'generateKey': - ka = alg(a), kx = b, ku = c; - break; - case 'importKey': - ka = alg(c), kx = args[3], ku = args[4]; - if (a === 'jwk') { - b = b2jwk(b); - if (!b.alg) b.alg = jwkAlg(ka); - if (!b.key_ops) b.key_ops = (b.kty !== 'oct') ? ('d' in b) ? ku.filter(isPrvKeyUse) : ku.filter(isPubKeyUse) : ku.slice(); - args[1] = jwk2b(b); - } - break; - case 'unwrapKey': - ka = args[4], kx = args[5], ku = args[6]; - args[2] = c._key; - break; - } - - if (m === 'generateKey' && ka.name === 'HMAC' && ka.hash) { - ka.length = ka.length || { 'SHA-1': 512, 'SHA-256': 512, 'SHA-384': 1024, 'SHA-512': 1024 }[ka.hash.name]; - return _subtle.importKey('raw', _crypto.getRandomValues(new Uint8Array((ka.length + 7) >> 3)), ka, kx, ku); - } - - if (isWebkit && m === 'generateKey' && ka.name === 'RSASSA-PKCS1-v1_5' && (!ka.modulusLength || ka.modulusLength >= 2048)) { - a = alg(a), a.name = 'RSAES-PKCS1-v1_5', delete a.hash; - return _subtle.generateKey(a, true, ['encrypt', 'decrypt']) - .then(function (k) { - return Promise.all([ - _subtle.exportKey('jwk', k.publicKey), - _subtle.exportKey('jwk', k.privateKey), - ]); - }) - .then(function (keys) { - keys[0].alg = keys[1].alg = jwkAlg(ka); - keys[0].key_ops = ku.filter(isPubKeyUse), keys[1].key_ops = ku.filter(isPrvKeyUse); - return Promise.all([ - _subtle.importKey('jwk', keys[0], ka, true, keys[0].key_ops), - _subtle.importKey('jwk', keys[1], ka, kx, keys[1].key_ops), - ]); - }) - .then(function (keys) { - return { - publicKey: keys[0], - privateKey: keys[1], - }; - }); - } - - if ((isWebkit || (isIE && (ka.hash || {}).name === 'SHA-1')) - && m === 'importKey' && a === 'jwk' && ka.name === 'HMAC' && b.kty === 'oct') { - return _subtle.importKey('raw', s2b(a2s(b.k)), c, args[3], args[4]); - } - - if (isWebkit && m === 'importKey' && (a === 'spki' || a === 'pkcs8')) { - return _subtle.importKey('jwk', pkcs2jwk(b), c, args[3], args[4]); - } - - if (isIE && m === 'unwrapKey') { - return _subtle.decrypt(args[3], c, b) - .then(function (k) { - return _subtle.importKey(a, k, args[4], args[5], args[6]); - }); - } - - var op; - try { - op = _fn.apply(_subtle, args); - } - catch (e) { - return Promise.reject(e); - } - - if (isIE) { - op = new Promise(function (res, rej) { - op.onabort = - op.onerror = function (e) { rej(e) }; - op.oncomplete = function (r) { res(r.target.result) }; - }); - } - - op = op.then(function (k) { - if (ka.name === 'HMAC') { - if (!ka.length) ka.length = 8 * k.algorithm.length; - } - if (ka.name.search('RSA') == 0) { - if (!ka.modulusLength) ka.modulusLength = (k.publicKey || k).algorithm.modulusLength; - if (!ka.publicExponent) ka.publicExponent = (k.publicKey || k).algorithm.publicExponent; - } - if (k.publicKey && k.privateKey) { - k = { - publicKey: new CryptoKey(k.publicKey, ka, kx, ku.filter(isPubKeyUse)), - privateKey: new CryptoKey(k.privateKey, ka, kx, ku.filter(isPrvKeyUse)), - }; - } - else { - k = new CryptoKey(k, ka, kx, ku); - } - return k; - }); - - return op; - } - }); - - ['exportKey', 'wrapKey'] - .forEach(function (m) { - var _fn = _subtle[m]; - - _subtle[m] = function (a, b, c) { - var args = [].slice.call(arguments); - - switch (m) { - case 'exportKey': - args[1] = b._key; - break; - case 'wrapKey': - args[1] = b._key, args[2] = c._key; - break; - } - - if ((isWebkit || (isIE && (b.algorithm.hash || {}).name === 'SHA-1')) - && m === 'exportKey' && a === 'jwk' && b.algorithm.name === 'HMAC') { - args[0] = 'raw'; - } - - if (isWebkit && m === 'exportKey' && (a === 'spki' || a === 'pkcs8')) { - args[0] = 'jwk'; - } - - if (isIE && m === 'wrapKey') { - return _subtle.exportKey(a, b) - .then(function (k) { - if (a === 'jwk') k = s2b(unescape(encodeURIComponent(JSON.stringify(b2jwk(k))))); - return _subtle.encrypt(args[3], c, k); - }); - } - - var op; - try { - op = _fn.apply(_subtle, args); - } - catch (e) { - return Promise.reject(e); - } - - if (isIE) { - op = new Promise(function (res, rej) { - op.onabort = - op.onerror = function (e) { rej(e) }; - op.oncomplete = function (r) { res(r.target.result) }; - }); - } - - if (m === 'exportKey' && a === 'jwk') { - op = op.then(function (k) { - if ((isWebkit || (isIE && (b.algorithm.hash || {}).name === 'SHA-1')) - && b.algorithm.name === 'HMAC') { - return { 'kty': 'oct', 'alg': jwkAlg(b.algorithm), 'key_ops': b.usages.slice(), 'ext': true, 'k': s2a(b2s(k)) }; - } - k = b2jwk(k); - if (!k.alg) k['alg'] = jwkAlg(b.algorithm); - if (!k.key_ops) k['key_ops'] = (b.type === 'public') ? b.usages.filter(isPubKeyUse) : (b.type === 'private') ? b.usages.filter(isPrvKeyUse) : b.usages.slice(); - return k; - }); - } - - if (isWebkit && m === 'exportKey' && (a === 'spki' || a === 'pkcs8')) { - op = op.then(function (k) { - k = jwk2pkcs(b2jwk(k)); - return k; - }); - } - - return op; - } - }); - - ['encrypt', 'decrypt', 'sign', 'verify'] - .forEach(function (m) { - var _fn = _subtle[m]; - - _subtle[m] = function (a, b, c, d) { - if (isIE && (!c.byteLength || (d && !d.byteLength))) - throw new Error("Empy input is not allowed"); - - var args = [].slice.call(arguments), - ka = alg(a); - - if (isIE && m === 'decrypt' && ka.name === 'AES-GCM') { - var tl = a.tagLength >> 3; - args[2] = (c.buffer || c).slice(0, c.byteLength - tl), - a.tag = (c.buffer || c).slice(c.byteLength - tl); - } - - args[1] = b._key; - - var op; - try { - op = _fn.apply(_subtle, args); - } - catch (e) { - return Promise.reject(e); - } - - if (isIE) { - op = new Promise(function (res, rej) { - op.onabort = - op.onerror = function (e) { - rej(e); - }; - - op.oncomplete = function (r) { - var r = r.target.result; - - if (m === 'encrypt' && r instanceof AesGcmEncryptResult) { - var c = r.ciphertext, t = r.tag; - r = new Uint8Array(c.byteLength + t.byteLength); - r.set(new Uint8Array(c), 0); - r.set(new Uint8Array(t), c.byteLength); - r = r.buffer; - } - - res(r); - }; - }); - } - - return op; - } - }); - - if (isIE) { - var _digest = _subtle.digest; - - _subtle['digest'] = function (a, b) { - if (!b.byteLength) - throw new Error("Empy input is not allowed"); - - var op; - try { - op = _digest.call(_subtle, a, b); - } - catch (e) { - return Promise.reject(e); - } - - op = new Promise(function (res, rej) { - op.onabort = - op.onerror = function (e) { rej(e) }; - op.oncomplete = function (r) { res(r.target.result) }; - }); - - return op; - }; - - global.crypto = Object.create(_crypto, { - getRandomValues: { value: function (a) { return _crypto.getRandomValues(a) } }, - subtle: { value: _subtle }, - }); - - global.CryptoKey = CryptoKey; - } - - if (isWebkit) { - _crypto.subtle = _subtle; - - global.Crypto = _Crypto; - global.SubtleCrypto = _SubtleCrypto; - global.CryptoKey = CryptoKey; - } -}(typeof window === 'undefined' ? typeof self === 'undefined' ? this : self : window); diff --git a/src/scss/styles.scss b/src/scss/styles.scss index 5b0d06c22f..0673432cf9 100644 --- a/src/scss/styles.scss +++ b/src/scss/styles.scss @@ -122,7 +122,7 @@ body { } .modal-footer { - justify-content: initial; + justify-content: flex-start; background-color: $input-bg; } @@ -156,7 +156,7 @@ app-vault { tr:hover { td:last-child .dropdown button { - visibility: initial; + visibility: visible; } } @@ -170,10 +170,12 @@ app-vault { td:first-child { width: 35px; + max-width: 35px; } td:nth-child(2) { width: 45px; + max-width: 45px; text-align: center; img { @@ -188,6 +190,7 @@ app-vault { td:last-child { width: 72px; + max-width: 72px; text-align: right; .btn { diff --git a/webpack.config.js b/webpack.config.js index d45e0f80ed..0adf768629 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -78,7 +78,7 @@ const plugins = [ new HtmlWebpackPlugin({ template: './src/index.html', filename: 'index.html', - chunks: ['app/vendor', 'app/main'], + chunks: ['app/polyfills', 'app/main'], }), new HtmlWebpackPlugin({ template: './src/connectors/duo.html', @@ -138,6 +138,7 @@ const config = { devtool: 'source-map', serve: serve, entry: { + 'app/polyfills': './src/app/polyfills.ts', 'app/main': './src/app/main.ts', 'connectors/u2f': './src/connectors/u2f.js', 'connectors/duo': './src/connectors/duo.js',