From fe0b04a75fbeaf644051042968bdc4314b754d68 Mon Sep 17 00:00:00 2001 From: David Florness Date: Sat, 28 Nov 2020 10:49:38 -0500 Subject: [PATCH] Start handling events and try to get decryption working --- go.mod | 1 + go.sum | 8 +++ matrix/crypto_logger.go | 28 +++++++++ matrix/main.go | 125 ++++++++++++++++++++++++++++++++++++++++ matrix/state_store.go | 90 +++++++++++++++++++++++++++++ 5 files changed, 252 insertions(+) create mode 100644 matrix/crypto_logger.go create mode 100644 matrix/state_store.go diff --git a/go.mod b/go.mod index 473b57c..c4dee1b 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/mr-tron/base58 v1.2.0 github.com/multiformats/go-multiaddr v0.2.1 github.com/rivo/tview v0.0.0-20200528200248-fe953220389f + github.com/sirupsen/logrus v1.2.0 golang.org/x/net v0.0.0-20201110031124-69a78807bb2b // indirect maunium.net/go/mautrix v0.7.13 ) diff --git a/go.sum b/go.sum index 2f54c23..e81a493 100644 --- a/go.sum +++ b/go.sum @@ -143,6 +143,7 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d h1:68u9r4wEvL3gYg2jvAOgROwZ3H+Y3hIDk4tbbmIjcYQ= github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= @@ -153,6 +154,7 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kyoh86/xdg v1.2.0 h1:CERuT/ShdTDj+A2UaX3hQ3mOV369+Sj+wyn2nIRIIkI= github.com/kyoh86/xdg v1.2.0/go.mod h1:/mg8zwu1+qe76oTFUBnyS7rJzk7LLC0VGEzJyJ19DHs= +github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY= github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libp2p/go-addr-util v0.0.1 h1:TpTQm9cXVRVSKsYbgQ7GKc3KbbHVTnbostgGaDEP+88= github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ= @@ -290,6 +292,7 @@ github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0= github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= @@ -379,6 +382,7 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY= github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0= @@ -402,10 +406,14 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc= github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= +github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc= github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/pretty v1.0.1 h1:WE4RBSZ1x6McVVC8S/Md+Qse8YUv6HRObAx6ke00NY8= github.com/tidwall/pretty v1.0.1/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/sjson v1.1.1 h1:7h1vk049Jnd5EH9NyzNiEuwYW4b5qgreBbqRC19AS3U= github.com/tidwall/sjson v1.1.1/go.mod h1:yvVuSnpEQv5cYIrO+AT6kw4QVfd5SDZoGIS7/5+fZFs= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= diff --git a/matrix/crypto_logger.go b/matrix/crypto_logger.go new file mode 100644 index 0000000..3b80c7e --- /dev/null +++ b/matrix/crypto_logger.go @@ -0,0 +1,28 @@ +package main + +import ( + log "github.com/sirupsen/logrus" +) + +// CryptoMachineLogger wraps around the usual logger, implementing the Logger interface needed by OlmMachine. +type CryptoMachineLogger struct{} + +// Error formats and logs an error message. +func (CryptoMachineLogger) Error(message string, args ...interface{}) { + log.Errorf(message, args...) +} + +// Warn formats and logs a warning message. +func (CryptoMachineLogger) Warn(message string, args ...interface{}) { + log.Warnf(message, args...) +} + +// Debug formats and logs a debug message. +func (CryptoMachineLogger) Debug(message string, args ...interface{}) { + log.Debugf(message, args...) +} + +// Trace formats and logs a trace message. +func (CryptoMachineLogger) Trace(message string, args ...interface{}) { + log.Tracef(message, args...) +} diff --git a/matrix/main.go b/matrix/main.go index 34e3ade..edfc12f 100644 --- a/matrix/main.go +++ b/matrix/main.go @@ -3,12 +3,137 @@ package main import ( "fmt" "os" + + "github.com/kyoh86/xdg" + log "github.com/sirupsen/logrus" + "maunium.net/go/mautrix" + "maunium.net/go/mautrix/crypto" + "maunium.net/go/mautrix/event" ) +type MockSharedStore struct{} + +var StartMessage = event.Type{"xyz.tallyard.start", event.MessageEventType} +var client *mautrix.Client +var olmMachine *crypto.OlmMachine +var gobStorePath string = xdg.DataHome() + "/tallyard/gob.dat" + +func debugCB(source mautrix.EventSource, evt *event.Event) { + fmt.Printf("%[5]d: <%[1]s> %[4]s (%[2]s/%[3]s)\n", evt.Sender, evt.Type.String(), evt.ID, evt.Content.AsMessage().Body, source) +} + +func joinRoomCB(source mautrix.EventSource, evt *event.Event) { + debugCB(source, evt) + + memberEvtContent := evt.Content.AsMember() + if memberEvtContent.Membership != event.MembershipInvite { + return + } + + _, err := client.JoinRoomByID(evt.RoomID) + if err != nil { + panic(err) + } +} + +func encryptedCB(source mautrix.EventSource, evt *event.Event) { + debugCB(source, evt) + + encryptedEvtContent := evt.Content.AsEncrypted() + fmt.Println(encryptedEvtContent) + + decrypted, err := olmMachine.DecryptMegolmEvent(evt) + if err != nil { + log.WithError(err).Warn("couldn't decrypt melogm event") + return + } + debugCB(source, decrypted) +} + func main() { data, err := GetData() if err != nil { fmt.Fprintln(os.Stderr, err) + os.Exit(1) } fmt.Println(data) + + client, err = mautrix.NewClient(data.Homeserver, data.UserID, data.AccessToken) + if err != nil { + panic(err) + } + + syncer := client.Syncer.(*mautrix.DefaultSyncer) + syncer.OnEventType(event.StateAliases, debugCB) + syncer.OnEventType(event.StateCanonicalAlias, debugCB) + syncer.OnEventType(event.StateCreate, debugCB) + syncer.OnEventType(event.StateJoinRules, debugCB) + syncer.OnEventType(event.StateHistoryVisibility, debugCB) + syncer.OnEventType(event.StateGuestAccess, debugCB) + syncer.OnEventType(event.StateMember, joinRoomCB) + syncer.OnEventType(event.StatePowerLevels, debugCB) + syncer.OnEventType(event.StateRoomName, debugCB) + syncer.OnEventType(event.StateTopic, debugCB) + syncer.OnEventType(event.StateRoomAvatar, debugCB) + syncer.OnEventType(event.StatePinnedEvents, debugCB) + syncer.OnEventType(event.StateTombstone, debugCB) + syncer.OnEventType(event.StateEncryption, debugCB) + syncer.OnEventType(event.EventRedaction, debugCB) + syncer.OnEventType(event.EventMessage, debugCB) + syncer.OnEventType(event.EventEncrypted, encryptedCB) + syncer.OnEventType(event.EventReaction, debugCB) + syncer.OnEventType(event.EventSticker, debugCB) + syncer.OnEventType(event.EphemeralEventReceipt, debugCB) + syncer.OnEventType(event.EphemeralEventTyping, debugCB) + syncer.OnEventType(event.EphemeralEventPresence, debugCB) + syncer.OnEventType(event.AccountDataDirectChats, debugCB) + syncer.OnEventType(event.AccountDataPushRules, debugCB) + syncer.OnEventType(event.AccountDataRoomTags, debugCB) + syncer.OnEventType(event.AccountDataFullyRead, debugCB) + syncer.OnEventType(event.AccountDataIgnoredUserList, debugCB) + syncer.OnEventType(event.ToDeviceRoomKey, debugCB) + syncer.OnEventType(event.ToDeviceRoomKeyRequest, debugCB) + syncer.OnEventType(event.ToDeviceForwardedRoomKey, debugCB) + syncer.OnEventType(event.ToDeviceEncrypted, debugCB) + syncer.OnEventType(event.ToDeviceRoomKeyWithheld, debugCB) + syncer.OnEventType(event.ToDeviceVerificationRequest, debugCB) + syncer.OnEventType(event.ToDeviceVerificationStart, debugCB) + syncer.OnEventType(event.ToDeviceVerificationAccept, debugCB) + syncer.OnEventType(event.ToDeviceVerificationKey, debugCB) + syncer.OnEventType(event.ToDeviceVerificationMAC, debugCB) + syncer.OnEventType(event.ToDeviceVerificationCancel, debugCB) + syncer.OnEventType(event.ToDeviceOrgMatrixRoomKeyWithheld, debugCB) + + resp, err := client.JoinedRooms() + fmt.Println("Joined rooms:") + for _, joinedRoom := range resp.JoinedRooms { + fmt.Println(joinedRoom) + } + + gobStore, err := crypto.NewGobStore(gobStorePath) + if err != nil { + panic(err) + } + stateStore := &TallyardStateStore{mautrix.NewInMemoryStore()} + logger := CryptoMachineLogger{} + olmMachine = crypto.NewOlmMachine(client, logger, gobStore, stateStore) + if err = olmMachine.Load(); err != nil { + panic(err) + } + syncer.OnEventType(event.StateMember, func(_ mautrix.EventSource, evt *event.Event) { + olmMachine.HandleMemberEvent(evt) + }) + syncer.OnSync(func(resp *mautrix.RespSync, since string) bool { + stateStore.UpdateStateStore(resp) + olmMachine.ProcessSyncResponse(resp, since) + if err := olmMachine.CryptoStore.Flush(); err != nil { + log.WithError(err).Error("Could not flush crypto store") + } + return true + }); + + err = client.Sync() + if err != nil { + panic(err) + } } diff --git a/matrix/state_store.go b/matrix/state_store.go new file mode 100644 index 0000000..9c853ca --- /dev/null +++ b/matrix/state_store.go @@ -0,0 +1,90 @@ +package main + +import ( + "errors" + + "maunium.net/go/mautrix" + "maunium.net/go/mautrix/event" + "maunium.net/go/mautrix/id" +) + +type TallyardStateStore struct { + Storer *mautrix.InMemoryStore +} + +// GetEncryptionEvent returns the encryption event for a room. +func (ss *TallyardStateStore) GetEncryptionEvent(roomID id.RoomID) *event.EncryptionEventContent { + room := ss.Storer.LoadRoom(roomID) + if room == nil { + return nil + } + if evts, ok := room.State[event.StateEncryption]; ok { + if evt, ok := evts[""]; ok { + return evt.Content.AsEncryption() + } + } + return nil +} + +// IsEncrypted returns whether a room has been encrypted. +func (ss *TallyardStateStore) IsEncrypted(roomID id.RoomID) bool { + room := ss.Storer.LoadRoom(roomID) + if room == nil { + return false + } + _, ok := room.State[event.StateEncryption] + return ok +} + +// FindSharedRooms returns a list of room IDs that the given user ID is also a member of. +func (ss *TallyardStateStore) FindSharedRooms(userID id.UserID) []id.RoomID { + sharedRooms := make([]id.RoomID, 0) + for roomID, room := range ss.Storer.Rooms { + if room.GetMembershipState(userID) != event.MembershipLeave { + sharedRooms = append(sharedRooms, roomID) + } + } + return sharedRooms +} + +// UpdateStateStore updates the internal state of NebStateStore from a /sync response. +func (ss *TallyardStateStore) UpdateStateStore(resp *mautrix.RespSync) { + for roomID, evts := range resp.Rooms.Join { + room := ss.Storer.LoadRoom(roomID) + if room == nil { + room = mautrix.NewRoom(roomID) + ss.Storer.SaveRoom(room) + } + for _, i := range evts.State.Events { + room.UpdateState(i) + } + for _, i := range evts.Timeline.Events { + if i.Type.IsState() { + room.UpdateState(i) + } + } + } +} + +// GetJoinedMembers returns a list of members that are currently in a room. +func (ss *TallyardStateStore) GetJoinedMembers(roomID id.RoomID) ([]id.UserID, error) { + joinedMembers := make([]id.UserID, 0) + room := ss.Storer.LoadRoom(roomID) + if room == nil { + return nil, errors.New("unknown roomID") + } + memberEvents := room.State[event.StateMember] + if memberEvents == nil { + return nil, errors.New("no state member events found") + } + for stateKey, stateEvent := range memberEvents { + if stateEvent == nil { + continue + } + stateEvent.Content.ParseRaw(event.StateMember) + if stateEvent.Content.AsMember().Membership == event.MembershipJoin { + joinedMembers = append(joinedMembers, id.UserID(stateKey)) + } + } + return joinedMembers, nil +} -- 2.38.4