Added due credit to KiwiFlare.cs for the original Go implementation I adapted this from. Also added extra logging, refactored the challenge worker, and cleaned up the code a little.

This commit is contained in:
barelyprofessional
2024-09-01 20:24:20 +08:00
parent b3dc9a23e4
commit 7509375b43
+20 -20
View File
@@ -9,20 +9,20 @@ using NLog;
namespace KfChatDotNetBot.Services; namespace KfChatDotNetBot.Services;
// Shoutout y a t s for making the original Go implementation I adapted for this
// https://github.com/y-a-t-s/firebird
public class KiwiFlare(string kfDomain, string? proxy = null, CancellationToken? cancellationToken = null) public class KiwiFlare(string kfDomain, string? proxy = null, CancellationToken? cancellationToken = null)
{ {
private readonly Logger _logger = LogManager.GetCurrentClassLogger(); private readonly Logger _logger = LogManager.GetCurrentClassLogger();
private string? _proxy = proxy; private readonly CancellationToken _ctx = cancellationToken ?? CancellationToken.None;
private CancellationToken _ctx = cancellationToken ?? CancellationToken.None; private readonly Random _random = new();
private string _kfDomain = kfDomain;
private Random _random = new Random();
private HttpClientHandler GetHttpClientHandler() private HttpClientHandler GetHttpClientHandler()
{ {
var handler = new HttpClientHandler { AutomaticDecompression = DecompressionMethods.All }; var handler = new HttpClientHandler { AutomaticDecompression = DecompressionMethods.All };
if (_proxy != null) if (proxy != null)
{ {
handler.Proxy = new WebProxy(_proxy); handler.Proxy = new WebProxy(proxy);
handler.UseProxy = true; handler.UseProxy = true;
} }
@@ -32,7 +32,7 @@ public class KiwiFlare(string kfDomain, string? proxy = null, CancellationToken?
public async Task<KiwiFlareChallengeModel> GetChallenge() public async Task<KiwiFlareChallengeModel> GetChallenge()
{ {
using var client = new HttpClient(GetHttpClientHandler()); using var client = new HttpClient(GetHttpClientHandler());
var response = await client.GetAsync($"https://{_kfDomain}/", _ctx); var response = await client.GetAsync($"https://{kfDomain}/", _ctx);
var document = new HtmlDocument(); var document = new HtmlDocument();
document.Load(await response.Content.ReadAsStreamAsync(_ctx)); document.Load(await response.Content.ReadAsStreamAsync(_ctx));
var challengeData = document.DocumentNode.SelectSingleNode("//html[@id=\"sssg\"]"); var challengeData = document.DocumentNode.SelectSingleNode("//html[@id=\"sssg\"]");
@@ -77,25 +77,24 @@ public class KiwiFlare(string kfDomain, string? proxy = null, CancellationToken?
private async Task<KiwiFlareChallengeSolutionModel> ChallengeWorker(KiwiFlareChallengeModel challenge) private async Task<KiwiFlareChallengeSolutionModel> ChallengeWorker(KiwiFlareChallengeModel challenge)
{ {
var found = false;
var nonce = _random.NextInt64(); var nonce = _random.NextInt64();
while (!found) while (true)
{ {
// Incrementing nonce first as then I can just pass TestHash straight into found and reuse the value
nonce++; nonce++;
var input = Encoding.UTF8.GetBytes($"{challenge.Salt}{nonce}"); var input = Encoding.UTF8.GetBytes($"{challenge.Salt}{nonce}");
found = TestHash(SHA256.HashData(input), challenge.Difficulty); if (!TestHash(SHA256.HashData(input), challenge.Difficulty)) continue;
_logger.Debug($"Hash passed the test, nonce: {nonce}");
return new KiwiFlareChallengeSolutionModel
{
Nonce = nonce,
Salt = challenge.Salt
};
} }
return new KiwiFlareChallengeSolutionModel
{
Nonce = nonce,
Salt = challenge.Salt
};
} }
public async Task<KiwiFlareChallengeSolutionModel> SolveChallenge(KiwiFlareChallengeModel challenge) public async Task<KiwiFlareChallengeSolutionModel> SolveChallenge(KiwiFlareChallengeModel challenge)
{ {
var start = DateTime.UtcNow;
var worker = Task.Run(() => ChallengeWorker(challenge), _ctx); var worker = Task.Run(() => ChallengeWorker(challenge), _ctx);
try try
{ {
@@ -115,6 +114,7 @@ public class KiwiFlare(string kfDomain, string? proxy = null, CancellationToken?
throw new Exception("Challenge worker faulted"); throw new Exception("Challenge worker faulted");
} }
_logger.Debug($"Worker solved the challenge after {(DateTime.UtcNow - start).TotalMilliseconds} ms");
return worker.Result; return worker.Result;
} }
@@ -127,7 +127,7 @@ public class KiwiFlare(string kfDomain, string? proxy = null, CancellationToken?
new("b", solution.Nonce.ToString()) new("b", solution.Nonce.ToString())
}); });
var response = await client.PostAsync($"https://{_kfDomain}/.sssg/api/answer", formData, _ctx); var response = await client.PostAsync($"https://{kfDomain}/.sssg/api/answer", formData, _ctx);
var json = await response.Content.ReadFromJsonAsync<JsonElement>(_ctx); var json = await response.Content.ReadFromJsonAsync<JsonElement>(_ctx);
if (json.TryGetProperty("error", out var error)) if (json.TryGetProperty("error", out var error))
{ {
@@ -153,7 +153,7 @@ public class KiwiFlare(string kfDomain, string? proxy = null, CancellationToken?
new("f", authToken), new("f", authToken),
}); });
var response = await client.PostAsync($"https://{_kfDomain}/.sssg/api/check", formData, _ctx); var response = await client.PostAsync($"https://{kfDomain}/.sssg/api/check", formData, _ctx);
var json = await response.Content.ReadFromJsonAsync<JsonElement>(_ctx); var json = await response.Content.ReadFromJsonAsync<JsonElement>(_ctx);
if (json.TryGetProperty("error", out var error)) if (json.TryGetProperty("error", out var error))
{ {
@@ -161,7 +161,7 @@ public class KiwiFlare(string kfDomain, string? proxy = null, CancellationToken?
return false; return false;
} }
if (json.TryGetProperty("auth", out var auth)) if (json.TryGetProperty("auth", out _))
{ {
return true; return true;
} }