diff --git a/src/background.html b/src/background.html
index 4a69fa81..fe0c92c3 100644
--- a/src/background.html
+++ b/src/background.html
@@ -11,7 +11,6 @@
-
diff --git a/src/background.js b/src/background.js
index cfb42b56..3c3a35b0 100644
--- a/src/background.js
+++ b/src/background.js
@@ -8,6 +8,7 @@ import i18nService from './services/i18nService.js';
import LockService from './services/lockService.js';
import PasswordGenerationService from './services/passwordGeneration.service';
import TokenService from './services/token.service';
+import TotpService from './services/totp.service';
import UtilsService from './services/utils.service';
// Model imports
@@ -93,7 +94,7 @@ var bg_isBackground = true,
window.bg_syncService = bg_syncService = new SyncService(bg_cipherService, bg_folderService, bg_userService, bg_apiService, bg_settingsService,
bg_cryptoService, logout);
window.bg_passwordGenerationService = bg_passwordGenerationService = new PasswordGenerationService(bg_cryptoService);
- window.bg_totpService = bg_totpService = new TotpService(bg_constantsService);
+ window.bg_totpService = bg_totpService = new TotpService();
window.bg_autofillService = bg_autofillService = new AutofillService(bg_utilsService, bg_totpService, bg_tokenService, bg_cipherService,
bg_constantsService);
diff --git a/src/services/environment.service.ts b/src/services/environment.service.ts
index 1ff5b98b..098a67f7 100644
--- a/src/services/environment.service.ts
+++ b/src/services/environment.service.ts
@@ -16,12 +16,12 @@ export default class EnvironmentService {
async setUrlsFromStorage(): Promise {
const urlsObj: any = await UtilsService.getObjFromStorage(ConstantsService.environmentUrlsKey);
- const urls = urlsObj[ConstantsService.environmentUrlsKey] || {
+ const urls = urlsObj || {
base: null,
api: null,
identity: null,
icons: null,
- webVault: null
+ webVault: null,
};
const envUrls = new EnvironmentUrls();
@@ -51,7 +51,7 @@ export default class EnvironmentService {
api: urls.api,
identity: urls.identity,
webVault: urls.webVault,
- icons: urls.icons
+ icons: urls.icons,
});
this.baseUrl = urls.base;
@@ -78,7 +78,7 @@ export default class EnvironmentService {
}
url = url.replace(/\/+$/g, '');
- if (!url.startsWith("http://") && !url.startsWith('https://')) {
+ if (!url.startsWith('http://') && !url.startsWith('https://')) {
url = 'https://' + url;
}
diff --git a/src/services/totp.service.ts b/src/services/totp.service.ts
new file mode 100644
index 00000000..a0f1e19f
--- /dev/null
+++ b/src/services/totp.service.ts
@@ -0,0 +1,113 @@
+import ConstantsService from './constants.service';
+import UtilsService from './utils.service';
+
+const b32Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
+
+const TotpAlgorithm = {
+ name: 'HMAC',
+ hash: { name: 'SHA-1' },
+};
+
+export default class TotpService {
+ async getCode(keyb32: string): Promise {
+ const epoch = Math.round(new Date().getTime() / 1000.0);
+ const timeHex = this.leftpad(this.dec2hex(Math.floor(epoch / 30)), 16, '0');
+ const timeBytes = this.hex2bytes(timeHex);
+ const keyBytes = this.b32tobytes(keyb32);
+
+ if (!keyBytes.length || !timeBytes.length) {
+ return null;
+ }
+
+ const hashHex = await this.sign(keyBytes, timeBytes);
+ if (!hashHex) {
+ return null;
+ }
+
+ const offset = this.hex2dec(hashHex.substring(hashHex.length - 1));
+ // tslint:disable-next-line
+ let otp = (this.hex2dec(hashHex.substr(offset * 2, 8)) & this.hex2dec('7fffffff')) + '';
+ otp = (otp).substr(otp.length - 6, 6);
+ return otp;
+ }
+
+ async isAutoCopyEnabled(): Promise {
+ return await UtilsService.getObjFromStorage(ConstantsService.disableAutoTotpCopyKey);
+ }
+
+ // Helpers
+
+ private leftpad(s: string, l: number, p: string): string {
+ if (l + 1 >= s.length) {
+ s = Array(l + 1 - s.length).join(p) + s;
+ }
+ return s;
+ }
+
+ private dec2hex(d: number): string {
+ return (d < 15.5 ? '0' : '') + Math.round(d).toString(16);
+ }
+
+ private hex2dec(s: string): number {
+ return parseInt(s, 16);
+ }
+
+ private hex2bytes(s: string): Uint8Array {
+ const bytes = new Uint8Array(s.length / 2);
+ for (let i = 0; i < s.length; i += 2) {
+ bytes[i / 2] = parseInt(s.substr(i, 2), 16);
+ }
+ return bytes;
+ }
+
+ private buff2hex(buff: ArrayBuffer): string {
+ const bytes = new Uint8Array(buff);
+ const hex = [];
+ for (const b of bytes) {
+ // tslint:disable-next-line
+ hex.push((b >>> 4).toString(16));
+ // tslint:disable-next-line
+ hex.push((b & 0xF).toString(16));
+ }
+ return hex.join('');
+ }
+
+ private b32tohex(s: string): string {
+ s = s.toUpperCase();
+ let cleanedInput = '';
+
+ for (const c of s) {
+ if (b32Chars.indexOf(c) < 0) {
+ continue;
+ }
+
+ cleanedInput += c;
+ }
+ s = cleanedInput;
+
+ let bits = '';
+ let hex = '';
+ for (let i = 0; i < s.length; i++) {
+ const byteIndex = b32Chars.indexOf(s.charAt(i));
+ if (byteIndex < 0) {
+ continue;
+ }
+ bits += this.leftpad(byteIndex.toString(2), 5, '0');
+ }
+ for (let i = 0; i + 4 <= bits.length; i += 4) {
+ const chunk = bits.substr(i, 4);
+ hex = hex + parseInt(chunk, 2).toString(16);
+ }
+ return hex;
+ }
+
+ private b32tobytes(s: string): Uint8Array {
+ return this.hex2bytes(this.b32tohex(s));
+ }
+
+ private async sign(keyBytes: Uint8Array, timeBytes: Uint8Array) {
+ const key = await window.crypto.subtle.importKey('raw', keyBytes, TotpAlgorithm, false, ['sign']);
+ const signature = await window.crypto.subtle.sign(TotpAlgorithm, key, timeBytes);
+ return this.buff2hex(signature);
+ }
+}
diff --git a/src/services/totpService.js b/src/services/totpService.js
deleted file mode 100644
index b079a605..00000000
--- a/src/services/totpService.js
+++ /dev/null
@@ -1,123 +0,0 @@
-function TotpService(constantsService) {
- this.constantsService = constantsService;
- initTotpService();
-}
-
-function initTotpService() {
- var b32Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
-
- var leftpad = function (s, l, p) {
- if (l + 1 >= s.length) {
- s = Array(l + 1 - s.length).join(p) + s;
- }
- return s;
- };
-
- var dec2hex = function (d) {
- return (d < 15.5 ? '0' : '') + Math.round(d).toString(16);
- };
-
- var hex2dec = function (s) {
- return parseInt(s, 16);
- };
-
- var hex2bytes = function (s) {
- var bytes = new Uint8Array(s.length / 2);
- for (var i = 0; i < s.length; i += 2) {
- bytes[i / 2] = parseInt(s.substr(i, 2), 16);
- }
- return bytes;
- };
-
- var buff2hex = function (buff) {
- var bytes = new Uint8Array(buff);
- var hex = [];
- for (var i = 0; i < bytes.length; i++) {
- hex.push((bytes[i] >>> 4).toString(16));
- hex.push((bytes[i] & 0xF).toString(16));
- }
- return hex.join('');
- };
-
- var b32tohex = function (s) {
- s = s.toUpperCase();
- var cleanedInput = '';
- var i;
- for (i = 0; i < s.length; i++) {
- if (b32Chars.indexOf(s[i]) < 0) {
- continue;
- }
-
- cleanedInput += s[i];
- }
- s = cleanedInput;
-
- var bits = '';
- var hex = '';
- for (i = 0; i < s.length; i++) {
- var byteIndex = b32Chars.indexOf(s.charAt(i));
- if (byteIndex < 0) {
- continue;
- }
- bits += leftpad(byteIndex.toString(2), 5, '0');
- }
- for (i = 0; i + 4 <= bits.length; i += 4) {
- var chunk = bits.substr(i, 4);
- hex = hex + parseInt(chunk, 2).toString(16);
- }
- return hex;
- };
-
- var b32tobytes = function (s) {
- return hex2bytes(b32tohex(s));
- };
-
- var sign = function (keyBytes, timeBytes) {
- return window.crypto.subtle.importKey('raw', keyBytes,
- { name: 'HMAC', hash: { name: 'SHA-1' } }, false, ['sign']).then(function (key) {
- return window.crypto.subtle.sign({ name: 'HMAC', hash: { name: 'SHA-1' } }, key, timeBytes);
- }).then(function (signature) {
- return buff2hex(signature);
- }).catch(function (err) {
- return null;
- });
- };
-
- TotpService.prototype.getCode = function (keyb32) {
- var epoch = Math.round(new Date().getTime() / 1000.0);
- var timeHex = leftpad(dec2hex(Math.floor(epoch / 30)), 16, '0');
- var timeBytes = hex2bytes(timeHex);
- var keyBytes = b32tobytes(keyb32);
-
- if (!keyBytes.length || !timeBytes.length) {
- return Q(null);
- }
-
- return sign(keyBytes, timeBytes).then(function (hashHex) {
- if (!hashHex) {
- return null;
- }
-
- var offset = hex2dec(hashHex.substring(hashHex.length - 1));
- var otp = (hex2dec(hashHex.substr(offset * 2, 8)) & hex2dec('7fffffff')) + '';
- otp = (otp).substr(otp.length - 6, 6);
- return otp;
- });
- };
-
- TotpService.prototype.isAutoCopyEnabled = function () {
- var deferred = Q.defer();
- var self = this;
-
- chrome.storage.local.get(self.constantsService.disableAutoTotpCopyKey, function (obj) {
- if (obj && !!obj[self.constantsService.disableAutoTotpCopyKey]) {
- deferred.resolve(false);
- }
- else {
- deferred.resolve(true);
- }
- });
-
- return deferred.promise;
- };
-}