From 5e7115f78d8a87b241a43fdcd6d53b67ccf0b605 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Sat, 21 Apr 2018 23:55:21 -0400 Subject: [PATCH] implement crypto functions in more places --- src/abstractions/crypto.service.ts | 1 - src/misc/utils.ts | 12 +++++++ src/services/audit.service.ts | 9 ++--- src/services/crypto.service.ts | 5 --- src/services/totp.service.ts | 40 +++++------------------ src/services/webCryptoFunction.service.ts | 2 +- 6 files changed, 26 insertions(+), 43 deletions(-) diff --git a/src/abstractions/crypto.service.ts b/src/abstractions/crypto.service.ts index 473758ae6e..2b2adbda6e 100644 --- a/src/abstractions/crypto.service.ts +++ b/src/abstractions/crypto.service.ts @@ -30,5 +30,4 @@ export abstract class CryptoService { decrypt: (cipherString: CipherString, key?: SymmetricCryptoKey) => Promise; decryptToUtf8: (cipherString: CipherString, key?: SymmetricCryptoKey) => Promise; decryptFromBytes: (encBuf: ArrayBuffer, key: SymmetricCryptoKey) => Promise; - sha1: (password: string) => Promise; } diff --git a/src/misc/utils.ts b/src/misc/utils.ts index e02bd66f3f..bcb9f76c80 100644 --- a/src/misc/utils.ts +++ b/src/misc/utils.ts @@ -26,6 +26,18 @@ export class Utils { } } + static fromHexToArray(str: string): Uint8Array { + if (Utils.isNode) { + return new Uint8Array(Buffer.from(str, 'hex')); + } else { + const bytes = new Uint8Array(str.length / 2); + for (let i = 0; i < str.length; i += 2) { + bytes[i / 2] = parseInt(str.substr(i, 2), 16); + } + return bytes; + } + } + static fromUtf8ToArray(str: string): Uint8Array { if (Utils.isNode) { return new Uint8Array(Buffer.from(str, 'utf8')); diff --git a/src/services/audit.service.ts b/src/services/audit.service.ts index 57279a09da..d18ccbe219 100644 --- a/src/services/audit.service.ts +++ b/src/services/audit.service.ts @@ -1,19 +1,20 @@ import { AuditService as AuditServiceAbstraction } from '../abstractions/audit.service'; -import { CryptoService } from '../abstractions/crypto.service'; +import { CryptoFunctionService } from '../abstractions/cryptoFunction.service'; +import { Utils } from '../misc/utils'; const PwnedPasswordsApi = 'https://api.pwnedpasswords.com/range/'; export class AuditService implements AuditServiceAbstraction { - constructor(private cryptoService: CryptoService) { } + constructor(private cryptoFunctionService: CryptoFunctionService) { } async passwordLeaked(password: string): Promise { - const hash = (await this.cryptoService.sha1(password)).toUpperCase(); + const hashBytes = await this.cryptoFunctionService.hash(password, 'sha1'); + const hash = Utils.fromBufferToHex(hashBytes).toUpperCase(); const hashStart = hash.substr(0, 5); const hashEnding = hash.substr(5); const response = await fetch(PwnedPasswordsApi + hashStart); const leakedHashes = await response.text(); - const match = leakedHashes.split(/\r?\n/).find((v) => { return v.split(':')[0] === hashEnding; }); diff --git a/src/services/crypto.service.ts b/src/services/crypto.service.ts index e08094f7dd..ab68394959 100644 --- a/src/services/crypto.service.ts +++ b/src/services/crypto.service.ts @@ -355,11 +355,6 @@ export class CryptoService implements CryptoServiceAbstraction { macBytes != null ? macBytes.buffer : null, key); } - async sha1(password: string): Promise { - const hash = await this.cryptoFunctionService.hash(password, 'sha1'); - return Utils.fromBufferToHex(hash); - } - // Helpers private async aesEncrypt(plainValue: ArrayBuffer, key: SymmetricCryptoKey): Promise { diff --git a/src/services/totp.service.ts b/src/services/totp.service.ts index e52c11e48c..3a8fb3b896 100644 --- a/src/services/totp.service.ts +++ b/src/services/totp.service.ts @@ -1,23 +1,20 @@ import { ConstantsService } from './constants.service'; +import { CryptoFunctionService } from '../abstractions/cryptoFunction.service'; import { StorageService } from '../abstractions/storage.service'; import { TotpService as TotpServiceAbstraction } from '../abstractions/totp.service'; +import { Utils } from '../misc/utils'; + const b32Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; -const TotpAlgorithm = { - name: 'HMAC', - hash: { name: 'SHA-1' }, -}; - export class TotpService implements TotpServiceAbstraction { - constructor(private storageService: StorageService) { - } + constructor(private storageService: StorageService, private cryptoFunctionService: CryptoFunctionService) {} 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 timeBytes = Utils.fromHexToArray(timeHex); const keyBytes = this.b32tobytes(keyb32); if (!keyBytes.length || !timeBytes.length) { @@ -57,26 +54,6 @@ export class TotpService implements TotpServiceAbstraction { 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 = ''; @@ -107,12 +84,11 @@ export class TotpService implements TotpServiceAbstraction { } private b32tobytes(s: string): Uint8Array { - return this.hex2bytes(this.b32tohex(s)); + return Utils.fromHexToArray(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); + const signature = await this.cryptoFunctionService.hmac(timeBytes.buffer, keyBytes.buffer, 'sha1'); + return Utils.fromBufferToHex(signature); } } diff --git a/src/services/webCryptoFunction.service.ts b/src/services/webCryptoFunction.service.ts index 25efa06d0e..dd4a33c62b 100644 --- a/src/services/webCryptoFunction.service.ts +++ b/src/services/webCryptoFunction.service.ts @@ -38,7 +38,7 @@ export class WebCryptoFunctionService implements CryptoFunctionService { }; const impKey = await this.subtle.importKey('raw', passwordBuf, { name: 'PBKDF2' }, false, ['deriveBits']); - return await window.crypto.subtle.deriveBits(pbkdf2Params, impKey, wcLen); + return await this.subtle.deriveBits(pbkdf2Params, impKey, wcLen); } async hash(value: string | ArrayBuffer, algorithm: 'sha1' | 'sha256' | 'sha512'): Promise {