From 47bf7efd4aecee67c80102f93010d47d6f4a521e Mon Sep 17 00:00:00 2001 From: Cheena Malhotra <13396919+cheenamalhotra@users.noreply.github.com> Date: Fri, 14 Apr 2023 15:45:37 -0700 Subject: [PATCH] Reset IV/Key if MSAL cache file decryption fails (#22733) --- .../src/account-provider/auths/azureAuth.ts | 2 +- .../utils/fileEncryptionHelper.ts | 54 ++++++++++++------- .../account-provider/utils/msalCachePlugin.ts | 8 +-- 3 files changed, 40 insertions(+), 24 deletions(-) diff --git a/extensions/azurecore/src/account-provider/auths/azureAuth.ts b/extensions/azurecore/src/account-provider/auths/azureAuth.ts index 464bfee415..d94901cc60 100644 --- a/extensions/azurecore/src/account-provider/auths/azureAuth.ts +++ b/extensions/azurecore/src/account-provider/auths/azureAuth.ts @@ -715,7 +715,7 @@ export abstract class AzureAuth implements vscode.Disposable { //#region data modeling public createAccount(tokenClaims: TokenClaims, key: string, tenants: Tenant[]): AzureAccount { - Logger.verbose(`Token Claims acccount: ${tokenClaims.name}, TID: ${tokenClaims.tid}`); + Logger.verbose(`Token Claims acccount: ${tokenClaims.preferred_username}, TID: ${tokenClaims.tid}`); tenants.forEach((tenant) => { Logger.verbose(`Tenant ID: ${tenant.id}, Tenant Name: ${tenant.displayName}`); }); diff --git a/extensions/azurecore/src/account-provider/utils/fileEncryptionHelper.ts b/extensions/azurecore/src/account-provider/utils/fileEncryptionHelper.ts index c4409d5cee..10774db56a 100644 --- a/extensions/azurecore/src/account-provider/utils/fileEncryptionHelper.ts +++ b/extensions/azurecore/src/account-provider/utils/fileEncryptionHelper.ts @@ -26,23 +26,22 @@ export class FileEncryptionHelper { private _algorithm: string; private _bufferEncoding: BufferEncoding; private _binaryEncoding: crypto.HexBase64BinaryEncoding; + private _ivCredId = `${this._fileName}-iv`; + private _keyCredId = `${this._fileName}-key`; private _ivBuffer: Buffer | undefined; private _keyBuffer: Buffer | undefined; public async init(): Promise { - const ivCredId = `${this._fileName}-iv`; - const keyCredId = `${this._fileName}-key`; - - const iv = await this.readEncryptionKey(ivCredId); - const key = await this.readEncryptionKey(keyCredId); + const iv = await this.readEncryptionKey(this._ivCredId); + const key = await this.readEncryptionKey(this._keyCredId); if (!iv || !key) { this._ivBuffer = crypto.randomBytes(16); this._keyBuffer = crypto.randomBytes(32); - if (!await this.saveEncryptionKey(ivCredId, this._ivBuffer.toString(this._bufferEncoding)) - || !await this.saveEncryptionKey(keyCredId, this._keyBuffer.toString(this._bufferEncoding))) { + if (!await this.saveEncryptionKey(this._ivCredId, this._ivBuffer.toString(this._bufferEncoding)) + || !await this.saveEncryptionKey(this._keyCredId, this._keyBuffer.toString(this._bufferEncoding))) { Logger.error(`Encryption keys could not be saved in credential store, this will cause access token persistence issues.`); await this.showCredSaveErrorOnWindows(); } @@ -81,26 +80,43 @@ export class FileEncryptionHelper { } fileOpener = async (content: string): Promise => { - if (!this._keyBuffer || !this._ivBuffer) { - await this.init(); - } - let plaintext = content; - const decipherIv = crypto.createDecipheriv(this._algorithm, this._keyBuffer!, this._ivBuffer!); - if (this._authLibrary === AuthLibrary.ADAL) { - const split = content.split('%'); - if (split.length !== 2) { - throw new Error('File didn\'t contain the auth tag.'); + try { + if (!this._keyBuffer || !this._ivBuffer) { + await this.init(); } - (decipherIv as crypto.DecipherGCM).setAuthTag(Buffer.from(split[1], this._binaryEncoding)); - plaintext = split[0]; + let plaintext = content; + const decipherIv = crypto.createDecipheriv(this._algorithm, this._keyBuffer!, this._ivBuffer!); + if (this._authLibrary === AuthLibrary.ADAL) { + const split = content.split('%'); + if (split.length !== 2) { + throw new Error('File didn\'t contain the auth tag.'); + } + (decipherIv as crypto.DecipherGCM).setAuthTag(Buffer.from(split[1], this._binaryEncoding)); + plaintext = split[0]; + } + return `${decipherIv.update(plaintext, this._binaryEncoding, 'utf8')}${decipherIv.final('utf8')}`; + } catch (ex) { + Logger.error(`FileEncryptionHelper: Error occurred when decrypting data, IV/KEY will be reset: ${ex}`); + // Reset IV/Keys if crypto cannot encrypt/decrypt data. + // This could be a possible case of corruption of expected iv/key combination + await this.deleteEncryptionKey(this._ivCredId); + await this.deleteEncryptionKey(this._keyCredId); + this._ivBuffer = undefined; + this._keyBuffer = undefined; + await this.init(); + // Throw error so cache file can be reset to empty. + throw new Error(`Decryption failed with error: ${ex}`); } - return `${decipherIv.update(plaintext, this._binaryEncoding, 'utf8')}${decipherIv.final('utf8')}`; } protected async readEncryptionKey(credentialId: string): Promise { return (await this._credentialService.readCredential(credentialId))?.password; } + protected async deleteEncryptionKey(credentialId: string): Promise { + return (await this._credentialService.deleteCredential(credentialId)); + } + protected async saveEncryptionKey(credentialId: string, password: string): Promise { let status: boolean = false; try { diff --git a/extensions/azurecore/src/account-provider/utils/msalCachePlugin.ts b/extensions/azurecore/src/account-provider/utils/msalCachePlugin.ts index 12b5b6442d..54c1ad9943 100644 --- a/extensions/azurecore/src/account-provider/utils/msalCachePlugin.ts +++ b/extensions/azurecore/src/account-provider/utils/msalCachePlugin.ts @@ -53,8 +53,8 @@ export class MsalCachePluginProvider { } catch (e) { // Handle deserialization error in cache file in case file gets corrupted. // Clearing cache here will ensure account is marked stale so re-authentication can be triggered. - Logger.verbose(`MsalCachePlugin: Error occurred when trying to read cache file, file contents will be cleared: ${e.message}`); - await fsPromises.writeFile(this._msalFilePath, '', { encoding: 'utf8' }); + Logger.verbose(`MsalCachePlugin: Error occurred when trying to read cache file, file will be deleted: ${e.message}`); + await fsPromises.unlink(this._msalFilePath); } Logger.verbose(`MsalCachePlugin: Token read from cache successfully.`); } catch (e) { @@ -64,8 +64,8 @@ export class MsalCachePluginProvider { } else { Logger.error(`MsalCachePlugin: Failed to read from cache file: ${e}`); - Logger.verbose(`MsalCachePlugin: Error occurred when trying to read cache file, file contents will be cleared: ${e.message}`); - await fsPromises.writeFile(this._msalFilePath, '', { encoding: 'utf8' }); + Logger.verbose(`MsalCachePlugin: Error occurred when trying to read cache file, file will be deleted: ${e.message}`); + await fsPromises.unlink(this._msalFilePath); } } finally { lockFile.unlockSync(lockFilePath);