[PS-2264] Make password protected exports support account's iterations and argon2 (#4479)

* Fix encrypted export using fixed PBKDF2 iterations

* Replace hardcoded KdfType in importer

* Clean up kdf handling in password-protected export

* Extract BitwardenPasswordProtectedFileFormat

* Rename bitwarden-json-types

* Move StateService import to fix linting issue

* Make linter happy

* Use abstraction instead of implementation

---------

Co-authored-by: Daniel James Smith <djsmith@web.de>
This commit is contained in:
Bernd Schoolmann 2023-03-31 13:49:07 +02:00 committed by GitHub
parent 1c88465316
commit 1f472ea309
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 37 additions and 27 deletions

View File

@ -466,7 +466,8 @@ export default class MainBackground {
this.cipherService,
this.apiService,
this.cryptoService,
this.cryptoFunctionService
this.cryptoFunctionService,
this.stateService
);
this.notificationsService = new NotificationsService(
this.syncService,

View File

@ -378,7 +378,8 @@ export class Main {
this.cipherService,
this.apiService,
this.cryptoService,
this.cryptoFunctionService
this.cryptoFunctionService,
this.stateService
);
this.auditService = new AuditService(this.cryptoFunctionService, this.apiService);

View File

@ -472,6 +472,7 @@ import { AbstractThemingService } from "./theming/theming.service.abstraction";
ApiServiceAbstraction,
CryptoServiceAbstraction,
CryptoFunctionServiceAbstraction,
StateServiceAbstraction,
],
},
{

View File

@ -9,6 +9,7 @@ import { Utils } from "@bitwarden/common/misc/utils";
import { EncString } from "@bitwarden/common/models/domain/enc-string";
import { CipherWithIdExport as CipherExport } from "@bitwarden/common/models/export/cipher-with-ids.export";
import { ExportService } from "@bitwarden/common/services/export.service";
import { StateService } from "@bitwarden/common/services/state.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
@ -144,6 +145,7 @@ describe("ExportService", () => {
let cipherService: SubstituteOf<CipherService>;
let folderService: SubstituteOf<FolderService>;
let cryptoService: SubstituteOf<CryptoService>;
let stateService: SubstituteOf<StateService>;
beforeEach(() => {
apiService = Substitute.for<ApiService>();
@ -160,7 +162,8 @@ describe("ExportService", () => {
cipherService,
apiService,
cryptoService,
cryptoFunctionService
cryptoFunctionService,
stateService
);
});

View File

@ -1,5 +1,7 @@
import * as papa from "papaparse";
import { BitwardenPasswordProtectedFileFormat } from "@bitwarden/importer/src/importers/bitwarden/bitwarden-password-protected-types";
import { ApiService } from "../abstractions/api.service";
import { CryptoService } from "../abstractions/crypto.service";
import { CryptoFunctionService } from "../abstractions/cryptoFunction.service";
@ -7,12 +9,13 @@ import {
ExportFormat,
ExportService as ExportServiceAbstraction,
} from "../abstractions/export.service";
import { StateService } from "../abstractions/state.service";
import { CollectionData } from "../admin-console/models/data/collection.data";
import { Collection } from "../admin-console/models/domain/collection";
import { CollectionDetailsResponse } from "../admin-console/models/response/collection.response";
import { CollectionView } from "../admin-console/models/view/collection.view";
import { KdfConfig } from "../auth/models/domain/kdf-config";
import { DEFAULT_PBKDF2_ITERATIONS, KdfType } from "../enums/kdfType";
import { KdfType } from "../enums/kdfType";
import { Utils } from "../misc/utils";
import { CipherWithIdExport as CipherExport } from "../models/export/cipher-with-ids.export";
import { CollectionWithIdExport as CollectionExport } from "../models/export/collection-with-id.export";
@ -34,7 +37,8 @@ export class ExportService implements ExportServiceAbstraction {
private cipherService: CipherService,
private apiService: ApiService,
private cryptoService: CryptoService,
private cryptoFunctionService: CryptoFunctionService
private cryptoFunctionService: CryptoFunctionService,
private stateService: StateService
) {}
async getExport(format: ExportFormat = "csv", organizationId?: string): Promise<string> {
@ -54,24 +58,23 @@ export class ExportService implements ExportServiceAbstraction {
? await this.getOrganizationExport(organizationId, "json")
: await this.getExport("json");
const kdfType: KdfType = await this.stateService.getKdfType();
const kdfConfig: KdfConfig = await this.stateService.getKdfConfig();
const salt = Utils.fromBufferToB64(await this.cryptoFunctionService.randomBytes(16));
const kdfConfig = new KdfConfig(DEFAULT_PBKDF2_ITERATIONS);
const key = await this.cryptoService.makePinKey(
password,
salt,
KdfType.PBKDF2_SHA256,
kdfConfig
);
const key = await this.cryptoService.makePinKey(password, salt, kdfType, kdfConfig);
const encKeyValidation = await this.cryptoService.encrypt(Utils.newGuid(), key);
const encText = await this.cryptoService.encrypt(clearText, key);
const jsonDoc: any = {
const jsonDoc: BitwardenPasswordProtectedFileFormat = {
encrypted: true,
passwordProtected: true,
salt: salt,
kdfType: kdfType,
kdfIterations: kdfConfig.iterations,
kdfType: KdfType.PBKDF2_SHA256,
kdfMemory: kdfConfig.memory,
kdfParallelism: kdfConfig.parallelism,
encKeyValidation_DO_NOT_EDIT: encKeyValidation.encryptedString,
data: encText.encryptedString,
};

View File

@ -9,17 +9,7 @@ import { ImportResult } from "../../models/import-result";
import { Importer } from "../importer";
import { BitwardenJsonImporter } from "./bitwarden-json-importer";
interface BitwardenPasswordProtectedFileFormat {
encrypted: boolean;
passwordProtected: boolean;
salt: string;
kdfIterations: number;
kdfType: number;
encKeyValidation_DO_NOT_EDIT: string;
data: string;
}
import { BitwardenPasswordProtectedFileFormat } from "./bitwarden-password-protected-types";
export class BitwardenPasswordProtectedImporter extends BitwardenJsonImporter implements Importer {
private key: SymmetricCryptoKey;
@ -50,8 +40,8 @@ export class BitwardenPasswordProtectedImporter extends BitwardenJsonImporter im
this.key = await this.cryptoService.makePinKey(
this.password,
jdoc.salt,
KdfType.PBKDF2_SHA256,
new KdfConfig(jdoc.kdfIterations)
jdoc.kdfType,
new KdfConfig(jdoc.kdfIterations, jdoc.kdfMemory, jdoc.kdfParallelism)
);
const encKeyValidation = new EncString(jdoc.encKeyValidation_DO_NOT_EDIT);

View File

@ -0,0 +1,11 @@
export interface BitwardenPasswordProtectedFileFormat {
encrypted: boolean;
passwordProtected: boolean;
salt: string;
kdfIterations: number;
kdfMemory?: number;
kdfParallelism?: number;
kdfType: number;
encKeyValidation_DO_NOT_EDIT: string;
data: string;
}