Skip to content

Commit

Permalink
[Core] NewDeviceVerify stage 1
Browse files Browse the repository at this point in the history
  • Loading branch information
Linwenxuan authored and Linwenxuan committed Jan 26, 2024
1 parent ba29128 commit e388d5b
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 1 deletion.
1 change: 1 addition & 0 deletions Lagrange.Core/Common/BotKeystore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ public class WtLoginSession
internal byte[]? UnusualSign { get; set; }
internal string? UnusualCookies { get; set; }
internal string? CaptchaUrl { get; set; }
internal string? NewDeviceVerifyUrl { get; set; }
internal (string, string, string)? Captcha { get; set; }

public byte[]? TempPassword { get; set; }
Expand Down
15 changes: 15 additions & 0 deletions Lagrange.Core/Event/EventArg/BotNewDeviceVerifyEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace Lagrange.Core.Event.EventArg;

public class BotNewDeviceVerifyEvent : EventBase
{
public string Url { get; }

public byte[] QrCode { get; }

public BotNewDeviceVerifyEvent(string url, byte[] qrCode)
{
Url = url;
EventMessage = $"[{nameof(BotNewDeviceVerifyEvent)}]: Url: {url}";
QrCode = qrCode;
}
}
4 changes: 3 additions & 1 deletion Lagrange.Core/Event/EventInvoker.Events.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ public partial class EventInvoker

public event LagrangeEvent<BotLogEvent>? OnBotLogEvent;

public event LagrangeEvent<BotCaptchaEvent>? OnBotCaptchaEvent;
public event LagrangeEvent<BotCaptchaEvent>? OnBotCaptchaEvent;

public event LagrangeEvent<BotNewDeviceVerifyEvent>? OnBotNewDeviceVerify;

public event LagrangeEvent<GroupInvitationEvent>? OnGroupInvitationReceived;

