From 5aae0c893d5aeaa2abcc212c8d8eb4d58bd42854 Mon Sep 17 00:00:00 2001 From: Vita Chumakova Date: Thu, 18 Jan 2024 18:44:00 +0400 Subject: [PATCH] update to .net 8 --- .editorconfig | 6 + .github/workflows/dotnet.yml | 8 +- YandexKeyExtractor/Decryptor.cs | 62 ++-- YandexKeyExtractor/Program.cs | 108 +++---- YandexKeyExtractor/WebHandler.cs | 315 ++++++++++--------- YandexKeyExtractor/YandexKeyExtractor.csproj | 8 +- 6 files changed, 267 insertions(+), 240 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..4d671ab --- /dev/null +++ b/.editorconfig @@ -0,0 +1,6 @@ +[*.cs] +max_line_length = 130 +charset = utf-8 +insert_final_newline = true +indent_style = tab +trim_trailing_whitespace = true diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index d464e1b..f0be41e 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -15,17 +15,17 @@ jobs: with: dotnet-version: ${{ env.DOTNET_SDK_VERSION }} - name: Publish generic - run: dotnet publish -c Release -o out/generic -p:UseAppHost=false + run: dotnet publish YandexKeyExtractor/YandexKeyExtractor.csproj -c Release -o out/generic -p:UseAppHost=false - name: Upload generic artifacts uses: actions/upload-artifact@v4 - with: + with: name: ${{ github.event.repository.name }} path: out/generic - name: Publish Windows version - run: dotnet publish -c Release -o out/win -p:PublishSingleFile=true -p:PublishTrimmed=true -r win-x64 + run: dotnet publish YandexKeyExtractor/YandexKeyExtractor.csproj -c Release -o out/win -p:PublishSingleFile=true -p:PublishTrimmed=true -r win-x64 - name: Upload Windows artifacts uses: actions/upload-artifact@v4 - with: + with: name: ${{ github.event.repository.name }}-Windows path: out/win diff --git a/YandexKeyExtractor/Decryptor.cs b/YandexKeyExtractor/Decryptor.cs index 7807c0f..1b6b45b 100644 --- a/YandexKeyExtractor/Decryptor.cs +++ b/YandexKeyExtractor/Decryptor.cs @@ -3,41 +3,49 @@ using System.Text; using CryptSharp.Utility; using NaCl; -namespace YandexKeyExtractor { - public static class Decryptor { - public static string? Decrypt(string encryptedText, string password) { - string base64Text = NormalizeBase64(encryptedText); +namespace YandexKeyExtractor; - ReadOnlySpan textBytes = Convert.FromBase64String(base64Text).AsSpan(); +public static class Decryptor +{ + public static string? Decrypt(string encryptedText, string password) + { + var base64Text = NormalizeBase64(encryptedText); - const byte saltLength = 16; - ReadOnlySpan textData = textBytes[..^saltLength]; - ReadOnlySpan textSalt = textBytes[^saltLength..]; + ReadOnlySpan textBytes = Convert.FromBase64String(base64Text).AsSpan(); - byte[]? generatedPassword = SCrypt.ComputeDerivedKey(Encoding.UTF8.GetBytes(password), textSalt.ToArray(), 32768, 20, 1, null, 32); + const byte SaltLength = 16; + var textData = textBytes[..^SaltLength]; + var textSalt = textBytes[^SaltLength..]; - using XSalsa20Poly1305 secureBox = new(generatedPassword); + var generatedPassword = SCrypt.ComputeDerivedKey( + Encoding.UTF8.GetBytes(password), textSalt.ToArray(), 32768, 20, 1, null, 32 + ); - const byte nonceLength = 24; - ReadOnlySpan nonce = textData[..nonceLength]; - ReadOnlySpan dataWithMac = textData[nonceLength..]; + using XSalsa20Poly1305 secureBox = new(generatedPassword); - - Span message = dataWithMac.Length <= 4096 ? stackalloc byte[dataWithMac.Length] : new byte[dataWithMac.Length]; + const byte NonceLength = 24; + var nonce = textData[..NonceLength]; + var dataWithMac = textData[NonceLength..]; - const byte macLength = 16; - ReadOnlySpan data = dataWithMac[macLength..]; - ReadOnlySpan mac = dataWithMac[..macLength]; - return secureBox.TryDecrypt(message, data, mac, nonce) ? new string(Encoding.UTF8.GetString(message).TrimEnd('\0')) : null; - } + var message = dataWithMac.Length <= 4096 ? stackalloc byte[dataWithMac.Length] : new byte[dataWithMac.Length]; - private static string NormalizeBase64(string encryptedText) { - return encryptedText.Replace('-', '+').Replace('_', '/') + (encryptedText.Length % 4) switch { - 2 => "==", - 3 => "=", - _ => "" - }; - } + const byte MacLength = 16; + var data = dataWithMac[MacLength..]; + var mac = dataWithMac[..MacLength]; + + return secureBox.TryDecrypt(message, data, mac, nonce) + ? new string(Encoding.UTF8.GetString(message).TrimEnd('\0')) + : null; + } + + private static string NormalizeBase64(string encryptedText) + { + return encryptedText.Replace('-', '+').Replace('_', '/') + (encryptedText.Length % 4) switch + { + 2 => "==", + 3 => "=", + _ => "" + }; } } diff --git a/YandexKeyExtractor/Program.cs b/YandexKeyExtractor/Program.cs index 2350da0..113f809 100644 --- a/YandexKeyExtractor/Program.cs +++ b/YandexKeyExtractor/Program.cs @@ -1,65 +1,61 @@ using System; using System.IO; using System.Runtime.CompilerServices; -using System.Threading.Tasks; +using YandexKeyExtractor; -namespace YandexKeyExtractor { - internal static class Program { - private static async Task Main() { - Console.WriteLine("Initializing..."); - using WebHandler handler = WebHandler.Create(); +Console.WriteLine("Initializing..."); +using var handler = WebHandler.Create(); - string country = await handler.TryGetCountry().ConfigureAwait(false); +var country = await handler.TryGetCountry(); - PromptInput(out string phoneNumber, nameof(phoneNumber)); +PromptInput(out var phoneNumber); - phoneNumber = phoneNumber.TrimStart('+'); - string phone = await handler.GetPhoneNumberInfo(phoneNumber, country).ConfigureAwait(false); +phoneNumber = phoneNumber.TrimStart('+'); +var phone = await handler.GetPhoneNumberInfo(phoneNumber, country); - string? trackID = await handler.SendSMSCodeAndGetTrackID(phone, country).ConfigureAwait(false); - if (string.IsNullOrEmpty(trackID)) { - return; - } - - PromptInput(out string smsCode, nameof(smsCode)); - - if (!await handler.CheckCode(smsCode, trackID).ConfigureAwait(false)) { - return; - } - - if (!await handler.ValidateBackupInfo(phone, trackID, country).ConfigureAwait(false)) { - return; - } - - string? backup = await handler.GetBackupData(phone, trackID).ConfigureAwait(false); - if (string.IsNullOrEmpty(backup)) { - return; - } - - PromptInput(out string backupPassword, nameof(backupPassword)); - - Console.WriteLine("Decrypting..."); - string? message = Decryptor.Decrypt(backup, backupPassword); - if (string.IsNullOrEmpty(message)) { - Console.WriteLine("Decryption failed!"); - - return; - } - - Console.WriteLine("Successfully decrypted!"); - await File.WriteAllTextAsync("result.txt", message).ConfigureAwait(false); - Console.WriteLine($"Written {message.Split('\n').Length} authenticators to result file"); - } - - private static void PromptInput(out string result, [CallerArgumentExpression("result")] string argumentName = "") { - Console.WriteLine($"Enter {argumentName}:"); - string? input = Console.ReadLine(); - while (string.IsNullOrEmpty(input)) { - Console.WriteLine($"{argumentName} is invalid, try again:"); - input = Console.ReadLine(); - } - - result = input; - } - } +var trackID = await handler.SendSMSCodeAndGetTrackID(phone, country); +if (string.IsNullOrEmpty(trackID)) { + return; +} + +PromptInput(out var smsCode); + +if (!await handler.CheckCode(smsCode, trackID)) { + return; +} + +if (!await handler.ValidateBackupInfo(phone, trackID, country)) { + return; +} + +var backup = await handler.GetBackupData(phone, trackID); +if (string.IsNullOrEmpty(backup)) { + return; +} + +PromptInput(out var backupPassword); + +Console.WriteLine("Decrypting..."); +var message = Decryptor.Decrypt(backup, backupPassword); +if (string.IsNullOrEmpty(message)) { + Console.WriteLine("Decryption failed!"); + + return; +} + +Console.WriteLine("Successfully decrypted!"); +await File.WriteAllTextAsync("result.txt", message); +Console.WriteLine($"Written {message.Split('\n').Length} authenticators to result file"); + +return; + +static void PromptInput(out string result, [CallerArgumentExpression("result")] string argumentName = "") { + Console.WriteLine($"Enter {argumentName}:"); + var input = Console.ReadLine(); + while (string.IsNullOrEmpty(input)) { + Console.WriteLine($"{argumentName} is invalid, try again:"); + input = Console.ReadLine(); + } + + result = input; } diff --git a/YandexKeyExtractor/WebHandler.cs b/YandexKeyExtractor/WebHandler.cs index c70c21e..b63ab5a 100644 --- a/YandexKeyExtractor/WebHandler.cs +++ b/YandexKeyExtractor/WebHandler.cs @@ -11,167 +11,188 @@ using Flurl.Http; using Flurl.Serialization.TextJson; using YandexKeyExtractor.Models; -namespace YandexKeyExtractor { - public sealed class WebHandler : IDisposable { - private WebHandler(IFlurlClient client) => Client = client; - private IFlurlClient Client { get; } +namespace YandexKeyExtractor; - public void Dispose() { - Client.Dispose(); - } +public sealed class WebHandler : IDisposable +{ + private WebHandler(IFlurlClient client) => Client = client; + private IFlurlClient Client { get; } - public async Task CheckCode(string? smsCode, string? trackID) { - StatusResponse? checkCodeResponse = await Client.Request("/bundle/yakey_backup/check_code/") - .PostUrlEncodedAsync( - new { - code = smsCode, - track_id = trackID - } - ) - .ReceiveJson() - .ConfigureAwait(false); + public void Dispose() + { + Client.Dispose(); + } - return ValidateResponse(checkCodeResponse, nameof(checkCodeResponse)); - } - - public static WebHandler Create() { - JsonSerializerOptions jsonSettings = new() { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; - - IFlurlClient? client = new FlurlClient( - new HttpClient { - DefaultRequestHeaders = { UserAgent = { new ProductInfoHeaderValue("okhttp", "2.7.5") } }, - BaseAddress = new Uri("https://registrator.mobile.yandex.net/1/") + public async Task CheckCode(string? smsCode, string? trackID) + { + var checkCodeResponse = await Client.Request("/bundle/yakey_backup/check_code/") + .PostUrlEncodedAsync( + new + { + code = smsCode, + track_id = trackID } - ).Configure(settings => settings.WithTextJsonSerializer(jsonSettings)); + ) + .ReceiveJson(); - return new WebHandler(client); - } + return ValidateResponse(checkCodeResponse, nameof(checkCodeResponse)); + } - public async Task GetBackupData(string phone, string? trackID) { - BackupResponse? backupResponse = await Client.Request("/bundle/yakey_backup/download") - .PostUrlEncodedAsync( - new { - number = phone, - track_id = trackID - } - ) - .ReceiveJson() - .ConfigureAwait(false); + public static WebHandler Create() + { + JsonSerializerOptions jsonSettings = new() + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; - if (!ValidateResponse(backupResponse, nameof(backupResponse))) { - return null; + var client = new FlurlClient( + new HttpClient + { + DefaultRequestHeaders = {UserAgent = {new ProductInfoHeaderValue("okhttp", "2.7.5")}}, + BaseAddress = new Uri("https://registrator.mobile.yandex.net/1/") } + ).Configure(settings => settings.WithTextJsonSerializer(jsonSettings)); - if (string.IsNullOrEmpty(backupResponse.Backup)) { - Console.WriteLine("Fatal error - Couldn't find valid backup!"); + return new WebHandler(client); + } - return null; - } - - return backupResponse.Backup; - } - - public async Task GetPhoneNumberInfo(string? phoneNumber, string country) { - PhoneNumberResponse? phoneNumberResponse = await Client.Request("/bundle/validate/phone_number/") - .PostUrlEncodedAsync( - new { - phone_number = phoneNumber, - country - } - ).ReceiveJson() - .ConfigureAwait(false); - - ValidateResponse(phoneNumberResponse, nameof(phoneNumberResponse)); - - string phone = phoneNumberResponse?.PhoneNumber?.StandardizedNumber ?? '+' + phoneNumber; - - return phone; - } - - public async Task SendSMSCodeAndGetTrackID(string phone, string country) { - TrackResponse? trackResponse = await Client.Request("/bundle/yakey_backup/send_code/") - .PostUrlEncodedAsync( - new { - display_language = "en", - number = phone, - country - } - ) - .ReceiveJson() - .ConfigureAwait(false); - - if (!ValidateResponse(trackResponse, nameof(trackResponse))) { - return null; - } - - string? trackID = trackResponse.TrackID; - if (string.IsNullOrEmpty(trackID)) { - Console.WriteLine("Track ID is empty!"); - - return null; - } - - return trackID; - } - - public async Task TryGetCountry() { - CountryResponse? countryResponse = await Client.Request("/suggest/country") - .GetAsync() - .ReceiveJson() - .ConfigureAwait(false); - - ValidateResponse(countryResponse, nameof(countryResponse)); - - string country = countryResponse?.Country?.FirstOrDefault() ?? "ru"; - - return country; - } - - public async Task ValidateBackupInfo(string phone, string? trackID, string country) { - BackupInfoResponse? backupInfoResponse = await Client.Request("/bundle/yakey_backup/info/") - .PostUrlEncodedAsync( - new { - number = phone, - track_id = trackID, - country - } - ) - .ReceiveJson() - .ConfigureAwait(false); - - if (!ValidateResponse(backupInfoResponse, nameof(backupInfoResponse))) { - return false; - } - - if (backupInfoResponse.Info?.Updated == null) { - Console.WriteLine("Fatal error - Couldn't find valid backup!"); - - return false; - } - - return true; - } - - - private static bool ValidateResponse([NotNullWhen(true)] T? response, [CallerArgumentExpression("response")] string responseName = "") where T : StatusResponse { - if (response == null) { - Console.WriteLine(responseName + " failed!"); - - return false; - } - - if (!response.IsSuccess) { - Console.WriteLine(responseName + $" failed with error {response.Status}!"); - if (response.Errors != null) { - Console.WriteLine("Errors: " + string.Join(',', response.Errors)); + public async Task GetBackupData(string phone, string? trackID) + { + var backupResponse = await Client.Request("/bundle/yakey_backup/download") + .PostUrlEncodedAsync( + new + { + number = phone, + track_id = trackID } + ) + .ReceiveJson(); - return false; + if (!ValidateResponse(backupResponse, nameof(backupResponse))) + { + return null; + } + + if (string.IsNullOrEmpty(backupResponse.Backup)) + { + Console.WriteLine("Fatal error - Couldn't find valid backup!"); + + return null; + } + + return backupResponse.Backup; + } + + public async Task GetPhoneNumberInfo(string? phoneNumber, string country) + { + var phoneNumberResponse = await Client.Request("/bundle/validate/phone_number/") + .PostUrlEncodedAsync( + new + { + phone_number = phoneNumber, + country + } + ).ReceiveJson(); + + ValidateResponse(phoneNumberResponse, nameof(phoneNumberResponse)); + + var phone = phoneNumberResponse?.PhoneNumber?.StandardizedNumber ?? '+' + phoneNumber; + + return phone; + } + + public async Task SendSMSCodeAndGetTrackID(string phone, string country) + { + var trackResponse = await Client.Request("/bundle/yakey_backup/send_code/") + .PostUrlEncodedAsync( + new + { + display_language = "en", + number = phone, + country + } + ) + .ReceiveJson(); + + if (!ValidateResponse(trackResponse, nameof(trackResponse))) + { + return null; + } + + var trackID = trackResponse.TrackID; + if (string.IsNullOrEmpty(trackID)) + { + Console.WriteLine("Track ID is empty!"); + + return null; + } + + return trackID; + } + + public async Task TryGetCountry() + { + var countryResponse = await Client.Request("/suggest/country") + .GetAsync() + .ReceiveJson(); + + ValidateResponse(countryResponse, nameof(countryResponse)); + + var country = countryResponse?.Country?.FirstOrDefault() ?? "ru"; + + return country; + } + + public async Task ValidateBackupInfo(string phone, string? trackID, string country) + { + var backupInfoResponse = await Client.Request("/bundle/yakey_backup/info/") + .PostUrlEncodedAsync( + new + { + number = phone, + track_id = trackID, + country + } + ) + .ReceiveJson(); + + if (!ValidateResponse(backupInfoResponse, nameof(backupInfoResponse))) + { + return false; + } + + if (backupInfoResponse.Info?.Updated == null) + { + Console.WriteLine("Fatal error - Couldn't find valid backup!"); + + return false; + } + + return true; + } + + + private static bool ValidateResponse([NotNullWhen(true)] T? response, + [CallerArgumentExpression("response")] string responseName = "") where T : StatusResponse + { + if (response == null) + { + Console.WriteLine(responseName + " failed!"); + + return false; + } + + if (!response.IsSuccess) + { + Console.WriteLine(responseName + $" failed with error {response.Status}!"); + if (response.Errors != null) + { + Console.WriteLine("Errors: " + string.Join(',', response.Errors)); } - return true; + return false; } + + return true; } } diff --git a/YandexKeyExtractor/YandexKeyExtractor.csproj b/YandexKeyExtractor/YandexKeyExtractor.csproj index e7d3042..4c9c95f 100644 --- a/YandexKeyExtractor/YandexKeyExtractor.csproj +++ b/YandexKeyExtractor/YandexKeyExtractor.csproj @@ -5,9 +5,9 @@ 1.0.0.0 enable Exe - net6.0 + net8.0 - + false @@ -19,10 +19,6 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive -