1
0
mirror of https://github.com/hrfee/jfa-go.git synced 2024-10-31 23:40:11 +00:00
jfa-go/matrix_crypto.go
Harvey Tindall aa2891fc87
matrix: fix instant crash with e2ee
also some more attempts at fixing it, so far all i've found is that if
we don't delete the cryptoStore, the matrix client also gets the same
error we do if it send s a message to us (no session with given ID
found).
2021-07-20 15:34:39 +01:00

225 lines
6.1 KiB
Go

// +build e2ee
package main
import (
"strings"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/crypto"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
type Crypto struct {
cryptoStore *crypto.GobStore
olm *crypto.OlmMachine
}
func MatrixE2EE() bool { return true }
type stateStore struct {
isEncrypted *map[id.RoomID]bool
}
func (m *stateStore) IsEncrypted(roomID id.RoomID) bool {
// encrypted, ok := (*m.isEncrypted)[roomID]
// return ok && encrypted
return true
}
func (m *stateStore) GetEncryptionEvent(roomID id.RoomID) *event.EncryptionEventContent {
return &event.EncryptionEventContent{
Algorithm: id.AlgorithmMegolmV1,
RotationPeriodMillis: 7 * 24 * 60 * 60 * 1000,
RotationPeriodMessages: 100,
}
}
// Users are assumed to only have one common channel with the bot, so we can stub this out.
func (m *stateStore) FindSharedRooms(userID id.UserID) []id.RoomID {
// for _, user := range m.app.storage.matrix {
// if id.UserID(user.UserID) == userID {
// return []id.RoomID{id.RoomID(user.RoomID)}
// }
// }
return []id.RoomID{}
}
func (d *MatrixDaemon) getUserIDs(roomID id.RoomID) (list []id.UserID, err error) {
members, err := d.bot.JoinedMembers(roomID)
if err != nil {
return
}
list = make([]id.UserID, len(members.Joined))
i := 0
for id := range members.Joined {
list[i] = id
i++
}
return
}
type olmLogger struct {
app *appContext
}
func (o olmLogger) Error(message string, args ...interface{}) {
o.app.err.Printf("OLM: "+message+"\n", args)
}
func (o olmLogger) Warn(message string, args ...interface{}) {
o.app.info.Printf("OLM: "+message+"\n", args)
}
func (o olmLogger) Debug(message string, args ...interface{}) {
o.app.debug.Printf("OLM: "+message+"\n", args)
}
func (o olmLogger) Trace(message string, args ...interface{}) {
if strings.HasPrefix(message, "Got membership state event") {
return
}
o.app.debug.Printf("OLM [TRACE]: "+message+"\n", args)
}
func InitMatrixCrypto(d *MatrixDaemon) (err error) {
d.Encryption = d.app.config.Section("matrix").Key("encryption").MustBool(false)
if !d.Encryption {
return
}
for _, user := range d.app.storage.matrix {
d.isEncrypted[id.RoomID(user.RoomID)] = user.Encrypted
}
dbPath := d.app.config.Section("files").Key("matrix_sql").String()
// If the db is maintained after restart, element reports "The secure channel with the sender was corrupted" when sending a message from the bot.
// This obviously isn't right, but it seems to work.
// Since its not really used anyway, just use the deprecated GobStore. This reduces cgo usage anyway.
var cryptoStore *crypto.GobStore
cryptoStore, err = crypto.NewGobStore(dbPath)
// d.db, err = sql.Open("sqlite3", dbPath)
if err != nil {
return
}
olmLog := &olmLogger{d.app}
// deviceID := "jfa-go" + commit
// cryptoStore := crypto.NewSQLCryptoStore(d.db, "sqlite3", string(d.userID)+deviceID, id.DeviceID(deviceID), []byte("jfa-go"), olmLog)
// err = cryptoStore.CreateTables()
// if err != nil {
// return
// }
olm := crypto.NewOlmMachine(d.bot, olmLog, cryptoStore, &stateStore{&d.isEncrypted})
olm.AllowUnverifiedDevices = true
err = olm.Load()
if err != nil {
return
}
d.crypto = Crypto{
cryptoStore: cryptoStore,
olm: olm,
}
return
}
func HandleSyncerCrypto(startTime int64, d *MatrixDaemon, syncer *mautrix.DefaultSyncer) {
if !d.Encryption {
return
}
syncer.OnSync(func(resp *mautrix.RespSync, since string) bool {
d.crypto.olm.ProcessSyncResponse(resp, since)
return true
})
syncer.OnEventType(event.StateMember, func(source mautrix.EventSource, evt *event.Event) {
d.crypto.olm.HandleMemberEvent(evt)
// if evt.Content.AsMember().Membership != event.MembershipJoin {
// return
// }
// userIDs, err := d.getUserIDs(evt.RoomID)
// if err != nil || len(userIDs) < 2 {
// fmt.Println("FS", err)
// return
// }
// err = d.crypto.olm.ShareGroupSession(evt.RoomID, userIDs)
// if err != nil {
// fmt.Println("FS", err)
// return
// }
})
syncer.OnEventType(event.EventEncrypted, func(source mautrix.EventSource, evt *event.Event) {
if evt.Timestamp < startTime {
return
}
decrypted, err := d.crypto.olm.DecryptMegolmEvent(evt)
// if strings.Contains(err.Error(), crypto.NoSessionFound.Error()) {
// d.app.err.Printf("Failed to decrypt Matrix message: no session found")
// return
// }
if err != nil {
d.app.err.Printf("Failed to decrypt Matrix message: %v", err)
return
}
d.handleMessage(source, decrypted)
})
}
func CryptoShutdown(d *MatrixDaemon) {
if d.Encryption {
d.crypto.olm.FlushStore()
}
}
func EncryptRoom(d *MatrixDaemon, room *mautrix.RespCreateRoom, userID id.UserID) (encrypted bool) {
if !d.Encryption {
return
}
_, err := d.bot.SendStateEvent(room.RoomID, event.StateEncryption, "", &event.EncryptionEventContent{
Algorithm: id.AlgorithmMegolmV1,
RotationPeriodMillis: 7 * 24 * 60 * 60 * 1000,
RotationPeriodMessages: 100,
})
if err == nil {
encrypted = true
} else {
d.app.debug.Printf("Matrix: Failed to enable encryption in room: %v", err)
return
}
d.isEncrypted[room.RoomID] = encrypted
var userIDs []id.UserID
userIDs, err = d.getUserIDs(room.RoomID)
if err != nil {
return
}
userIDs = append(userIDs, userID)
return
}
func SendEncrypted(d *MatrixDaemon, content *event.MessageEventContent, roomID id.RoomID) (err error) {
if !d.Encryption {
err = d.send(content, roomID)
return
}
var encrypted *event.EncryptedEventContent
encrypted, err = d.crypto.olm.EncryptMegolmEvent(roomID, event.EventMessage, content)
if err == crypto.SessionExpired || err == crypto.SessionNotShared || err == crypto.NoGroupSession {
// err = d.crypto.olm.ShareGroupSession(id.RoomID(user.RoomID), []id.UserID{id.UserID(user.UserID), d.userID})
var userIDs []id.UserID
userIDs, err = d.getUserIDs(roomID)
if err != nil {
return
}
err = d.crypto.olm.ShareGroupSession(roomID, userIDs)
if err != nil {
return
}
encrypted, err = d.crypto.olm.EncryptMegolmEvent(roomID, event.EventMessage, content)
}
if err != nil {
return
}
_, err = d.bot.SendMessageEvent(roomID, event.EventEncrypted, &event.Content{Parsed: encrypted})
if err != nil {
return
}
return
}