Expand Down
1 change: 1 addition & 0 deletions Lagrange.Core/Event/EventInvoker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ internal EventInvoker(BotContext context)
RegisterEvent((BotOfflineEvent e) => OnBotOfflineEvent?.Invoke(context, e));
RegisterEvent((BotLogEvent e) => OnBotLogEvent?.Invoke(context, e));
RegisterEvent((BotCaptchaEvent e) => OnBotCaptchaEvent?.Invoke(context, e));
RegisterEvent((BotNewDeviceVerifyEvent e) => OnBotNewDeviceVerify?.Invoke(context, e));
RegisterEvent((GroupInvitationEvent e) => OnGroupInvitationReceived?.Invoke(context, e));
RegisterEvent((FriendMessageEvent e) => OnFriendMessageReceived?.Invoke(context, e));
RegisterEvent((GroupMessageEvent e) => OnGroupMessageReceived?.Invoke(context, e));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ public async Task<bool> LoginByPassword()
case LoginCommon.Error.CaptchaVerify:
{
Collection.Log.LogInfo(Tag, "Login Success, but captcha is required, please follow the link from event");

if (Collection.Keystore.Session.CaptchaUrl != null)
{
var captchaEvent = new BotCaptchaEvent(Collection.Keystore.Session.CaptchaUrl);
Expand All @@ -203,6 +204,17 @@ public async Task<bool> LoginByPassword()
Collection.Log.LogInfo(Tag, "Captcha Url is null, please try again later");
return false;
}
case LoginCommon.Error.NewDeviceVerify:
{
Collection.Log.LogInfo(Tag, $"NewDeviceVerify Url: {Collection.Keystore.Session.NewDeviceVerifyUrl}");

var newDeviceEvent = new BotNewDeviceVerifyEvent("", Array.Empty<byte>());
Collection.Invoker.PostEvent(newDeviceEvent);

bool result = await _transEmpTask.Task;
if (result) await BotOnline();
return result;
}
default:
{
Collection.Log.LogWarning(Tag, @event is { Message: not null, Tag: not null }
Expand Down Expand Up @@ -359,5 +371,13 @@ private async Task<bool> DoUnusualEasyLogin()
return result.Count != 0 && ((UnusualEasyLoginEvent)result[0]).Success;
}

private async Task<bool> DoNewDeviceLogin()
{
Collection.Log.LogInfo(Tag, "Trying to Login by EasyLogin...");
var unusualEvent = NewDeviceLoginEvent.Create();
var result = await Collection.Business.SendEvent(unusualEvent);
return result.Count != 0 && ((NewDeviceLoginEvent)result[0]).Success;
}

public bool SubmitCaptcha(string ticket, string randStr) => _captchaTask?.TrySetResult((ticket, randStr)) ?? false;
}
17 changes: 17 additions & 0 deletions Lagrange.Core/Internal/Event/Login/NewDeviceLoginEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Lagrange.Core.Internal.Event.Login;

internal class NewDeviceLoginEvent : ProtocolEvent
{
public bool Success { get; set; }

private NewDeviceLoginEvent() : base(true) { }

private NewDeviceLoginEvent(int result) : base(result)
{
Success = result == 0;
}

public static NewDeviceLoginEvent Create() => new();

public static NewDeviceLoginEvent Result(int result) => new(result);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ internal class SsoNTLoginError
[ProtoMember(2)] public string Tag { get; set; }

[ProtoMember(3)] public string Message { get; set; }

[ProtoMember(4)] public string? NewDeviceVerifyUrl { get; set; }
}
61 changes: 61 additions & 0 deletions Lagrange.Core/Internal/Service/Login/NewDeviceLoginService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using Lagrange.Core.Common;
using Lagrange.Core.Internal.Event;
using Lagrange.Core.Internal.Event.Login;
using Lagrange.Core.Internal.Packets.Login.NTLogin;
using Lagrange.Core.Internal.Packets.Login.NTLogin.Plain;
using Lagrange.Core.Internal.Packets.Login.NTLogin.Plain.Body;
using Lagrange.Core.Utility.Binary;
using Lagrange.Core.Utility.Crypto;
using ProtoBuf;

namespace Lagrange.Core.Internal.Service.Login;

[Service("trpc.login.ecdh.EcdhService.SsoNTLoginPasswordLoginNewDevice")]
internal class NewDeviceLoginService : BaseService<NewDeviceLoginEvent>
{
protected override bool Build(NewDeviceLoginEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device,
out BinaryPacket output, out List<BinaryPacket>? extraPackets)
{
if (keystore.Session.TempPassword == null) throw new InvalidOperationException("TempPassword is null");

output = SsoNTLoginCommon.BuildNTLoginPacket(keystore, appInfo, device, keystore.Session.TempPassword);
extraPackets = null;
return true;
}

protected override bool Parse(byte[] input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device,
out NewDeviceLoginEvent output, out List<ProtocolEvent>? extraEvents)
{
if (keystore.Session.ExchangeKey == null) throw new InvalidOperationException("ExchangeKey is null");

var encrypted = Serializer.Deserialize<SsoNTLoginEncryptedData>(input.AsSpan());

if (encrypted.GcmCalc != null)
{
var decrypted = new AesGcmImpl().Decrypt(encrypted.GcmCalc, keystore.Session.ExchangeKey);
var response = Serializer.Deserialize<SsoNTLoginBase<SsoNTLoginResponse>>(decrypted.AsSpan());
var body = response.Body;

if (response.Header?.Error != null || body is not { Credentials: not null })
{
output = NewDeviceLoginEvent.Result((int)(response.Header?.Error?.ErrorCode ?? 1));
}
else
{
keystore.Session.Tgt = body.Credentials.Tgt;
keystore.Session.D2 = body.Credentials.D2;
keystore.Session.D2Key = body.Credentials.D2Key;
keystore.Session.TempPassword = body.Credentials.TempPassword;

output = NewDeviceLoginEvent.Result(0);
}
}
else
{
output = NewDeviceLoginEvent.Result(1);
}

extraEvents = null;
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ protected override bool Parse(byte[] input, BotKeystore keystore, BotAppInfo app
keystore.Session.UnusualSign = body?.Unusual?.Sig;
keystore.Session.UnusualCookies = response.Header?.Cookie?.Cookie;
keystore.Session.CaptchaUrl = body?.Captcha?.Url;
keystore.Session.NewDeviceVerifyUrl = response.Header?.Error?.NewDeviceVerifyUrl;

string? tag = response.Header?.Error?.Tag;
string? message = response.Header?.Error?.Message;
Expand Down

0 comments on commit e388d5b

Please sign in to comment.