gmessages/libgm/manualdecrypt/main.go

142 lines
5.4 KiB
Go
Raw Normal View History

2023-08-27 09:49:14 +00:00
package main
import (
"bufio"
"bytes"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"os/exec"
"google.golang.org/protobuf/proto"
"go.mau.fi/mautrix-gmessages/libgm/crypto"
"go.mau.fi/mautrix-gmessages/libgm/gmproto"
"go.mau.fi/mautrix-gmessages/libgm/pblite"
)
func must[T any](t T, err error) T {
if err != nil {
panic(err)
}
return t
}
func mustNoReturn(err error) {
if err != nil {
panic(err)
}
}
var requestType = map[gmproto.ActionType]proto.Message{
gmproto.ActionType_LIST_CONVERSATIONS: &gmproto.ListConversationsRequest{},
gmproto.ActionType_NOTIFY_DITTO_ACTIVITY: &gmproto.NotifyDittoActivityRequest{},
gmproto.ActionType_GET_CONVERSATION_TYPE: &gmproto.GetConversationTypeRequest{},
gmproto.ActionType_GET_CONVERSATION: &gmproto.GetConversationRequest{},
gmproto.ActionType_LIST_MESSAGES: &gmproto.ListMessagesRequest{},
gmproto.ActionType_SEND_MESSAGE: &gmproto.SendMessageRequest{},
gmproto.ActionType_SEND_REACTION: &gmproto.SendReactionRequest{},
gmproto.ActionType_DELETE_MESSAGE: &gmproto.DeleteMessageRequest{},
gmproto.ActionType_GET_PARTICIPANTS_THUMBNAIL: &gmproto.GetParticipantThumbnailRequest{},
gmproto.ActionType_LIST_CONTACTS: &gmproto.ListContactsRequest{},
gmproto.ActionType_LIST_TOP_CONTACTS: &gmproto.ListTopContactsRequest{},
gmproto.ActionType_GET_OR_CREATE_CONVERSATION: &gmproto.GetOrCreateConversationRequest{},
gmproto.ActionType_UPDATE_CONVERSATION: &gmproto.UpdateConversationRequest{},
}
func main() {
var x crypto.AESCTRHelper
file, err := os.Open("config.json")
if errors.Is(err, os.ErrNotExist) {
_ = file.Close()
_, _ = fmt.Fprintln(os.Stderr, "config.json doesn't exist")
_, _ = fmt.Fprintln(os.Stderr, "Please find pr_crypto_msg_enc_key and pr_crypto_msg_hmac from localStorage")
_, _ = fmt.Fprintln(os.Stderr, "(make sure not to confuse it with pr_crypto_hmac)")
stdin := bufio.NewScanner(os.Stdin)
_, _ = fmt.Fprint(os.Stderr, "AES key (pr_crypto_msg_enc_key): ")
stdin.Scan()
x.AESKey = must(base64.StdEncoding.DecodeString(stdin.Text()))
if len(x.AESKey) != 32 {
_, _ = fmt.Fprintln(os.Stderr, "AES key must be 32 bytes")
return
}
_, _ = fmt.Fprint(os.Stderr, "HMAC key (pr_crypto_msg_hmac): ")
stdin.Scan()
x.HMACKey = must(base64.StdEncoding.DecodeString(stdin.Text()))
if len(x.HMACKey) != 32 {
_, _ = fmt.Fprintln(os.Stderr, "HMAC key must be 32 bytes")
return
}
file, err = os.OpenFile("config.json", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0600)
if err != nil {
_, _ = fmt.Fprintln(os.Stderr, "Failed to open config.json for writing")
return
}
mustNoReturn(json.NewEncoder(file).Encode(&x))
_, _ = fmt.Fprintln(os.Stderr, "Saved keys to config.json")
} else {
mustNoReturn(json.NewDecoder(file).Decode(&x))
}
_ = file.Close()
_, _ = fmt.Fprintln(os.Stderr, "Please paste the request body, then press Ctrl+D to close stdin")
d := must(io.ReadAll(os.Stdin))
var decoded []byte
var typ gmproto.MessageType
if json.Valid(d) {
var orm gmproto.OutgoingRPCMessage
mustNoReturn(pblite.Unmarshal(d, &orm))
decoded = orm.Data.MessageData
typ = orm.Data.MessageTypeData.MessageType
} else {
decoded = must(base64.StdEncoding.DecodeString(string(d)))
}
outgoing := true
if outgoing {
var ord gmproto.OutgoingRPCData
mustNoReturn(proto.Unmarshal(decoded, &ord))
_, _ = fmt.Fprintln(os.Stderr)
_, _ = fmt.Fprintln(os.Stderr, "CHANNEL:", typ.String())
_, _ = fmt.Fprintln(os.Stderr, "REQUEST TYPE:", ord.Action.String())
_, _ = fmt.Fprintln(os.Stderr, "REQUEST ID:", ord.RequestID)
if ord.EncryptedProtoData == nil {
_, _ = fmt.Fprintln(os.Stderr, "No encrypted data")
return
}
decrypted := must(x.Decrypt(ord.EncryptedProtoData))
_, _ = fmt.Fprintln(os.Stderr, "------------------------------ RAW DECRYPTED DATA ------------------------------")
fmt.Println(base64.StdEncoding.EncodeToString(decrypted))
_, _ = fmt.Fprintln(os.Stderr, "--------------------------------- DECODED DATA ---------------------------------")
respType, ok := requestType[ord.Action]
var cmd *exec.Cmd
if ok {
cmd = exec.Command("protoc", "--proto_path=../gmproto", "--decode", string(respType.ProtoReflect().Type().Descriptor().FullName()), "client.proto")
} else {
cmd = exec.Command("protoc", "--decode_raw")
}
2023-08-27 09:49:14 +00:00
cmd.Stdin = bytes.NewReader(decrypted)
cmd.Stdout = os.Stderr
cmd.Stderr = os.Stderr
mustNoReturn(cmd.Run())
if ok {
respData := respType.ProtoReflect().New().Interface()
mustNoReturn(proto.Unmarshal(decrypted, respData))
_, _ = fmt.Fprintln(os.Stderr, "------------------------------ PARSED STRUCT DATA ------------------------------")
_, _ = fmt.Fprintf(os.Stderr, "%+v\n", respData)
}
} else {
var ird gmproto.RPCMessageData
mustNoReturn(proto.Unmarshal(decoded, &ird))
decrypted := must(x.Decrypt(ird.EncryptedData))
_, _ = fmt.Fprintln(os.Stderr)
_, _ = fmt.Fprintln(os.Stderr, "CHANNEL:", typ.String())
_, _ = fmt.Fprintln(os.Stderr, "REQUEST TYPE:", ird.Action.String())
_, _ = fmt.Fprintln(os.Stderr, "REQUEST ID:", ird.SessionID)
_, _ = fmt.Fprintln(os.Stderr, "------------------------------ RAW DECRYPTED DATA ------------------------------")
fmt.Println(base64.StdEncoding.EncodeToString(decrypted))
}
_, _ = fmt.Fprintln(os.Stderr, "--------------------------------------------------------------------------------")
}