mirror of
https://github.com/gnh1201/welsonjs.git
synced 2025-05-07 12:16:04 +00:00
Add TFA (TOTP) endpoint
This commit is contained in:
parent
8190c80f1a
commit
54af38b6b6
|
@ -125,6 +125,14 @@ namespace WelsonJS.Launcher.Tools
|
|||
return;
|
||||
}
|
||||
|
||||
// Serve TFA request
|
||||
const string tfaPrefix = "tfa/";
|
||||
if (path.StartsWith(tfaPrefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ServeTfaRequest(context, path.Substring(tfaPrefix.Length - 1));
|
||||
return;
|
||||
}
|
||||
|
||||
// Serve a resource
|
||||
ServeResource(context, GetResource(_resourceName), "text/html");
|
||||
}
|
||||
|
@ -251,6 +259,24 @@ namespace WelsonJS.Launcher.Tools
|
|||
}
|
||||
}
|
||||
|
||||
private void ServeTfaRequest(HttpListenerContext context, string endpoint)
|
||||
{
|
||||
Tfa _tfa = new Tfa();
|
||||
|
||||
if (endpoint.Equals("/pubkey"))
|
||||
{
|
||||
ServeResource(context, _tfa.GetPubKey(), "text/plain", 200);
|
||||
return;
|
||||
}
|
||||
|
||||
ServeResource(context);
|
||||
}
|
||||
|
||||
private void ServeResource(HttpListenerContext context)
|
||||
{
|
||||
ServeResource(context, "<error>Not Found</error>", "application/xml", 404);
|
||||
}
|
||||
|
||||
private void ServeResource(HttpListenerContext context, byte[] data, string mimeType = "text/html", int statusCode = 200)
|
||||
{
|
||||
string xmlHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
|
||||
|
|
65
WelsonJS.Toolkit/WelsonJS.Launcher/Tools/Tfa.cs
Normal file
65
WelsonJS.Toolkit/WelsonJS.Launcher/Tools/Tfa.cs
Normal file
|
@ -0,0 +1,65 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace WelsonJS.Launcher.Tools
|
||||
{
|
||||
public class Tfa
|
||||
{
|
||||
private const string Base32Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
|
||||
public int GetOtp(string key)
|
||||
{
|
||||
byte[] binaryKey = DecodeBase32(key.Replace(" ", ""));
|
||||
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds() / 30;
|
||||
byte[] timestampBytes = BitConverter.GetBytes(timestamp);
|
||||
Array.Reverse(timestampBytes); // Ensure big-endian order
|
||||
|
||||
using (var hmac = new HMACSHA1(binaryKey))
|
||||
{
|
||||
byte[] hash = hmac.ComputeHash(timestampBytes);
|
||||
int offset = hash[hash.Length - 1] & 0xF;
|
||||
|
||||
int otp = ((hash[offset] & 0x7F) << 24) |
|
||||
((hash[offset + 1] & 0xFF) << 16) |
|
||||
((hash[offset + 2] & 0xFF) << 8) |
|
||||
(hash[offset + 3] & 0xFF);
|
||||
|
||||
return otp % 1000000; // Ensure 6-digit OTP
|
||||
}
|
||||
}
|
||||
|
||||
public string GetPubKey()
|
||||
{
|
||||
var rand = new Random();
|
||||
var key = new char[16];
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
key[i] = Base32Chars[rand.Next(Base32Chars.Length)];
|
||||
}
|
||||
return string.Join(" ", Enumerable.Range(0, 4).Select(i => new string(key, i * 4, 4)));
|
||||
}
|
||||
|
||||
private static byte[] DecodeBase32(string key)
|
||||
{
|
||||
int buffer = 0, bitsLeft = 0;
|
||||
var binaryKey = new List<byte>();
|
||||
|
||||
foreach (char c in key)
|
||||
{
|
||||
int value = Base32Chars.IndexOf(c);
|
||||
if (value < 0) continue; // Ignore invalid characters
|
||||
|
||||
buffer = (buffer << 5) + value;
|
||||
bitsLeft += 5;
|
||||
if (bitsLeft >= 8)
|
||||
{
|
||||
bitsLeft -= 8;
|
||||
binaryKey.Add((byte)((buffer >> bitsLeft) & 0xFF));
|
||||
}
|
||||
}
|
||||
return binaryKey.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -101,6 +101,7 @@
|
|||
<DependentUpon>GlobalSettingsForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Tools\ResourceServer.cs" />
|
||||
<Compile Include="Tools\Tfa.cs" />
|
||||
<EmbeddedResource Include="EnvForm.resx">
|
||||
<DependentUpon>EnvForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
|
|
Loading…
Reference in New Issue
Block a user