@ -4,224 +4,54 @@
package main
import (
"fmt"
"strings"
"context"
lm "github.com/hrfee/jfa-go/logmessages"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/crypto"
_ "github.com/mattn/go-sqlite3"
"maunium.net/go/mautrix/crypto/cryptohelper"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
type Crypto struct {
cryptoStore * crypto . GobStore
olm * crypto . OlmMachine
helper * cryptohelper . CryptoHelper
}
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 ( lm . MatrixOlmLog , fmt . Sprintf ( message , args ) )
}
func ( o olmLogger ) Warn ( message string , args ... interface { } ) {
o . app . info . Printf ( lm . MatrixOlmLog , fmt . Sprintf ( message , args ) )
}
func ( o olmLogger ) Debug ( message string , args ... interface { } ) {
o . app . debug . Printf ( lm . MatrixOlmLog , fmt . Sprintf ( message , args ) )
}
func ( o olmLogger ) Trace ( message string , args ... interface { } ) {
if strings . HasPrefix ( message , "Got membership state event" ) {
return
}
o . app . debug . Printf ( lm . MatrixOlmTracelog , fmt . Sprintf ( message , args ) )
}
func InitMatrixCrypto ( d * MatrixDaemon ) ( err error ) {
func InitMatrixCrypto ( d * MatrixDaemon ) 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
// return fmt.Errorf("encryption disabled")
return nil
}
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)
var err error
d . crypto = & Crypto { }
d . crypto . helper , err = cryptohelper . NewCryptoHelper ( d . bot , [ ] byte ( "jfa-go" ) , dbPath )
if err != nil {
return
return err
}
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 ( )
err = d . crypto . helper . Init ( context . TODO ( ) )
if err != nil {
return
}
d . crypto = Crypto {
cryptoStore : cryptoStore ,
olm : olm ,
return err
}
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 ( lm . FailedDecryptMatrixMessage , err )
return
}
d . handleMessage ( source , decrypted )
} )
}
d . bot . Crypto = d . crypto . helper
func CryptoShutdown ( d * MatrixDaemon ) {
if d . Encryption {
d . crypto . olm . FlushStore ( )
}
d . Encryption = true
return nil
}
func EncryptRoom ( d * MatrixDaemon , room * mautrix . RespCreateRoom , userID id . UserID ) ( encrypted bool ) {
func EncryptRoom ( d * MatrixDaemon , roomID id . RoomID ) error {
if ! d . Encryption {
return
return nil
}
_ , err := d . bot . SendStateEvent ( room. R oomID, event . StateEncryption , "" , & event . EncryptionEventContent {
_ , err := d . bot . SendStateEvent ( context . TODO ( ) , 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 ( lm . FailedEnableMatrixEncryption , room . RoomID , 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
return err
}