mirror of https://github.com/Xhofe/alist
fix(aliyundrive): device session signature error (#3398)
* fix signature * fix: indent-error-flow [skip ci]pull/3409/head
parent
2ca3e0b8bc
commit
de66708b24
|
@ -3,7 +3,6 @@ package aliyundrive
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
@ -33,11 +32,6 @@ type AliDrive struct {
|
||||||
cron *cron.Cron
|
cron *cron.Cron
|
||||||
DriveId string
|
DriveId string
|
||||||
UserID string
|
UserID string
|
||||||
|
|
||||||
signature string
|
|
||||||
nonce int
|
|
||||||
privateKey *ecdsa.PrivateKey
|
|
||||||
cron2 *cron.Cron
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *AliDrive) Config() driver.Config {
|
func (d *AliDrive) Config() driver.Config {
|
||||||
|
@ -69,39 +63,28 @@ func (d *AliDrive) Init(ctx context.Context) error {
|
||||||
log.Errorf("%+v", err)
|
log.Errorf("%+v", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
if global.Has(d.UserID) {
|
||||||
// init deviceID
|
return nil
|
||||||
if len(d.DeviceID) < 64 {
|
|
||||||
d.DeviceID = utils.GetSHA256Encode(d.DeviceID)
|
|
||||||
}
|
}
|
||||||
|
// init deviceID
|
||||||
|
deviceID := utils.GetSHA256Encode(d.UserID)
|
||||||
// init privateKey
|
// init privateKey
|
||||||
d.privateKey, _ = NewPrivateKey()
|
privateKey, _ := NewPrivateKeyFromHex(deviceID)
|
||||||
|
state := State{
|
||||||
|
nonce: -1,
|
||||||
|
privateKey: privateKey,
|
||||||
|
deviceID: deviceID,
|
||||||
|
}
|
||||||
|
// store state
|
||||||
|
global.Store(d.UserID, &state)
|
||||||
// init signature
|
// init signature
|
||||||
d.sign()
|
return d.reSign()
|
||||||
d.createSession()
|
|
||||||
d.cron2 = cron.NewCron(time.Minute * 5)
|
|
||||||
d.cron2.Do(func() {
|
|
||||||
d.nonce++
|
|
||||||
d.sign()
|
|
||||||
err := d.renewSession()
|
|
||||||
if d.nonce >= 1073741823 || (err != nil && err.Error() == "device session signature error") {
|
|
||||||
d.nonce = 0
|
|
||||||
d.sign()
|
|
||||||
d.createSession()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *AliDrive) Drop(ctx context.Context) error {
|
func (d *AliDrive) Drop(ctx context.Context) error {
|
||||||
if d.cron != nil {
|
if d.cron != nil {
|
||||||
d.cron.Stop()
|
d.cron.Stop()
|
||||||
}
|
}
|
||||||
if d.cron2 != nil {
|
|
||||||
d.cron2.Stop()
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
package aliyundrive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/pkg/generic_sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type State struct {
|
||||||
|
deviceID string
|
||||||
|
signature string
|
||||||
|
nonce int
|
||||||
|
privateKey *ecdsa.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
var global = generic_sync.MapOf[string, *State]{}
|
|
@ -7,8 +7,8 @@ import (
|
||||||
|
|
||||||
type Addition struct {
|
type Addition struct {
|
||||||
driver.RootID
|
driver.RootID
|
||||||
RefreshToken string `json:"refresh_token" required:"true"`
|
RefreshToken string `json:"refresh_token" required:"true"`
|
||||||
DeviceID string `json:"device_id" required:"true"`
|
//DeviceID string `json:"device_id" required:"true"`
|
||||||
OrderBy string `json:"order_by" type:"select" options:"name,size,updated_at,created_at"`
|
OrderBy string `json:"order_by" type:"select" options:"name,size,updated_at,created_at"`
|
||||||
OrderDirection string `json:"order_direction" type:"select" options:"ASC,DESC"`
|
OrderDirection string `json:"order_direction" type:"select" options:"ASC,DESC"`
|
||||||
RapidUpload bool `json:"rapid_upload"`
|
RapidUpload bool `json:"rapid_upload"`
|
||||||
|
|
|
@ -13,15 +13,20 @@ import (
|
||||||
"github.com/dustinxie/ecc"
|
"github.com/dustinxie/ecc"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (d *AliDrive) createSession() error {
|
func (d *AliDrive) createSession() error {
|
||||||
|
state, ok := global.Load(d.UserID)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("can't load user state, user_id: %s", d.UserID)
|
||||||
|
}
|
||||||
_, err, _ := d.request("https://api.aliyundrive.com/users/v1/users/device/create_session", http.MethodPost, func(req *resty.Request) {
|
_, err, _ := d.request("https://api.aliyundrive.com/users/v1/users/device/create_session", http.MethodPost, func(req *resty.Request) {
|
||||||
req.SetBody(base.Json{
|
req.SetBody(base.Json{
|
||||||
"deviceName": "samsung",
|
"deviceName": "samsung",
|
||||||
"modelName": "SM-G9810",
|
"modelName": "SM-G9810",
|
||||||
"nonce": d.nonce,
|
"nonce": state.nonce,
|
||||||
"pubKey": PublicKeyToHex(&d.privateKey.PublicKey),
|
"pubKey": PublicKeyToHex(&state.privateKey.PublicKey),
|
||||||
"refreshToken": d.RefreshToken,
|
"refreshToken": d.RefreshToken,
|
||||||
})
|
})
|
||||||
}, nil)
|
}, nil)
|
||||||
|
@ -34,11 +39,38 @@ func (d *AliDrive) renewSession() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *AliDrive) sign() {
|
func (d *AliDrive) sign() {
|
||||||
|
state, ok := global.Load(d.UserID)
|
||||||
|
if !ok {
|
||||||
|
log.Errorf("can't load user state, user_id: %s", d.UserID)
|
||||||
|
return
|
||||||
|
}
|
||||||
secpAppID := "5dde4e1bdf9e4966b387ba58f4b3fdc3"
|
secpAppID := "5dde4e1bdf9e4966b387ba58f4b3fdc3"
|
||||||
singdata := fmt.Sprintf("%s:%s:%s:%d", secpAppID, d.DeviceID, d.UserID, d.nonce)
|
singdata := fmt.Sprintf("%s:%s:%s:%d", secpAppID, state.deviceID, d.UserID, state.nonce)
|
||||||
hash := sha256.Sum256([]byte(singdata))
|
hash := sha256.Sum256([]byte(singdata))
|
||||||
data, _ := ecc.SignBytes(d.privateKey, hash[:], ecc.RecID|ecc.LowerS)
|
data, _ := ecc.SignBytes(state.privateKey, hash[:], ecc.RecID|ecc.LowerS)
|
||||||
d.signature = hex.EncodeToString(data)
|
state.signature = hex.EncodeToString(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliDrive) reSign() error {
|
||||||
|
state, ok := global.Load(d.UserID)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("can't load user state, user_id: %s", d.UserID)
|
||||||
|
}
|
||||||
|
state.nonce++
|
||||||
|
if state.nonce >= 1073741823 {
|
||||||
|
state.nonce = 0
|
||||||
|
}
|
||||||
|
d.sign()
|
||||||
|
if state.nonce == 0 {
|
||||||
|
return d.createSession()
|
||||||
|
}
|
||||||
|
err := d.renewSession()
|
||||||
|
if err != nil && err.Error() == "device session signature error" {
|
||||||
|
state.nonce = 0
|
||||||
|
d.sign()
|
||||||
|
return d.createSession()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// do others that not defined in Driver interface
|
// do others that not defined in Driver interface
|
||||||
|
@ -69,15 +101,23 @@ func (d *AliDrive) refreshToken() error {
|
||||||
|
|
||||||
func (d *AliDrive) request(url, method string, callback base.ReqCallback, resp interface{}) ([]byte, error, RespErr) {
|
func (d *AliDrive) request(url, method string, callback base.ReqCallback, resp interface{}) ([]byte, error, RespErr) {
|
||||||
req := base.RestyClient.R()
|
req := base.RestyClient.R()
|
||||||
|
state, ok := global.Load(d.UserID)
|
||||||
|
if !ok {
|
||||||
|
if url == "https://api.aliyundrive.com/v2/user/get" {
|
||||||
|
state = &State{}
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("can't load user state, user_id: %s", d.UserID), RespErr{}
|
||||||
|
}
|
||||||
|
}
|
||||||
req.SetHeaders(map[string]string{
|
req.SetHeaders(map[string]string{
|
||||||
"Authorization": "Bearer\t" + d.AccessToken,
|
"Authorization": "Bearer\t" + d.AccessToken,
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
"origin": "https://www.aliyundrive.com",
|
"origin": "https://www.aliyundrive.com",
|
||||||
"Referer": "https://aliyundrive.com/",
|
"Referer": "https://aliyundrive.com/",
|
||||||
"X-Signature": d.signature,
|
"X-Signature": state.signature,
|
||||||
"x-request-id": uuid.NewString(),
|
"x-request-id": uuid.NewString(),
|
||||||
"X-Canary": "client=Android,app=adrive,version=v4.1.0",
|
"X-Canary": "client=Android,app=adrive,version=v4.1.0",
|
||||||
"X-Device-Id": d.DeviceID,
|
"X-Device-Id": state.deviceID,
|
||||||
})
|
})
|
||||||
if callback != nil {
|
if callback != nil {
|
||||||
callback(req)
|
callback(req)
|
||||||
|
@ -94,14 +134,21 @@ func (d *AliDrive) request(url, method string, callback base.ReqCallback, resp i
|
||||||
return nil, err, e
|
return nil, err, e
|
||||||
}
|
}
|
||||||
if e.Code != "" {
|
if e.Code != "" {
|
||||||
if e.Code == "AccessTokenInvalid" {
|
switch e.Code {
|
||||||
|
case "AccessTokenInvalid":
|
||||||
err = d.refreshToken()
|
err = d.refreshToken()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err, e
|
return nil, err, e
|
||||||
}
|
}
|
||||||
return d.request(url, method, callback, resp)
|
case "DeviceSessionSignatureInvalid":
|
||||||
|
err = d.reSign()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err, e
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, errors.New(e.Message), e
|
||||||
}
|
}
|
||||||
return nil, errors.New(e.Message), e
|
return d.request(url, method, callback, resp)
|
||||||
} else if res.IsError() {
|
} else if res.IsError() {
|
||||||
return nil, errors.New("bad status code " + res.Status()), e
|
return nil, errors.New("bad status code " + res.Status()), e
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue