mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -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
|
//#region data modeling
|
||||||
|
|
||||||
public createAccount(tokenClaims: TokenClaims, key: string, tenants: Tenant[]): AzureAccount {
|
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) => {
|
tenants.forEach((tenant) => {
|
||||||
Logger.verbose(`Tenant ID: ${tenant.id}, Tenant Name: ${tenant.displayName}`);
|
Logger.verbose(`Tenant ID: ${tenant.id}, Tenant Name: ${tenant.displayName}`);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,23 +26,22 @@ export class FileEncryptionHelper {
|
|||||||
private _algorithm: string;
|
private _algorithm: string;
|
||||||
private _bufferEncoding: BufferEncoding;
|
private _bufferEncoding: BufferEncoding;
|
||||||
private _binaryEncoding: crypto.HexBase64BinaryEncoding;
|
private _binaryEncoding: crypto.HexBase64BinaryEncoding;
|
||||||
|
private _ivCredId = `${this._fileName}-iv`;
|
||||||
|
private _keyCredId = `${this._fileName}-key`;
|
||||||
private _ivBuffer: Buffer | undefined;
|
private _ivBuffer: Buffer | undefined;
|
||||||
private _keyBuffer: Buffer | undefined;
|
private _keyBuffer: Buffer | undefined;
|
||||||
|
|
||||||
public async init(): Promise<void> {
|
public async init(): Promise<void> {
|
||||||
|
|
||||||
const ivCredId = `${this._fileName}-iv`;
|
const iv = await this.readEncryptionKey(this._ivCredId);
|
||||||
const keyCredId = `${this._fileName}-key`;
|
const key = await this.readEncryptionKey(this._keyCredId);
|
||||||
|
|
||||||
const iv = await this.readEncryptionKey(ivCredId);
|
|
||||||
const key = await this.readEncryptionKey(keyCredId);
|
|
||||||
|
|
||||||
if (!iv || !key) {
|
if (!iv || !key) {
|
||||||
this._ivBuffer = crypto.randomBytes(16);
|
this._ivBuffer = crypto.randomBytes(16);
|
||||||
this._keyBuffer = crypto.randomBytes(32);
|
this._keyBuffer = crypto.randomBytes(32);
|
||||||
|
|
||||||
if (!await this.saveEncryptionKey(ivCredId, this._ivBuffer.toString(this._bufferEncoding))
|
if (!await this.saveEncryptionKey(this._ivCredId, this._ivBuffer.toString(this._bufferEncoding))
|
||||||
|| !await this.saveEncryptionKey(keyCredId, this._keyBuffer.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.`);
|
Logger.error(`Encryption keys could not be saved in credential store, this will cause access token persistence issues.`);
|
||||||
await this.showCredSaveErrorOnWindows();
|
await this.showCredSaveErrorOnWindows();
|
||||||
}
|
}
|
||||||
@@ -81,26 +80,43 @@ export class FileEncryptionHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fileOpener = async (content: string): Promise<string> => {
|
fileOpener = async (content: string): Promise<string> => {
|
||||||
if (!this._keyBuffer || !this._ivBuffer) {
|
try {
|
||||||
await this.init();
|
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.');
|
|
||||||
}
|
}
|
||||||
(decipherIv as crypto.DecipherGCM).setAuthTag(Buffer.from(split[1], this._binaryEncoding));
|
let plaintext = content;
|
||||||
plaintext = split[0];
|
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> {
|
protected async readEncryptionKey(credentialId: string): Promise<string | undefined> {
|
||||||
return (await this._credentialService.readCredential(credentialId))?.password;
|
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> {
|
protected async saveEncryptionKey(credentialId: string, password: string): Promise<boolean> {
|
||||||
let status: boolean = false;
|
let status: boolean = false;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ export class MsalCachePluginProvider {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Handle deserialization error in cache file in case file gets corrupted.
|
// 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.
|
// 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}`);
|
Logger.verbose(`MsalCachePlugin: Error occurred when trying to read cache file, file will be deleted: ${e.message}`);
|
||||||
await fsPromises.writeFile(this._msalFilePath, '', { encoding: 'utf8' });
|
await fsPromises.unlink(this._msalFilePath);
|
||||||
}
|
}
|
||||||
Logger.verbose(`MsalCachePlugin: Token read from cache successfully.`);
|
Logger.verbose(`MsalCachePlugin: Token read from cache successfully.`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -64,8 +64,8 @@ export class MsalCachePluginProvider {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Logger.error(`MsalCachePlugin: Failed to read from cache file: ${e}`);
|
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}`);
|
Logger.verbose(`MsalCachePlugin: Error occurred when trying to read cache file, file will be deleted: ${e.message}`);
|
||||||
await fsPromises.writeFile(this._msalFilePath, '', { encoding: 'utf8' });
|
await fsPromises.unlink(this._msalFilePath);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
lockFile.unlockSync(lockFilePath);
|
lockFile.unlockSync(lockFilePath);
|
||||||
|
|||||||
Reference in New Issue
Block a user