Skip to content

Commit 78dcebe

Browse files
committed
algorithm support
1 parent c1a416a commit 78dcebe

File tree

12 files changed

+100
-20
lines changed

12 files changed

+100
-20
lines changed

_locales/en/messages.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,5 +324,8 @@
324324
},
325325
"digits": {
326326
"message": "Digits"
327+
},
328+
"algorithm": {
329+
"message": "Algorithm"
327330
}
328331
}

src/TODO

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
- from import normal
55
- from import otpauth
66
- from manual
7-
- export normal
8-
- export otpauth
9-
- algo
7+
- algo
8+
- export
9+
- from qr
10+
- from import normal
11+
- from import otpauth
12+
- from manual

src/background.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ async function getTotp(text: string) {
129129
let secret = "";
130130
let account: string | undefined;
131131
let issuer: string | undefined;
132+
let algorithm: string | undefined;
132133
let period: number | undefined;
133134
let digits: number | undefined;
134135

@@ -167,6 +168,8 @@ async function getTotp(text: string) {
167168
} else if (parameter[0].toLowerCase() === "digits") {
168169
digits = Number(parameter[1]);
169170
digits = isNaN(digits) ? 6 : digits;
171+
} else if (parameter[0].toLowerCase() === "algorithm") {
172+
algorithm = parameter[1];
170173
}
171174
});
172175

