github.com/LagrangeDev/LagrangeGo@v0.0.0-20240512064304-ad4a85e10cb4/client/ntlogin.go (about)

     1  package client
     2  
     3  import (
     4  	"fmt"
     5  	"net/url"
     6  	"strconv"
     7  
     8  	"github.com/LagrangeDev/LagrangeGo/client/auth"
     9  	"github.com/LagrangeDev/LagrangeGo/client/packets/pb/login"
    10  	"github.com/LagrangeDev/LagrangeGo/client/packets/wtlogin/loginState"
    11  	"github.com/LagrangeDev/LagrangeGo/internal/proto"
    12  	"github.com/LagrangeDev/LagrangeGo/utils"
    13  	"github.com/LagrangeDev/LagrangeGo/utils/crypto"
    14  )
    15  
    16  func buildNtloginCaptchaSubmit(ticket, randStr, aid string) proto.DynamicMessage {
    17  	return proto.DynamicMessage{
    18  		1: ticket,
    19  		2: randStr,
    20  		3: aid,
    21  	}
    22  }
    23  
    24  func buildNtloginRequest(uin uint32, app *auth.AppInfo, device *auth.DeviceInfo, sig *auth.SigInfo, credential []byte) ([]byte, error) {
    25  	body := proto.DynamicMessage{
    26  		1: proto.DynamicMessage{
    27  			1: proto.DynamicMessage{
    28  				1: strconv.Itoa(int(uin)),
    29  			},
    30  			2: proto.DynamicMessage{
    31  				1: app.OS,
    32  				2: device.DeviceName,
    33  				3: app.NTLoginType,
    34  				4: utils.MustParseHexStr(device.Guid),
    35  			},
    36  			3: proto.DynamicMessage{
    37  				1: device.KernelVersion,
    38  				2: app.AppID,
    39  				3: app.PackageName,
    40  			},
    41  		},
    42  		2: proto.DynamicMessage{
    43  			1: credential,
    44  		},
    45  	}
    46  
    47  	if sig.Cookies != "" {
    48  		body[1].(proto.DynamicMessage)[5] = proto.DynamicMessage{1: sig.Cookies}
    49  	}
    50  	if all(sig.CaptchaInfo[:3]) {
    51  		body[2].(proto.DynamicMessage)[2] = buildNtloginCaptchaSubmit(sig.CaptchaInfo[0], sig.CaptchaInfo[1], sig.CaptchaInfo[2])
    52  	}
    53  
    54  	data, err := crypto.AESGCMEncrypt(body.Encode(), sig.ExchangeKey)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  
    59  	return proto.DynamicMessage{
    60  		1: sig.KeySig,
    61  		3: data,
    62  		4: 1,
    63  	}.Encode(), nil
    64  }
    65  
    66  func parseNtloginResponse(response []byte, sig *auth.SigInfo) (loginState.State, error) {
    67  	var frame login.SsoNTLoginEncryptedData
    68  	err := proto.Unmarshal(response, &frame)
    69  	if err != nil {
    70  		return -1, fmt.Errorf("proto decode failed: %s", err)
    71  	}
    72  
    73  	var base login.SsoNTLoginBase
    74  	data, err := crypto.AESGCMDecrypt(frame.GcmCalc, sig.ExchangeKey)
    75  	if err != nil {
    76  		return -1, err
    77  	}
    78  	err = proto.Unmarshal(data, &base)
    79  	if err != nil {
    80  		return -1, fmt.Errorf("proto decode failed: %s", err)
    81  	}
    82  	var body login.SsoNTLoginResponse
    83  	err = proto.Unmarshal(base.Body, &body)
    84  	if err != nil {
    85  		return -1, fmt.Errorf("proto decode failed: %s", err)
    86  	}
    87  
    88  	if body.Credentials != nil {
    89  		sig.Tgt = body.Credentials.Tgt
    90  		sig.D2 = body.Credentials.D2
    91  		sig.D2Key = body.Credentials.D2Key
    92  		sig.TempPwd = body.Credentials.TempPassword
    93  		return loginState.Success, nil
    94  	}
    95  	ret := loginState.State(base.Header.Error.ErrorCode)
    96  	if ret == loginState.CaptchaVerify {
    97  		sig.Cookies = base.Header.Cookie.Cookie.Unwrap()
    98  		verifyUrl := body.Captcha.Url
    99  		aid := getAid(verifyUrl)
   100  		sig.CaptchaInfo[2] = aid
   101  		return ret, fmt.Errorf("need captcha verify: %v", verifyUrl)
   102  	}
   103  	if base.Header.Error.Tag != "" {
   104  		stat := base.Header.Error
   105  		title := stat.Tag
   106  		content := stat.Message
   107  		return ret, fmt.Errorf("login fail on ntlogin(%s): [%s]>%s", ret.Name(), title, content)
   108  	}
   109  	return ret, fmt.Errorf("login fail: %s", ret.Name())
   110  }
   111  
   112  func getAid(verifyUrl string) string {
   113  	u, _ := url.Parse(verifyUrl)
   114  	q := u.Query()
   115  	return q["sid"][0]
   116  }
   117  
   118  func all(b []string) bool {
   119  	for _, b1 := range b {
   120  		if b1 == "" {
   121  			return false
   122  		}
   123  	}
   124  	return true
   125  }