implement crypto functions in more places
This commit is contained in:
parent
fc1114a6bd
commit
5e7115f78d
|
@ -30,5 +30,4 @@ export abstract class CryptoService {
|
||||||
decrypt: (cipherString: CipherString, key?: SymmetricCryptoKey) => Promise<ArrayBuffer>;
|
decrypt: (cipherString: CipherString, key?: SymmetricCryptoKey) => Promise<ArrayBuffer>;
|
||||||
decryptToUtf8: (cipherString: CipherString, key?: SymmetricCryptoKey) => Promise<string>;
|
decryptToUtf8: (cipherString: CipherString, key?: SymmetricCryptoKey) => Promise<string>;
|
||||||
decryptFromBytes: (encBuf: ArrayBuffer, key: SymmetricCryptoKey) => Promise<ArrayBuffer>;
|
decryptFromBytes: (encBuf: ArrayBuffer, key: SymmetricCryptoKey) => Promise<ArrayBuffer>;
|
||||||
sha1: (password: string) => Promise<string>;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
static fromUtf8ToArray(str: string): Uint8Array {
|
||||||
if (Utils.isNode) {
|
if (Utils.isNode) {
|
||||||
return new Uint8Array(Buffer.from(str, 'utf8'));
|
return new Uint8Array(Buffer.from(str, 'utf8'));
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
import { AuditService as AuditServiceAbstraction } from '../abstractions/audit.service';
|
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/';
|
const PwnedPasswordsApi = 'https://api.pwnedpasswords.com/range/';
|
||||||
|
|
||||||
export class AuditService implements AuditServiceAbstraction {
|
export class AuditService implements AuditServiceAbstraction {
|
||||||
constructor(private cryptoService: CryptoService) { }
|
constructor(private cryptoFunctionService: CryptoFunctionService) { }
|
||||||
|
|
||||||
async passwordLeaked(password: string): Promise<number> {
|
async passwordLeaked(password: string): Promise<number> {
|
||||||
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 hashStart = hash.substr(0, 5);
|
||||||
const hashEnding = hash.substr(5);
|
const hashEnding = hash.substr(5);
|
||||||
|
|
||||||
const response = await fetch(PwnedPasswordsApi + hashStart);
|
const response = await fetch(PwnedPasswordsApi + hashStart);
|
||||||
const leakedHashes = await response.text();
|
const leakedHashes = await response.text();
|
||||||
|
|
||||||
const match = leakedHashes.split(/\r?\n/).find((v) => {
|
const match = leakedHashes.split(/\r?\n/).find((v) => {
|
||||||
return v.split(':')[0] === hashEnding;
|
return v.split(':')[0] === hashEnding;
|
||||||
});
|
});
|
||||||
|
|
|
@ -355,11 +355,6 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||||
macBytes != null ? macBytes.buffer : null, key);
|
macBytes != null ? macBytes.buffer : null, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
async sha1(password: string): Promise<string> {
|
|
||||||
const hash = await this.cryptoFunctionService.hash(password, 'sha1');
|
|
||||||
return Utils.fromBufferToHex(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
|
|
||||||
private async aesEncrypt(plainValue: ArrayBuffer, key: SymmetricCryptoKey): Promise<EncryptedObject> {
|
private async aesEncrypt(plainValue: ArrayBuffer, key: SymmetricCryptoKey): Promise<EncryptedObject> {
|
||||||
|
|
|
@ -1,23 +1,20 @@
|
||||||
import { ConstantsService } from './constants.service';
|
import { ConstantsService } from './constants.service';
|
||||||
|
|
||||||
|
import { CryptoFunctionService } from '../abstractions/cryptoFunction.service';
|
||||||
import { StorageService } from '../abstractions/storage.service';
|
import { StorageService } from '../abstractions/storage.service';
|
||||||
import { TotpService as TotpServiceAbstraction } from '../abstractions/totp.service';
|
import { TotpService as TotpServiceAbstraction } from '../abstractions/totp.service';
|
||||||
|
|
||||||
|
import { Utils } from '../misc/utils';
|
||||||
|
|
||||||
const b32Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
const b32Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
||||||
|
|
||||||
const TotpAlgorithm = {
|
|
||||||
name: 'HMAC',
|
|
||||||
hash: { name: 'SHA-1' },
|
|
||||||
};
|
|
||||||
|
|
||||||
export class TotpService implements TotpServiceAbstraction {
|
export class TotpService implements TotpServiceAbstraction {
|
||||||
constructor(private storageService: StorageService) {
|
constructor(private storageService: StorageService, private cryptoFunctionService: CryptoFunctionService) {}
|
||||||
}
|
|
||||||
|
|
||||||
async getCode(keyb32: string): Promise<string> {
|
async getCode(keyb32: string): Promise<string> {
|
||||||
const epoch = Math.round(new Date().getTime() / 1000.0);
|
const epoch = Math.round(new Date().getTime() / 1000.0);
|
||||||
const timeHex = this.leftpad(this.dec2hex(Math.floor(epoch / 30)), 16, '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);
|
const keyBytes = this.b32tobytes(keyb32);
|
||||||
|
|
||||||
if (!keyBytes.length || !timeBytes.length) {
|
if (!keyBytes.length || !timeBytes.length) {
|
||||||
|
@ -57,26 +54,6 @@ export class TotpService implements TotpServiceAbstraction {
|
||||||
return parseInt(s, 16);
|
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 {
|
private b32tohex(s: string): string {
|
||||||
s = s.toUpperCase();
|
s = s.toUpperCase();
|
||||||
let cleanedInput = '';
|
let cleanedInput = '';
|
||||||
|
@ -107,12 +84,11 @@ export class TotpService implements TotpServiceAbstraction {
|
||||||
}
|
}
|
||||||
|
|
||||||
private b32tobytes(s: string): Uint8Array {
|
private b32tobytes(s: string): Uint8Array {
|
||||||
return this.hex2bytes(this.b32tohex(s));
|
return Utils.fromHexToArray(this.b32tohex(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async sign(keyBytes: Uint8Array, timeBytes: Uint8Array) {
|
private async sign(keyBytes: Uint8Array, timeBytes: Uint8Array) {
|
||||||
const key = await window.crypto.subtle.importKey('raw', keyBytes, TotpAlgorithm, false, ['sign']);
|
const signature = await this.cryptoFunctionService.hmac(timeBytes.buffer, keyBytes.buffer, 'sha1');
|
||||||
const signature = await window.crypto.subtle.sign(TotpAlgorithm, key, timeBytes);
|
return Utils.fromBufferToHex(signature);
|
||||||
return this.buff2hex(signature);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ export class WebCryptoFunctionService implements CryptoFunctionService {
|
||||||
};
|
};
|
||||||
|
|
||||||
const impKey = await this.subtle.importKey('raw', passwordBuf, { name: 'PBKDF2' }, false, ['deriveBits']);
|
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<ArrayBuffer> {
|
async hash(value: string | ArrayBuffer, algorithm: 'sha1' | 'sha256' | 'sha512'): Promise<ArrayBuffer> {
|
||||||
|
|
Loading…
Reference in New Issue