mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-11 02:32:35 -05:00
Reset IV/Key if MSAL cache file decryption fails (#22733)
This commit is contained in:
@@ -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}`);
|
||||
});
|
||||
|
||||
@@ -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<void> {
|
||||
|
||||
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<string> => {
|
||||
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<string | undefined> {
|
||||
return (await this._credentialService.readCredential(credentialId))?.password;
|
||||
}
|
||||
|
||||
protected async deleteEncryptionKey(credentialId: string): Promise<boolean> {
|
||||
return (await this._credentialService.deleteCredential(credentialId));
|
||||
}
|
||||
|
||||
protected async saveEncryptionKey(credentialId: string, password: string): Promise<boolean> {
|
||||
let status: boolean = false;
|
||||
try {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user