@@ -210,6 +213,9 @@ async function getTotp(text: string) {
210213
if (digits) {
211214
entryData[hash].digits = digits;
212215
}
216+
if (algorithm) {
217+
entryData[hash].algorithm = algorithm;
218+
}
213219
if (
214220
(await EntryStorage.hasEncryptedEntry()) !==
215221
encryption.getEncryptionStatus()

src/components/Popup/AddAccountPage.vue

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@
2222
<option v-bind:value="8">8</option>
2323
</select>
2424
<br />
25+
<label class="combo-label">{{ i18n.algorithm }}</label>
26+
<select v-model="newAccount.algorithm">
27+
<option v-bind:value="OTPAlgorithm.SHA1">SHA-1</option>
28+
<option v-bind:value="OTPAlgorithm.SHA256">SHA-256</option>
29+
<option v-bind:value="OTPAlgorithm.SHA512">SHA-512</option>
30+
</select>
31+
<br />
2532
<label class="combo-label">{{ i18n.type }}</label>
2633
<select v-model="newAccount.type">
2734
<option v-bind:value="OTPType.totp">{{ i18n.based_on_time }}</option>
@@ -36,7 +43,7 @@
3643
<script lang="ts">
3744
import Vue from "vue";
3845
import { mapState } from "vuex";
39-
import { OTPType, OTPEntry } from "../../models/otp";
46+
import { OTPType, OTPEntry, OTPAlgorithm } from "../../models/otp";
4047
4148
export default Vue.extend({
4249
data: function(): {
@@ -47,6 +54,7 @@ export default Vue.extend({
4754
type: OTPType;
4855
period: number | undefined;
4956
digits: number;
57+
algorithm: OTPAlgorithm;
5058
};
5159
} {
5260
return {
@@ -56,11 +64,12 @@ export default Vue.extend({
5664
secret: "",
5765
type: OTPType.totp,
5866
period: undefined,
59-
digits: 6
67+
digits: 6,
68+
algorithm: OTPAlgorithm.SHA1
6069
}
6170
};
6271
},
63-
computed: mapState("accounts", ["OTPType"]),
72+
computed: mapState("accounts", ["OTPType", "OTPAlgorithm"]),
6473
methods: {
6574
async addNewAccount() {
6675
this.newAccount.secret = this.newAccount.secret.replace(/ /g, "");
@@ -113,7 +122,8 @@ export default Vue.extend({
113122
secret: this.newAccount.secret,
114123
counter: 0,
115124
period: this.newAccount.period,
116-
digits: this.newAccount.digits
125+
digits: this.newAccount.digits,
126+
algorithm: this.newAccount.algorithm
117127
},
118128
this.$store.state.accounts.encryption
119129
);

src/components/Popup/EntryComponent.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,8 @@ function getQrUrl(entry: OTPEntry) {
251251
(entry.type === OTPType.totp && entry.period !== 30
252252
? "&period=" + entry.period
253253
: "") +
254-
(entry.digits !== 6 ? "&digits=" + entry.digits : "");
254+
(entry.digits !== 6 ? "&digits=" + entry.digits : "") +
255+
(entry.algorithm ? "&algorithm=" + entry.algorithm : "");
255256
const qr = QRGen(0, "L");
256257
qr.addData(otpauth);
257258
qr.make();

src/components/Popup/ExportPage.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,8 @@ function getOneLineOtpBackupFile(entryData: { [hash: string]: OTPStorage }) {
129129
(type === "totp" && otpStorage.period
130130
? "&period=" + otpStorage.period
131131
: "") +
132-
(otpStorage.digits ? "&digits=" + otpStorage.digits : "");
132+
(otpStorage.digits ? "&digits=" + otpStorage.digits : "") +
133+
(otpStorage.algorithm ? "&algorithm=" + otpStorage.algorithm : "");
133134
134135
otpAuthLines.push(otpAuthLine);
135136
}

src/definitions/otp.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ interface IOTPEntry {
1010
code: string;
1111
period: number;
1212
digits: number;
13+
algorithm: number; // OTPAlgorithm
1314
create(): Promise<void>;
1415
update(): Promise<void>;
1516
next(): Promise<void>;
@@ -38,4 +39,5 @@ interface OTPStorage {
3839
counter?: number;
3940
period?: number;
4041
digits?: number;
42+
algorithm?: string;
4143
}

src/import.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ export async function getEntryDataFromOTPAuthPerLine(importCode: string) {
107107
let secret = "";
108108
let account: string | undefined;
109109
let issuer: string | undefined;
110+
let algorithm: string | undefined;
110111
let period: number | undefined;
111112
let digits: number | undefined;
112113

@@ -145,6 +146,8 @@ export async function getEntryDataFromOTPAuthPerLine(importCode: string) {
145146
} else if (parameter[0].toLowerCase() === "digits") {
146147
digits = Number(parameter[1]);
147148
digits = isNaN(digits) ? 6 : digits;
149+
} else if (parameter[0].toLowerCase() === "algorithm") {
150+
algorithm = parameter[1];
148151
}
149152
});
150153

@@ -187,6 +190,9 @@ export async function getEntryDataFromOTPAuthPerLine(importCode: string) {
187190
if (digits) {
188191
exportData[hash].digits = digits;
189192
}
193+
if (algorithm) {
194+
exportData[hash].algorithm = algorithm;
195+
}
190196
}
191197
}
192198
}

src/models/key-utilities.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { OTPType } from "./otp";
1+
import { OTPType, OTPAlgorithm } from "./otp";
22
import * as CryptoJS from "crypto-js";
33

44
// Originally based on the JavaScript implementation as provided by Russell
@@ -96,7 +96,8 @@ export class KeyUtilities {
9696
secret: string,
9797
counter: number,
9898
period: number,
99-
len?: number
99+
len?: number,
100+
algorithm?: OTPAlgorithm
100101
) {
101102
secret = secret.replace(/\s/g, "");
102103
if (!len) {
@@ -148,10 +149,28 @@ export class KeyUtilities {
148149
}
149150
}
150151

151-
const hmacObj = CryptoJS.HmacSHA1(
152-
CryptoJS.enc.Hex.parse(time),
153-
CryptoJS.enc.Hex.parse(key)
154-
);
152+
let hmacObj: CryptoJS.WordArray;
153+
switch (algorithm) {
154+
case OTPAlgorithm.SHA256:
155+
hmacObj = CryptoJS.HmacSHA256(
156+
CryptoJS.enc.Hex.parse(time),
157+
CryptoJS.enc.Hex.parse(key)
158+
);
159+
break;
160+
case OTPAlgorithm.SHA512:
161+
hmacObj = CryptoJS.HmacSHA512(
162+
CryptoJS.enc.Hex.parse(time),
163+
CryptoJS.enc.Hex.parse(key)
164+
);
165+
break;
166+
default:
167+
hmacObj = CryptoJS.HmacSHA1(
168+
CryptoJS.enc.Hex.parse(time),
169+
CryptoJS.enc.Hex.parse(key)
170+
);
171+
break;
172+
}
173+
155174
const hmac = CryptoJS.enc.Hex.stringify(hmacObj);
156175

157176
let offset = 0;

src/models/otp.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ export enum CodeState {
1717
Encrypted = "-2"
1818
}
1919

20+
export enum OTPAlgorithm {
21+
SHA1 = 1,
22+
SHA256,
23+
SHA512
24+
}
25+
2026
export class OTPEntry implements IOTPEntry {
2127
type: OTPType;
2228
index: number;
@@ -28,6 +34,7 @@ export class OTPEntry implements IOTPEntry {
2834
counter: number;
2935
period: number;
3036
digits: number;
37+
algorithm: OTPAlgorithm;
3138
code = "&bull;&bull;&bull;&bull;&bull;&bull;";
3239

3340
constructor(
@@ -42,6 +49,7 @@ export class OTPEntry implements IOTPEntry {
4249
period?: number;
4350
hash?: string;
4451
digits?: number;
52+
algorithm?: OTPAlgorithm;
4553
},
4654
encryption?: Encryption
4755
) {
@@ -82,6 +90,11 @@ export class OTPEntry implements IOTPEntry {
8290
} else {
8391
this.digits = 6;
8492
}
93+
if (entry.algorithm) {
94+
this.algorithm = entry.algorithm;
95+
} else {
96+
this.algorithm = OTPAlgorithm.SHA1;
97+
}
8598
if (this.type === OTPType.totp && entry.period) {
8699
this.period = entry.period;
87100
} else {
@@ -162,7 +175,8 @@ export class OTPEntry implements IOTPEntry {
162175
this.secret,
163176
this.counter,
164177
this.period,
165-
this.digits
178+
this.digits,
179+
this.algorithm
166180
);
167181
} catch (error) {
168182
this.code = CodeState.Invalid;

0 commit comments

Comments
 (0)