diff --git a/README.md b/README.md index 154e2ee..e3fc10f 100644 --- a/README.md +++ b/README.md @@ -26,13 +26,28 @@ go build github.com/matrix-org/go-neb BIND_ADDRESS=:4050 DATABASE_TYPE=sqlite3 DATABASE_URL=go-neb.db?_busy_timeout=5000 BASE_URL=http://localhost:4050 ./go-neb ``` -Get a Matrix user ID and access token and give it to Go-NEB: +Get a Matrix user ID and access token. You can do this, for example, with the following curl command by replacing the user ID, password and Synapse URL with your own. + +```bash +curl -X POST --header 'Content-Type: application/json' -d '{ + "identifier": { "type": "m.id.user", "user": "nebUsername" }, + "password": "nebPassword", + "type": "m.login.password" +}' 'http://localhost:8008/_matrix/client/r0/login' +``` + +This is preferable to, for example, logging in via Riot and copying the access token and device ID from there, as then Riot will have uploaded its own device keys which Go-NEB won't have access to causing it to be unable to create encryption sessions. + +The response of this command will be a JSON object with an access token and device ID. + +Then, give the values to Go-NEB: ```bash curl -X POST localhost:4050/admin/configureClient --data-binary '{ "UserID": "@goneb:localhost", "HomeserverURL": "http://localhost:8008", "AccessToken": "", + "DeviceID": "", "Sync": true, "AutoJoinRooms": true, "DisplayName": "My Bot" diff --git a/clients/bot_client.go b/clients/bot_client.go index 219f2ac..2c95e5c 100644 --- a/clients/bot_client.go +++ b/clients/bot_client.go @@ -32,7 +32,8 @@ func (botClient *BotClient) InitOlmMachine(client *mautrix.Client, nebStore *mat if sdb, ok := database.GetServiceDB().(*database.ServiceDB); ok { // Create an SQL crypto store based on the ServiceDB used db, dialect := sdb.GetSQLDb() - sqlCryptoStore := crypto.NewSQLCryptoStore(db, dialect, client.DeviceID, []byte(client.DeviceID.String()+"pickle"), cryptoLogger) + accountID := botClient.config.UserID.String() + "-" + client.DeviceID.String() + sqlCryptoStore := crypto.NewSQLCryptoStore(db, dialect, accountID, client.DeviceID, []byte(client.DeviceID.String()+"pickle"), cryptoLogger) // Try to create the tables if they are missing if err = sqlCryptoStore.CreateTables(); err != nil { return diff --git a/clients/clients.go b/clients/clients.go index 8167cda..33afd5f 100644 --- a/clients/clients.go +++ b/clients/clients.go @@ -159,10 +159,6 @@ func (c *Clients) onMessageEvent(botClient *BotClient, event *mevt.Event) { }).Warn("Error loading services") } - if err := event.Content.ParseRaw(mevt.EventMessage); err != nil { - return - } - message := event.Content.AsMessage() body := message.Body @@ -307,9 +303,6 @@ func (c *Clients) onBotOptionsEvent(client *mautrix.Client, event *mevt.Event) { } func (c *Clients) onRoomMemberEvent(client *mautrix.Client, event *mevt.Event) { - if err := event.Content.ParseRaw(mevt.StateMember); err != nil { - return - } if event.StateKey == nil || *event.StateKey != client.UserID.String() { return // not our member event } @@ -384,10 +377,6 @@ func (c *Clients) initClient(botClient *BotClient) error { // When receiving an encrypted event, attempt to decrypt it using the BotClient's capabilities. // If successfully decrypted propagate the decrypted event to the clients. syncer.OnEventType(mevt.EventEncrypted, func(source mautrix.EventSource, evt *mevt.Event) { - if err := evt.Content.ParseRaw(mevt.EventEncrypted); err != nil { - log.WithError(err).Error("Failed to parse encrypted message") - return - } encContent := evt.Content.AsEncrypted() decrypted, err := botClient.DecryptMegolmEvent(evt) if err != nil { @@ -399,12 +388,7 @@ func (c *Clients) initClient(botClient *BotClient) error { }).WithError(err).Error("Failed to decrypt message") } else { if decrypted.Type == mevt.EventMessage { - err = decrypted.Content.ParseRaw(mevt.EventMessage) - if err != nil { - log.WithError(err).Error("Could not parse decrypted message event") - } else { - c.onMessageEvent(botClient, decrypted) - } + c.onMessageEvent(botClient, decrypted) } log.WithFields(log.Fields{ "type": evt.Type, diff --git a/clients/clients_test.go b/clients/clients_test.go index d133fe8..bb1267f 100644 --- a/clients/clients_test.go +++ b/clients/clients_test.go @@ -89,6 +89,7 @@ func TestCommandParsing(t *testing.T) { } else { content.VeryRaw = veryRaw } + content.ParseRaw(mevt.EventMessage) event := mevt.Event{ Type: mevt.EventMessage, Sender: "@someone:somewhere", diff --git a/clients/state_store.go b/clients/state_store.go index fa95a74..c05eb8f 100644 --- a/clients/state_store.go +++ b/clients/state_store.go @@ -15,6 +15,20 @@ type NebStateStore struct { Storer *mautrix.InMemoryStore } +// GetEncryptionEvent returns the encryption event for a room. +func (ss *NebStateStore) 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 *NebStateStore) IsEncrypted(roomID id.RoomID) bool { room := ss.Storer.LoadRoom(roomID) diff --git a/config.sample.yaml b/config.sample.yaml index 22d328f..00c10c2 100644 --- a/config.sample.yaml +++ b/config.sample.yaml @@ -21,6 +21,7 @@ clients: - UserID: "@goneb:localhost" AccessToken: "MDASDASJDIASDJASDAFGFRGER" + DeviceID: "DEVICE1" HomeserverURL: "http://localhost:8008" Sync: true AutoJoinRooms: true @@ -28,6 +29,7 @@ clients: - UserID: "@another_goneb:localhost" AccessToken: "MDASDASJDIASDJASDAFGFRGER" + DeviceID: "DEVICE2" HomeserverURL: "http://localhost:8008" Sync: false AutoJoinRooms: false diff --git a/go.mod b/go.mod index 6ef508e..4735a48 100644 --- a/go.mod +++ b/go.mod @@ -51,5 +51,5 @@ require ( gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v2 v2.3.0 - maunium.net/go/mautrix v0.5.5 + maunium.net/go/mautrix v0.6.0 ) diff --git a/go.sum b/go.sum index d896bb6..75005e6 100644 --- a/go.sum +++ b/go.sum @@ -270,5 +270,7 @@ maunium.net/go/mautrix v0.5.0-rc.1 h1:Ux2rwQfgf044lXjzCXmaY6VVJPcLioNzdN3okbOVIl maunium.net/go/mautrix v0.5.0-rc.1/go.mod h1:LnkFnB1yjCbb8V+upoEHDGvI/F38NHSTWYCe2RRJgSY= maunium.net/go/mautrix v0.5.5 h1:e0Pql1FdxoNUudx2oXo1gZHMrqIh5MC72cdXEPIrYLA= maunium.net/go/mautrix v0.5.5/go.mod h1:FLbMANzwqlsX2Fgm7SDe+E4I3wSa4UxJRKqS5wGkCwA= +maunium.net/go/mautrix v0.6.0 h1:V32l4aygKk2XcH3fi8Yd0pFeSyYZJNRIvr8vdA2GtC8= +maunium.net/go/mautrix v0.6.0/go.mod h1:Va/74MijqaS0DQ3aUqxmFO54/PMfr1LVsCOcGRHbYmo= maunium.net/go/mauview v0.1.1/go.mod h1:3QBUiuLct9moP1LgDhCGIg0Ovxn38Bd2sGndnUOuj4o= maunium.net/go/tcell v0.2.0/go.mod h1:9Apcb3lNNS6C6lCqKT9UFp7BTRzHXfWE+/tgufsAMho= diff --git a/goneb_services_test.go b/goneb_services_test.go index 14daa1f..e6c5892 100644 --- a/goneb_services_test.go +++ b/goneb_services_test.go @@ -14,6 +14,7 @@ import ( "maunium.net/go/mautrix/crypto" "maunium.net/go/mautrix/crypto/olm" mevt "maunium.net/go/mautrix/event" + "maunium.net/go/mautrix/id" ) func setupMockServer() (*http.ServeMux, *matrixTripper, *httptest.ResponseRecorder, chan string) { @@ -164,13 +165,14 @@ func TestEncryptedRespondToEcho(t *testing.T) { accountBot := olm.NewAccount() signingKeyMock, identityKeyMock := accountMock.IdentityKeys() signingKeyBot, identityKeyBot := accountBot.IdentityKeys() - ogsBot := crypto.NewOutboundGroupSession("!greatdekutree:hyrule") + // encryptionEvtContent := &mevt.EncryptionEventContent{Algorithm: "m.megolm.v1.aes-sha2"} + ogsBot := crypto.NewOutboundGroupSession("!greatdekutree:hyrule", nil) ogsBot.Shared = true igsMock, err := crypto.NewInboundGroupSession(identityKeyBot, signingKeyBot, "!greatdekutree:hyrule", ogsBot.Internal.Key()) if err != nil { t.Errorf("Error creating mock IGS: %v", err) } - ogsMock := crypto.NewOutboundGroupSession("!greatdekutree:hyrule") + ogsMock := crypto.NewOutboundGroupSession("!greatdekutree:hyrule", nil) ogsMock.Shared = true igsBot, err := crypto.NewInboundGroupSession(identityKeyMock, signingKeyMock, "!greatdekutree:hyrule", ogsMock.Internal.Key()) if err != nil { @@ -279,7 +281,7 @@ func TestEncryptedRespondToEcho(t *testing.T) { // DB is initialized, store the megolm sessions from before for the bot to be able to decrypt and encrypt sqlDB, dialect := database.GetServiceDB().(*database.ServiceDB).GetSQLDb() - cryptoStore := crypto.NewSQLCryptoStore(sqlDB, dialect, "mastersword", []byte("masterswordpickle"), clients.CryptoMachineLogger{}) + cryptoStore := crypto.NewSQLCryptoStore(sqlDB, dialect, "@link:hyrule-mastersword", "mastersword", []byte("masterswordpickle"), clients.CryptoMachineLogger{}) if err := cryptoStore.AddOutboundGroupSession(ogsBot); err != nil { t.Errorf("Error storing bot OGS: %v", err) } @@ -287,6 +289,14 @@ func TestEncryptedRespondToEcho(t *testing.T) { if err := cryptoStore.PutGroupSession("!greatdekutree:hyrule", identityKeyMock, igsBot.ID(), igsBot); err != nil { t.Errorf("Error storing bot IGS: %v", err) } + cryptoStore.PutDevices("@navi:hyrule", map[id.DeviceID]*crypto.DeviceIdentity{ + "NAVI": { + UserID: "@navi:hyrule", + DeviceID: "NAVI", + IdentityKey: identityKeyMock, + SigningKey: signingKeyMock, + }, + }) plaintext := `{"room_id":"!greatdekutree:hyrule","type":"m.room.message","content":{"body":"!echo save zelda","msgtype":"m.text"}}` ciphertext, err := ogsMock.Encrypt([]byte(plaintext)) @@ -305,7 +315,8 @@ func TestEncryptedRespondToEcho(t *testing.T) { "algorithm":"m.megolm.v1.aes-sha2", "sender_key":"%s", "ciphertext":"%s", - "session_id":"%s" + "session_id":"%s", + "device_id": "NAVI" }, "origin_server_ts": 10000, "unsigned": {"age": 100},