Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions pkg/connector/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -445,15 +445,20 @@ func (m *MetaClient) connectE2EE() error {
isNew = true
m.WADevice = m.Main.DeviceStore.NewDevice()
}
if suggested := m.Client.MessengerLite.GetSuggestedDeviceID(); suggested != uuid.Nil {
m.WADevice.FacebookUUID = suggested
if known := m.LoginMeta.MessengerLite.DeviceID; known != "" {
// we know this uuid is safe to parse because we generated it
m.WADevice.FacebookUUID = uuid.MustParse(known)
}
m.Client.SetDevice(m.WADevice)

if isNew {
fbid := metaid.ParseUserLoginID(m.UserLogin.ID)
log.Info().Msg("Registering new e2ee device")
err = m.Client.RegisterE2EE(ctx, fbid)
var ml *metaid.MessengerLiteLoginMetadata
if meta, ok := m.UserLogin.Metadata.(*metaid.UserLoginMetadata); ok && meta != nil {
ml = &meta.MessengerLite
}
err = m.Client.RegisterE2EE(ctx, fbid, ml)
if err != nil {
return fmt.Errorf("failed to register e2ee device: %w", err)
}
Expand Down
25 changes: 18 additions & 7 deletions pkg/connector/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ func loginWithCookies(
bridgeUser *bridgev2.User,
conn *MetaConnector,
c *cookies.Cookies,
mlm metaid.MessengerLiteLoginMetadata,
) (*bridgev2.LoginStep, error) {

log.Debug().
Expand Down Expand Up @@ -263,9 +264,10 @@ func loginWithCookies(
Name: user.GetName(),
},
Metadata: &metaid.UserLoginMetadata{
Platform: c.Platform,
Cookies: c,
LoginUA: loginUA,
Platform: c.Platform,
Cookies: c,
LoginUA: loginUA,
MessengerLite: mlm,
},
}, nil)
if err != nil {
Expand Down Expand Up @@ -311,7 +313,7 @@ func (m *MetaCookieLogin) SubmitCookies(ctx context.Context, strCookies map[stri
if err != nil {
return nil, err
}
return loginWithCookies(ctx, log, client, m.User, m.Main, c)
return loginWithCookies(ctx, log, client, m.User, m.Main, c, metaid.MessengerLiteLoginMetadata{})
}

type MetaNativeLogin struct {
Expand Down Expand Up @@ -351,7 +353,7 @@ func (m *MetaNativeLogin) Wait(ctx context.Context) (*bridgev2.LoginStep, error)
func (m *MetaNativeLogin) proceed(ctx context.Context, userInput map[string]string) (*bridgev2.LoginStep, error) {
log := m.User.Log.With().Str("component", "messagix").Logger()

step, newCookies, err := m.SavedClient.MessengerLite.DoLoginSteps(ctx, userInput)
step, loginResp, err := m.SavedClient.MessengerLite.DoLoginSteps(ctx, userInput)
if err != nil {
log.Error().Err(err).Msg("Login steps returned error")
if errors.As(err, &bloks.CheckpointError{}) {
Expand All @@ -363,11 +365,20 @@ func (m *MetaNativeLogin) proceed(ctx context.Context, userInput map[string]stri
return step, nil
}

_ = newCookies
newCookies := loginResp.GetCookies()

m.SavedClient.GetCookies().UpdateValues(newCookies.GetAll())

step, err = loginWithCookies(ctx, log, m.SavedClient, m.User, m.Main, newCookies)
loginMeta := m.SavedClient.MessengerLite.GetLoginMetadata()
step, err = loginWithCookies(
ctx, log, m.SavedClient, m.User, m.Main, newCookies,
metaid.MessengerLiteLoginMetadata{
DeviceID: loginMeta.DeviceID,
FamilyDeviceID: loginMeta.FamilyDeviceID,
MachineID: loginMeta.MachineID,
AccessToken: loginResp.AccessToken,
},
)
if err != nil {
return nil, err
}
Expand Down
92 changes: 53 additions & 39 deletions pkg/connector/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"go.mau.fi/mautrix-meta/pkg/messagix"
"go.mau.fi/mautrix-meta/pkg/messagix/methods"
"go.mau.fi/mautrix-meta/pkg/messagix/table"
"go.mau.fi/mautrix-meta/pkg/messagix/types"
"go.mau.fi/mautrix-meta/pkg/metaid"
)

Expand All @@ -40,12 +41,21 @@ var (
_ bridgev2.BackgroundSyncingNetworkAPI = (*MetaClient)(nil)
)

var pushCfg = &bridgev2.PushConfig{
var webPushCfg = &bridgev2.PushConfig{
Web: &bridgev2.WebPushConfig{VapidKey: "BIBn3E_rWTci8Xn6P9Xj3btShT85Wdtne0LtwNUyRQ5XjFNkuTq9j4MPAVLvAFhXrUU1A9UxyxBA7YIOjqDIDHI"},
}

var apnsPushCfg = &bridgev2.PushConfig{
APNs: &bridgev2.APNsPushConfig{
BundleID: "com.facebook.Messenger",
},
}

func (m *MetaClient) GetPushConfigs() *bridgev2.PushConfig {
return pushCfg
if m.Client.Platform == types.MessengerLite {
return apnsPushCfg
}
return webPushCfg
}

type DoubleToken struct {
Expand All @@ -54,47 +64,51 @@ type DoubleToken struct {
}

func (m *MetaClient) RegisterPushNotifications(ctx context.Context, pushType bridgev2.PushType, token string) error {
if pushType != bridgev2.PushTypeWeb {
return fmt.Errorf("unsupported push type %s", pushType)
}
meta := m.UserLogin.Metadata.(*metaid.UserLoginMetadata)
if meta.PushKeys == nil {
meta.GeneratePushKeys()
err := m.UserLogin.Save(ctx)
if err != nil {
return fmt.Errorf("failed to save push key: %w", err)
switch pushType {
case bridgev2.PushTypeWeb:
if meta.PushKeys == nil {
meta.GeneratePushKeys()
err := m.UserLogin.Save(ctx)
if err != nil {
return fmt.Errorf("failed to save push key: %w", err)
}
}
}
keys := messagix.PushKeys{
P256DH: meta.PushKeys.P256DH,
Auth: meta.PushKeys.Auth,
}
var encToken string
if token[0] == '{' && token[len(token)-1] == '}' {
var dt DoubleToken
err := json.Unmarshal([]byte(token), &dt)
if err != nil {
return fmt.Errorf("failed to unmarshal double token: %w", err)
keys := messagix.PushKeys{
P256DH: meta.PushKeys.P256DH,
Auth: meta.PushKeys.Auth,
}
token = dt.Unencrypted
encToken = dt.Encrypted
}
if encToken != "" {
err := m.E2EEClient.RegisterForPushNotifications(ctx, &whatsmeow.WebPushConfig{
Endpoint: encToken,
Auth: meta.PushKeys.Auth,
P256DH: meta.PushKeys.P256DH,
})
if err != nil {
return fmt.Errorf("failed to register e2ee notifications: %w", err)
var encToken string
if token[0] == '{' && token[len(token)-1] == '}' {
var dt DoubleToken
err := json.Unmarshal([]byte(token), &dt)
if err != nil {
return fmt.Errorf("failed to unmarshal double token: %w", err)
}
token = dt.Unencrypted
encToken = dt.Encrypted
}
}
if cli := m.Client; cli == nil {
return messagix.ErrClientIsNil
} else if cli.Platform.IsMessenger() {
return cli.Facebook.RegisterPushNotifications(ctx, token, keys)
} else {
return cli.Instagram.RegisterPushNotifications(ctx, token, keys)
if encToken != "" {
err := m.E2EEClient.RegisterForPushNotifications(ctx, &whatsmeow.WebPushConfig{
Endpoint: encToken,
Auth: meta.PushKeys.Auth,
P256DH: meta.PushKeys.P256DH,
})
if err != nil {
return fmt.Errorf("failed to register e2ee notifications: %w", err)
}
}
if cli := m.Client; cli == nil {
return messagix.ErrClientIsNil
} else if cli.Platform.IsMessenger() {
return cli.Facebook.RegisterPushNotifications(ctx, token, keys)
} else {
return cli.Instagram.RegisterPushNotifications(ctx, token, keys)
}
case bridgev2.PushTypeAPNs:
return m.Client.MessengerLite.RegisterPushNotifications(ctx, token, meta.MessengerLite)
default:
return fmt.Errorf("unsupported push type %s", pushType)
}
}

Expand Down
72 changes: 63 additions & 9 deletions pkg/messagix/e2ee-register.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@
"go.mau.fi/whatsmeow/types"
"google.golang.org/protobuf/proto"

messagix_types "go.mau.fi/mautrix-meta/pkg/messagix/types"
"go.mau.fi/mautrix-meta/pkg/messagix/useragent"
"go.mau.fi/mautrix-meta/pkg/metaid"
)

type ICDCFetchResponse struct {
Expand Down Expand Up @@ -88,6 +90,7 @@
req.Header.Set("sec-fetch-site", "cross-site")
req.Header.Set("origin", c.GetEndpoint("base_url"))
req.Header.Set("referer", c.GetEndpoint("messages")+"/")
req.Header.Set("request_token", strings.ToUpper(uuid.NewString()))
zerolog.Ctx(ctx).Trace().
Str("url", addr).
Any("body", body).
Expand Down Expand Up @@ -117,12 +120,12 @@
return nil
}

func (c *Client) fetchICDC(ctx context.Context, fbid int64, deviceUUID uuid.UUID) (*ICDCFetchResponse, error) {
func (c *Client) fetchICDC(ctx context.Context, fbid int64, deviceUUID uuid.UUID, fbAppID string, fbCAT string) (*ICDCFetchResponse, error) {
formBody := url.Values{
"fbid": {strconv.FormatInt(fbid, 10)},
"fb_cat": {c.configs.BrowserConfigTable.MessengerWebInitData.CryptoAuthToken.EncryptedSerializedCat},
"app_id": {strconv.FormatInt(c.configs.BrowserConfigTable.MessengerWebInitData.AppID, 10)},
"device_id": {deviceUUID.String()},
"fb_cat": {fbCAT},
"app_id": {fbAppID},
"device_id": {strings.ToUpper(deviceUUID.String())},
}
var icdcResp ICDCFetchResponse
err := c.doE2EERequest(ctx, "icdc_fetch", formBody, &icdcResp)
Expand Down Expand Up @@ -150,7 +153,7 @@
}
}

func (c *Client) RegisterE2EE(ctx context.Context, fbid int64) error {
func (c *Client) RegisterE2EE(ctx context.Context, fbid int64, metadata *metaid.MessengerLiteLoginMetadata) error {
if c == nil {
return ErrClientIsNil
} else if c.device == nil {
Expand All @@ -159,7 +162,17 @@
if c.device.FacebookUUID == uuid.Nil {
c.device.FacebookUUID = uuid.New()
}
icdcMeta, err := c.fetchICDC(ctx, fbid, c.device.FacebookUUID)
fbAppID := strconv.FormatInt(c.configs.BrowserConfigTable.MessengerWebInitData.AppID, 10)
fbCAT := c.configs.BrowserConfigTable.MessengerWebInitData.CryptoAuthToken.EncryptedSerializedCat
if c.Platform == messagix_types.MessengerLite {
fbAppID = useragent.MessengerLiteAppId
var err error
fbCAT, err = c.getMessengerLiteCAT(ctx, metadata)
if err != nil {
return fmt.Errorf("failed to fetch messenger-lite CAT: %w", err)
}
}
icdcMeta, err := c.fetchICDC(ctx, fbid, c.device.FacebookUUID, fbAppID, fbCAT)
if err != nil {
return fmt.Errorf("failed to fetch ICDC metadata: %w", err)
}
Expand Down Expand Up @@ -194,9 +207,9 @@
}
formBody := url.Values{
"fbid": {strconv.FormatInt(fbid, 10)},
"fb_cat": {c.configs.BrowserConfigTable.MessengerWebInitData.CryptoAuthToken.EncryptedSerializedCat},
"app_id": {strconv.FormatInt(c.configs.BrowserConfigTable.MessengerWebInitData.AppID, 10)},
"device_id": {c.device.FacebookUUID.String()},
"fb_cat": {fbCAT},
"app_id": {fbAppID},
"device_id": {strings.ToUpper(c.device.FacebookUUID.String())},
"e_regid": {base64.StdEncoding.EncodeToString(binary.BigEndian.AppendUint32(nil, c.device.RegistrationID))},
"e_keytype": {base64.StdEncoding.EncodeToString([]byte{ecc.DjbType})},
"e_ident": {base64.StdEncoding.EncodeToString(c.device.IdentityKey.Pub[:])},
Expand Down Expand Up @@ -232,3 +245,44 @@
Msg("ICDC registration successful")
return err
}

type messengerLiteCatResponse struct {
CAT string `json:"encrypted_serialized_cat"`
}

func (c *Client) getMessengerLiteCAT(ctx context.Context, metadata *metaid.MessengerLiteLoginMetadata) (string, error) {
analHdr, err := makeRequestAnalyticsHeader(false)
if err != nil {
return "", err
}
headers := map[string]string{
"accept": "*/*",
"x-fb-http-engine": "NSURL",
"x-fb-request-analytics-tags": analHdr,
"request_token": strings.ToUpper(uuid.New().String()),
"accept-language": "en-US,en;q=0.9",
"user-agent": useragent.MessengerLiteUserAgent,
"x-fb-appid": useragent.MessengerLiteAppId,
"access_token": metadata.AccessToken,
}

httpHeaders := http.Header{}
for k, v := range headers {
httpHeaders.Set(k, v)
}

resp, body, err := c.MakeRequest(ctx, "https://web.facebook.com/messaging/lightspeed/cat", "POST", httpHeaders, nil, messagix_types.NONE)

Check failure on line 274 in pkg/messagix/e2ee-register.go

View workflow job for this annotation

GitHub Actions / Lint (latest)

this value of err is never used (SA4006)

Check failure on line 274 in pkg/messagix/e2ee-register.go

View workflow job for this annotation

GitHub Actions / Lint (old)

this value of err is never used (SA4006)

Check failure on line 274 in pkg/messagix/e2ee-register.go

View workflow job for this annotation

GitHub Actions / Lint (old)

this value of err is never used (SA4006)

Check failure on line 274 in pkg/messagix/e2ee-register.go

View workflow job for this annotation

GitHub Actions / Lint (latest)

this value of err is never used (SA4006)
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("bad http status on cat request: %d", resp.StatusCode)
}

var catResp messengerLiteCatResponse
err = json.Unmarshal(bytes.TrimPrefix(body, []byte("for (;;);")), &catResp)
if err != nil {
return "", fmt.Errorf("parse cat response: %w", err)
}
if catResp.CAT == "" {
return "", fmt.Errorf("no cat token in response")
}
return catResp.CAT, nil
}
2 changes: 1 addition & 1 deletion pkg/messagix/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ func (c *Client) buildHeaders(withCookies, isSecFetchDocument bool) http.Header

func (c *Client) buildMessengerLiteHeaders() (http.Header, error) {

analHdr, err := makeRequestAnalyticsHeader()
analHdr, err := makeRequestAnalyticsHeader(true)
if err != nil {
return nil, err
}
Expand Down
Loading
Loading