googleplay/auth.go

169 lines
3.8 KiB
Go

package googleplay
import (
"2a.pages.dev/rosso/http"
"2a.pages.dev/rosso/protobuf"
"2a.pages.dev/rosso/tls"
"bufio"
"io"
"net/url"
"os"
"strconv"
"strings"
)
// You can also use host "android.clients.google.com", but it also uses
// TLS fingerprinting.
func New_Auth(email, password string) (*Response, error) {
req_body := url.Values{
"Email": {email},
"Passwd": {password},
"client_sig": {""},
// wikipedia.org/wiki/URL_encoding#Types_of_URI_characters
"droidguard_results": {"-"},
}.Encode()
req, err := http.NewRequest(
"POST", "https://android.googleapis.com/auth",
strings.NewReader(req_body),
)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
hello := tls.New_Client_Hello()
if err := hello.UnmarshalText(tls.Android_API()); err != nil {
return nil, err
}
res, err := Client.Transport(hello.Transport()).Do(req)
if err != nil {
return nil, err
}
return &Response{res}, nil
}
var Client = http.Default_Client
type Auth struct {
url.Values
}
type Response struct {
*http.Response
}
func (r Response) Create(name string) error {
file, err := os.Create(name)
if err != nil {
return err
}
defer file.Close()
if _, err := file.ReadFrom(r.Body); err != nil {
return err
}
return nil
}
func (a Auth) Get_Auth() string {
return a.Get("Auth")
}
func (a Auth) Get_Token() string {
return a.Get("Token")
}
func (a *Auth) Exchange() error {
// these values take from Android API 28
req_body := url.Values{
"Token": {a.Get_Token()},
"app": {"com.android.vending"},
"client_sig": {"38918a453d07199354f8b19af05ec6562ced5788"},
"service": {"oauth2:https://www.googleapis.com/auth/googleplay"},
}.Encode()
req, err := http.NewRequest(
"POST", "https://android.googleapis.com/auth",
strings.NewReader(req_body),
)
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
res, err := Client.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
a.Values = read_query(res.Body)
return nil
}
func (h Header) Set_Device(head http.Header) error {
id, err := h.Device.ID()
if err != nil {
return err
}
head.Set("X-DFE-Device-ID", strconv.FormatUint(id, 16))
return nil
}
func (h Header) Set_Agent(head http.Header) {
// `sdk` is needed for `/fdfe/delivery`
b := []byte("Android-Finsky (sdk=")
// valid range 0 - 0x7FFF_FFFF
b = strconv.AppendInt(b, 9, 10)
// com.android.vending
b = append(b, ",versionCode="...)
if h.Single {
// valid range 8032_0000 - 8091_9999
b = strconv.AppendInt(b, 8091_9999, 10)
} else {
// valid range 8092_0000 - 0x7FFF_FFFF
b = strconv.AppendInt(b, 9999_9999, 10)
}
b = append(b, ')')
head.Set("User-Agent", string(b))
}
func (h Header) Set_Auth(head http.Header) {
head.Set("Authorization", "Bearer " + h.Auth.Get_Auth())
}
func read_query(read io.Reader) url.Values {
values := make(url.Values)
scan := bufio.NewScanner(read)
for scan.Scan() {
key, value, pass := strings.Cut(scan.Text(), "=")
if pass {
values.Add(key, value)
}
}
return values
}
type Header struct {
Auth Auth // Authorization
Device Device // X-Dfe-Device-Id
Single bool
}
func (h *Header) Open_Auth(name string) error {
file, err := os.Open(name)
if err != nil {
return err
}
defer file.Close()
h.Auth.Values = read_query(file)
return nil
}
func (h *Header) Open_Device(name string) error {
data, err := os.ReadFile(name)
if err != nil {
return err
}
h.Device.Message, err = protobuf.Unmarshal(data)
if err != nil {
return err
}
return nil
}