mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-27 12:36:14 +01:00
convert totp service to jslib
This commit is contained in:
parent
cfbd67060a
commit
79aca025d0
@ -5,6 +5,7 @@ import {
|
|||||||
AppIdService,
|
AppIdService,
|
||||||
CryptoService,
|
CryptoService,
|
||||||
TokenService,
|
TokenService,
|
||||||
|
TotpService,
|
||||||
UserService,
|
UserService,
|
||||||
UtilsService,
|
UtilsService,
|
||||||
} from 'jslib/services';
|
} from 'jslib/services';
|
||||||
@ -17,6 +18,7 @@ import {
|
|||||||
PlatformUtilsService as PlatformUtilsServiceAbstraction,
|
PlatformUtilsService as PlatformUtilsServiceAbstraction,
|
||||||
StorageService as StorageServiceAbstraction,
|
StorageService as StorageServiceAbstraction,
|
||||||
TokenService as TokenServiceAbstraction,
|
TokenService as TokenServiceAbstraction,
|
||||||
|
TotpService as TotpServiceAbstraction,
|
||||||
UserService as UserServiceAbstraction,
|
UserService as UserServiceAbstraction,
|
||||||
UtilsService as UtilsServiceAbstraction,
|
UtilsService as UtilsServiceAbstraction,
|
||||||
} from 'jslib/abstractions';
|
} from 'jslib/abstractions';
|
||||||
@ -46,7 +48,6 @@ import LockService from '../services/lock.service';
|
|||||||
import PasswordGenerationService from '../services/passwordGeneration.service';
|
import PasswordGenerationService from '../services/passwordGeneration.service';
|
||||||
import SettingsService from '../services/settings.service';
|
import SettingsService from '../services/settings.service';
|
||||||
import SyncService from '../services/sync.service';
|
import SyncService from '../services/sync.service';
|
||||||
import TotpService from '../services/totp.service';
|
|
||||||
|
|
||||||
export default class MainBackground {
|
export default class MainBackground {
|
||||||
messagingService: MessagingServiceAbstraction;
|
messagingService: MessagingServiceAbstraction;
|
||||||
@ -68,7 +69,7 @@ export default class MainBackground {
|
|||||||
lockService: LockService;
|
lockService: LockService;
|
||||||
syncService: SyncService;
|
syncService: SyncService;
|
||||||
passwordGenerationService: PasswordGenerationService;
|
passwordGenerationService: PasswordGenerationService;
|
||||||
totpService: TotpService;
|
totpService: TotpServiceAbstraction;
|
||||||
autofillService: AutofillService;
|
autofillService: AutofillService;
|
||||||
containerService: ContainerService;
|
containerService: ContainerService;
|
||||||
|
|
||||||
|
@ -8,13 +8,13 @@ import AutofillPageDetails from '../models/domain/autofillPageDetails';
|
|||||||
import AutofillScript from '../models/domain/autofillScript';
|
import AutofillScript from '../models/domain/autofillScript';
|
||||||
|
|
||||||
import CipherService from './cipher.service';
|
import CipherService from './cipher.service';
|
||||||
import TotpService from './totp.service';
|
|
||||||
|
|
||||||
import { UtilsService } from 'jslib/services';
|
import { UtilsService } from 'jslib/services';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
PlatformUtilsService,
|
PlatformUtilsService,
|
||||||
TokenService,
|
TokenService,
|
||||||
|
TotpService,
|
||||||
UtilsService as UtilsServiceAbstraction,
|
UtilsService as UtilsServiceAbstraction,
|
||||||
} from 'jslib/abstractions';
|
} from 'jslib/abstractions';
|
||||||
|
|
||||||
|
@ -1,117 +0,0 @@
|
|||||||
import ConstantsService from './constants.service';
|
|
||||||
|
|
||||||
import { StorageService } from 'jslib/abstractions';
|
|
||||||
|
|
||||||
const b32Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
|
||||||
|
|
||||||
const TotpAlgorithm = {
|
|
||||||
name: 'HMAC',
|
|
||||||
hash: { name: 'SHA-1' },
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class TotpService {
|
|
||||||
constructor(private storageService: StorageService) {
|
|
||||||
}
|
|
||||||
|
|
||||||
async getCode(keyb32: string): Promise<string> {
|
|
||||||
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<boolean> {
|
|
||||||
return !(await this.storageService.get<boolean>(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: string[] = [];
|
|
||||||
bytes.forEach((b) => {
|
|
||||||
// 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 (let i = 0; i < s.length; i++) {
|
|
||||||
if (b32Chars.indexOf(s[i]) < 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanedInput += s[i];
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user