Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
476 changes: 177 additions & 299 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@
},
"homepage": "https://github.com/Authenticator-Extension/Authenticator#readme",
"devDependencies": {
"@types/argon2-browser": "^1.6.0",
"@types/chrome": "^0.0.86",
"@types/crypto-js": "^3.1.43",
"@types/jssha": "2.0.0",
"css-loader": "^2.1.1",
"base64-loader": "^1.0.0",
"fork-ts-checker-webpack-plugin": "^1.3.5",
"prettier": "1.18.2",
"sass": "^1.21.0",
Expand All @@ -32,11 +33,12 @@
"vue-loader": "^15.7.0",
"vue-svg-loader": "^0.12.0",
"vue-template-compiler": "^2.6.10",
"webpack": "^4.33.0",
"webpack": "^4.38.0",
"webpack-cli": "^3.3.3",
"webpack-merge": "^4.2.1"
},
"dependencies": {
"argon2-browser": "^1.8.0",
"crypto-js": "^3.1.9-1",
"jssha": "^2.3.1",
"qrcode-generator": "^1.4.3",
Expand Down
4 changes: 4 additions & 0 deletions sass/popup.scss
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,10 @@ svg {
top: 110px;
animation: fadeout 0.2s 1 ease-in;
}

&.show {
top: 10px;
}
}

// Menu
Expand Down
5 changes: 2 additions & 3 deletions src/background.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import * as CryptoJS from "crypto-js";
// tslint:disable-next-line:ban-ts-ignore
// @ts-ignore
import QRCode from "qrcode-reader";

import { getCredentials } from "./models/credentials";
import { Encryption } from "./models/encryption";
import { EntryStorage, ManagedStorage } from "./models/storage";
import { Dropbox, Drive } from "./models/backup";
import { argon } from "./models/argon";

