~edwargix/tallyard

fe0b04a75fbeaf644051042968bdc4314b754d68 — David Florness 5 years ago 31f7d6e
Start handling events and try to get decryption working
5 files changed, 252 insertions(+), 0 deletions(-)

M go.mod
M go.sum
A matrix/crypto_logger.go
M matrix/main.go
A matrix/state_store.go
M go.mod => go.mod +1 -0
@@ 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
)

M go.sum => go.sum +8 -0
@@ 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=

A matrix/crypto_logger.go => matrix/crypto_logger.go +28 -0
@@ 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...)
}

M matrix/main.go => matrix/main.go +125 -0
@@ 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)
	}
}

A matrix/state_store.go => matrix/state_store.go +90 -0
@@ 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
}