let cachedPassphrase = "";
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
Expand Down Expand Up @@ -167,7 +166,7 @@ async function getTotp(text: string) {
chrome.tabs.sendMessage(id, { action: "secretqr", secret });
} else {
const encryption = new Encryption(cachedPassphrase);
const hash = CryptoJS.MD5(secret).toString();
const hash = await argon.hash(secret);
if (
!/^[2-7a-z]+=*$/i.test(secret) &&
/^[0-9a-f]+$/i.test(secret) &&
Expand Down
2 changes: 1 addition & 1 deletion src/components/Import/FileImport.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export default Vue.extend({
try {
importData = JSON.parse(reader.result as string);
} catch (e) {
importData = getEntryDataFromOTPAuthPerLine(
importData = await getEntryDataFromOTPAuthPerLine(
reader.result as string
);
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Import/TextImport.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export default Vue.extend({
exportData = JSON.parse(this.importCode);
} catch (error) {
// Maybe one-otpauth-per line text
exportData = getEntryDataFromOTPAuthPerLine(this.importCode);
exportData = await getEntryDataFromOTPAuthPerLine(this.importCode);
}

try {
Expand Down
6 changes: 5 additions & 1 deletion src/components/Popup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@
/>

<PageHandler
v-bind:class="{ fadein: style.fadein, fadeout: style.fadeout }"
v-bind:class="{
fadein: style.fadein,
fadeout: style.fadeout,
show: style.show
}"
/>

<NotificationHandler />
Expand Down
31 changes: 17 additions & 14 deletions src/components/Popup/AddAccountPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
import Vue from "vue";
import { mapState } from "vuex";
import { OTPType, OTPEntry } from "../../models/otp";
import * as CryptoJS from "crypto-js";
import { argon } from "../../models/argon";

export default Vue.extend({
data: function(): {
Expand Down Expand Up @@ -91,20 +91,23 @@ export default Vue.extend({
this.newAccount.period = undefined;
}

const entry = new OTPEntry({
type,
index: 0,
issuer: this.newAccount.issuer,
account: this.newAccount.account,
encrypted: false,
hash: CryptoJS.MD5(this.newAccount.secret).toString(),
secret: this.newAccount.secret,
counter: 0,
period: this.newAccount.period
});
const entry = new OTPEntry(
{
type,
index: 0,
issuer: this.newAccount.issuer,
account: this.newAccount.account,
encrypted: false,
hash: await argon.hash(this.newAccount.secret),
secret: this.newAccount.secret,
counter: 0,
period: this.newAccount.period
},
this.$store.state.accounts.encryption
);

await entry.create(this.$store.state.accounts.encryption);
await this.$store.dispatch("accounts/updateEntries");
await entry.create();
await this.$store.dispatch("accounts/addCode", entry);
this.$store.commit("style/hideInfo");
this.$store.commit("style/toggleEdit");

Expand Down
4 changes: 2 additions & 2 deletions src/components/Popup/EntryComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export default Vue.extend({
)
) {
await entry.delete();
await this.$store.dispatch("accounts/updateEntries");
await this.$store.dispatch("accounts/deleteCode", entry.hash);
}
return;
},
Expand All @@ -141,7 +141,7 @@ export default Vue.extend({
return;
}
this.$store.commit("style/toggleHotpDisabled");
await entry.next(this.$store.state.accounts.encryption);
await entry.next();
setTimeout(() => {
this.$store.commit("style/toggleHotpDisabled");
}, 3000);
Expand Down
10 changes: 10 additions & 0 deletions src/components/Popup/LoadingPage.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<template>
<div class="text">
{{ i18n.loading }}
</div>
</template>
<script lang="ts">
import Vue from "vue";

export default Vue.extend({});
</script>
5 changes: 1 addition & 4 deletions src/components/Popup/MainBody.vue
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,7 @@ export default Vue.extend({
from: dragIndex,
to: dropIndex
});
await EntryStorage.set(
this.$store.state.accounts.encryption,
this.$store.state.accounts.entries
);
await EntryStorage.set(this.$store.state.accounts.entries);
}
);
},
Expand Down
6 changes: 4 additions & 2 deletions src/components/Popup/PageHandler.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div id="info">
<div
id="infoClose"
v-if="info !== 'EnterPasswordPage'"
v-if="!(info === 'EnterPasswordPage' || info === 'LoadingPage')"
v-on:click="hideInfo()"
>
<IconXCircle />
Expand All @@ -25,6 +25,7 @@ import DropboxPage from "./DropboxPage.vue";
import DrivePage from "./DrivePage.vue";
import StorageSyncConfPage from "./StorageSyncConfPage.vue";
import PrefrencesPage from "./PrefrencesPage.vue";
import LoadingPage from "./LoadingPage.vue";

export default Vue.extend({
computed: {
Expand All @@ -48,7 +49,8 @@ export default Vue.extend({
DropboxPage,
DrivePage,
PrefrencesPage,
StorageSyncConfPage
StorageSyncConfPage,
LoadingPage
}
});
</script>
1 change: 1 addition & 0 deletions src/definitions/module-interface.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ interface StyleState {
slideout: Boolean;
fadein: Boolean;
fadeout: Boolean;
show: Boolean;
qrfadein: Boolean;
qrfadeout: Boolean;
notificationFadein: Boolean;
Expand Down
13 changes: 7 additions & 6 deletions src/definitions/otp.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,19 @@ interface IOTPEntry {
counter: number;
code: string;
period: number;
create(encryption: IEncryption): Promise<void>;
update(encryption: IEncryption): Promise<void>;
next(encryption: IEncryption): Promise<void>;
applyEncryption(encryption: IEncryption): void;
create(): Promise<void>;
update(): Promise<void>;
next(): Promise<void>;
applyEncryption(encryption: IEncryption): Promise<void>;
changeEncryption(encryption: IEncryption): Promise<void>;
delete(): Promise<void>;
generate(): void;
rehash(encryption: IEncryption): Promise<void>;
}

interface IEncryption {
getEncryptedSecret(entry: IOTPEntry): string;
getEncryptedString(data: string): string;
getDecryptedSecret(entry: OTPStorage): string | null;
getDecryptedSecret(entry: OTPStorage): Promise<string | null>;
getEncryptionStatus(): boolean;
updateEncryptionPassword(password: string): void;
}
Expand Down
15 changes: 12 additions & 3 deletions src/import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,23 @@ import { loadI18nMessages } from "./store/i18n";
import { Encryption } from "./models/encryption";
import { EntryStorage } from "./models/storage";
import * as CryptoJS from "crypto-js";
import { argon } from "./models/argon";

async function init() {
// i18n
Vue.prototype.i18n = await loadI18nMessages();

// Load entries to global
const encryption = new Encryption(await getCachedPassphrase());
Vue.prototype.$entries = await EntryStorage.get(encryption);
const entries = await EntryStorage.get();

if (encryption.getEncryptionStatus()) {
for (const entry of entries) {
await entry.applyEncryption(encryption);
}
}

Vue.prototype.$entries = entries;
Vue.prototype.$encryption = encryption;

const instance = new Vue({
Expand Down Expand Up @@ -88,7 +97,7 @@ export function decryptBackupData(
return decryptedbackupData;
}

export function getEntryDataFromOTPAuthPerLine(importCode: string) {
export async function getEntryDataFromOTPAuthPerLine(importCode: string) {
const lines = importCode.split("\n");
const exportData: { [hash: string]: OTPStorage } = {};
for (let item of lines) {
Expand Down Expand Up @@ -153,7 +162,7 @@ export function getEntryDataFromOTPAuthPerLine(importCode: string) {
) {
continue;
} else {
const hash = CryptoJS.MD5(secret).toString();
const hash = await argon.hash(secret);
if (
!/^[2-7a-z]+=*$/i.test(secret) &&
/^[0-9a-f]+$/i.test(secret) &&
Expand Down
30 changes: 30 additions & 0 deletions src/models/argon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import * as argon2 from "argon2-browser";

export class argon {
static async hash(value: string) {
const salt = window.crypto.getRandomValues(new Uint8Array(16));
const hash = await argon2.hash({
pass: value,
salt,
mem: 1024 * 8,
type: argon2.ArgonType.Argon2id
});

return hash.encoded;
}

static compareHash(hash: string, value: string) {
return new Promise((resolve: (value: boolean) => void) => {
argon2
.verify({
pass: value,
encoded: hash
})
.then(() => resolve(true))
.catch((e: { message: string; code: number }) => {
console.error("Error decoding hash", e);
resolve(false);
});
});
}
}
4 changes: 2 additions & 2 deletions src/models/backup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class Dropbox implements BackupProvider {
// Encrypt by default if user hasn't set yet
localStorage.dropboxEncrypted = "true";
}
const exportData = await EntryStorage.getExport(
const exportData = await EntryStorage.backupGetExport(
encryption,
localStorage.dropboxEncrypted === "true"
);
Expand Down Expand Up @@ -335,7 +335,7 @@ export class Drive implements BackupProvider {
if (localStorage.driveEncrypted === undefined) {
localStorage.driveEncrypted = "true";
}
const exportData = await EntryStorage.getExport(
const exportData = await EntryStorage.backupGetExport(
encryption,
localStorage.driveEncrypted === "true"
);
Expand Down
Loading