mirror of
https://mau.dev/mautrix/go.git
synced 2026-03-14 14:25:53 +01:00
Add some comments and other changes to crypto stuff
This commit is contained in:
parent
173dce8619
commit
ea5cf3ee5e
12 changed files with 194 additions and 42 deletions
|
|
@ -13,8 +13,10 @@ import (
|
|||
)
|
||||
|
||||
type OlmAccount struct {
|
||||
Internal olm.Account
|
||||
Shared bool
|
||||
Internal olm.Account
|
||||
signingKey id.Curve25519
|
||||
identityKey id.Ed25519
|
||||
Shared bool
|
||||
}
|
||||
|
||||
func NewOlmAccount() *OlmAccount {
|
||||
|
|
@ -23,15 +25,35 @@ func NewOlmAccount() *OlmAccount {
|
|||
}
|
||||
}
|
||||
|
||||
func (account *OlmAccount) Keys() (id.Ed25519, id.Curve25519) {
|
||||
if len(account.signingKey) == 0 || len(account.identityKey) == 0 {
|
||||
account.identityKey, account.signingKey = account.Internal.IdentityKeys()
|
||||
}
|
||||
return account.identityKey, account.signingKey
|
||||
}
|
||||
|
||||
func (account *OlmAccount) SigningKey() id.Curve25519 {
|
||||
if len(account.signingKey) == 0 {
|
||||
account.identityKey, account.signingKey = account.Internal.IdentityKeys()
|
||||
}
|
||||
return account.signingKey
|
||||
}
|
||||
|
||||
func (account *OlmAccount) IdentityKey() id.Ed25519 {
|
||||
if len(account.identityKey) == 0 {
|
||||
account.identityKey, account.signingKey = account.Internal.IdentityKeys()
|
||||
}
|
||||
return account.identityKey
|
||||
}
|
||||
|
||||
func (account *OlmAccount) getInitialKeys(userID id.UserID, deviceID id.DeviceID) *mautrix.DeviceKeys {
|
||||
ed, curve := account.Internal.IdentityKeys()
|
||||
deviceKeys := &mautrix.DeviceKeys{
|
||||
UserID: userID,
|
||||
DeviceID: deviceID,
|
||||
Algorithms: []id.Algorithm{id.AlgorithmMegolmV1, id.AlgorithmOlmV1},
|
||||
Keys: map[id.DeviceKeyID]string{
|
||||
id.NewDeviceKeyID(id.KeyAlgorithmCurve25519, deviceID): string(curve),
|
||||
id.NewDeviceKeyID(id.KeyAlgorithmEd25519, deviceID): string(ed),
|
||||
id.NewDeviceKeyID(id.KeyAlgorithmCurve25519, deviceID): string(account.SigningKey()),
|
||||
id.NewDeviceKeyID(id.KeyAlgorithmEd25519, deviceID): string(account.IdentityKey()),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -49,7 +71,7 @@ func (account *OlmAccount) getInitialKeys(userID id.UserID, deviceID id.DeviceID
|
|||
}
|
||||
|
||||
func (account *OlmAccount) getOneTimeKeys(userID id.UserID, deviceID id.DeviceID, currentOTKCount int) map[id.KeyID]mautrix.OneTimeKey {
|
||||
newCount := int(account.Internal.MaxNumberOfOneTimeKeys() / 2) - currentOTKCount
|
||||
newCount := int(account.Internal.MaxNumberOfOneTimeKeys()/2) - currentOTKCount
|
||||
if newCount > 0 {
|
||||
account.Internal.GenOneTimeKeys(uint(newCount))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,12 +23,13 @@ var (
|
|||
DeviceKeyMismatch = errors.New("device keys in event and verified device info do not match")
|
||||
)
|
||||
|
||||
type MegolmEvent struct {
|
||||
type megolmEvent struct {
|
||||
RoomID id.RoomID `json:"room_id"`
|
||||
Type event.Type `json:"type"`
|
||||
Content event.Content `json:"content"`
|
||||
}
|
||||
|
||||
// DecryptMegolmEvent decrypts an m.room.encrypted event where the algorithm is m.megolm.v1.aes-sha2
|
||||
func (mach *OlmMachine) DecryptMegolmEvent(evt *event.Event) (*event.Event, error) {
|
||||
content, ok := evt.Content.Parsed.(*event.EncryptedEventContent)
|
||||
if !ok {
|
||||
|
|
@ -51,7 +52,7 @@ func (mach *OlmMachine) DecryptMegolmEvent(evt *event.Event) (*event.Event, erro
|
|||
}
|
||||
|
||||
var verified bool
|
||||
ownSigningKey, ownIdentityKey := mach.account.Internal.IdentityKeys()
|
||||
ownSigningKey, ownIdentityKey := mach.account.Keys()
|
||||
if content.DeviceID == mach.Client.DeviceID && sess.SigningKey == ownSigningKey && content.SenderKey == ownIdentityKey {
|
||||
verified = true
|
||||
} else {
|
||||
|
|
@ -68,7 +69,7 @@ func (mach *OlmMachine) DecryptMegolmEvent(evt *event.Event) (*event.Event, erro
|
|||
}
|
||||
}
|
||||
|
||||
megolmEvt := &MegolmEvent{}
|
||||
megolmEvt := &megolmEvent{}
|
||||
err = json.Unmarshal(plaintext, &megolmEvt)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse megolm payload")
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ var (
|
|||
RecipientKeyMismatch = errors.New("mismatched recipient key in olm payload")
|
||||
)
|
||||
|
||||
type OlmEvent struct {
|
||||
// DecryptedOlmEvent represents an event that was decrypted from an event encrypted with the m.olm.v1.curve25519-aes-sha2 algorithm.
|
||||
type DecryptedOlmEvent struct {
|
||||
Source *event.Event `json:"-"`
|
||||
|
||||
SenderKey id.SenderKey `json:"-"`
|
||||
|
|
@ -41,15 +42,14 @@ type OlmEvent struct {
|
|||
Content event.Content `json:"content"`
|
||||
}
|
||||
|
||||
func (mach *OlmMachine) decryptOlmEvent(evt *event.Event) (*OlmEvent, error) {
|
||||
func (mach *OlmMachine) decryptOlmEvent(evt *event.Event) (*DecryptedOlmEvent, error) {
|
||||
content, ok := evt.Content.Parsed.(*event.EncryptedEventContent)
|
||||
if !ok {
|
||||
return nil, IncorrectEncryptedContentType
|
||||
} else if content.Algorithm != id.AlgorithmOlmV1 {
|
||||
return nil, UnsupportedAlgorithm
|
||||
}
|
||||
_, ownKey := mach.account.Internal.IdentityKeys()
|
||||
ownContent, ok := content.OlmCiphertext[ownKey]
|
||||
ownContent, ok := content.OlmCiphertext[mach.account.SigningKey()]
|
||||
if !ok {
|
||||
return nil, NotEncryptedForMe
|
||||
}
|
||||
|
|
@ -65,7 +65,7 @@ type OlmEventKeys struct {
|
|||
Ed25519 id.Ed25519 `json:"ed25519"`
|
||||
}
|
||||
|
||||
func (mach *OlmMachine) decryptOlmCiphertext(sender id.UserID, deviceID id.DeviceID, senderKey id.SenderKey, olmType id.OlmMsgType, ciphertext string) (*OlmEvent, error) {
|
||||
func (mach *OlmMachine) decryptOlmCiphertext(sender id.UserID, deviceID id.DeviceID, senderKey id.SenderKey, olmType id.OlmMsgType, ciphertext string) (*DecryptedOlmEvent, error) {
|
||||
if olmType != id.OlmMsgTypePreKey && olmType != id.OlmMsgTypeMsg {
|
||||
return nil, UnsupportedOlmMessageType
|
||||
}
|
||||
|
|
@ -107,7 +107,7 @@ func (mach *OlmMachine) decryptOlmCiphertext(sender id.UserID, deviceID id.Devic
|
|||
}
|
||||
}
|
||||
|
||||
var olmEvt OlmEvent
|
||||
var olmEvt DecryptedOlmEvent
|
||||
err = json.Unmarshal(plaintext, &olmEvt)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse olm payload")
|
||||
|
|
@ -116,7 +116,7 @@ func (mach *OlmMachine) decryptOlmCiphertext(sender id.UserID, deviceID id.Devic
|
|||
return nil, SenderMismatch
|
||||
} else if mach.Client.UserID != olmEvt.Recipient {
|
||||
return nil, RecipientMismatch
|
||||
} else if ed25519, _ := mach.account.Internal.IdentityKeys(); ed25519 != olmEvt.RecipientKeys.Ed25519 {
|
||||
} else if mach.account.IdentityKey() != olmEvt.RecipientKeys.Ed25519 {
|
||||
return nil, RecipientKeyMismatch
|
||||
}
|
||||
|
||||
|
|
@ -165,7 +165,7 @@ func (mach *OlmMachine) createInboundSession(senderKey id.SenderKey, ciphertext
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mach.SaveAccount()
|
||||
mach.saveAccount()
|
||||
err = mach.CryptoStore.AddSession(senderKey, session)
|
||||
if err != nil {
|
||||
mach.Log.Error("Failed to store created inbound session: %v", err)
|
||||
|
|
|
|||
|
|
@ -92,6 +92,10 @@ func (mach *OlmMachine) fetchKeys(users []id.UserID, sinceToken string, includeU
|
|||
return data
|
||||
}
|
||||
|
||||
// OnDevicesChanged finds all shared rooms with the given user and invalidates outbound sessions in those rooms.
|
||||
//
|
||||
// This is called automatically whenever a device list change is noticed in ProcessSyncResponse and usually does
|
||||
// not need to be called manually.
|
||||
func (mach *OlmMachine) OnDevicesChanged(userID id.UserID) {
|
||||
for _, roomID := range mach.StateStore.FindSharedRooms(userID) {
|
||||
mach.Log.Debug("Devices of %s changed, invalidating group session for %s", userID, roomID)
|
||||
|
|
|
|||
|
|
@ -39,6 +39,15 @@ type rawMegolmEvent struct {
|
|||
Content interface{} `json:"content"`
|
||||
}
|
||||
|
||||
// IsShareError returns true if the error is caused by the lack of an outgoing megolm session and can be solved with OlmMachine.ShareGroupSession
|
||||
func IsShareError(err error) bool {
|
||||
return err == SessionExpired || err == SessionNotShared || err == NoGroupSession
|
||||
}
|
||||
|
||||
// EncryptMegolmEvent encrypts data with the m.megolm.v1.aes-sha2 algorithm.
|
||||
//
|
||||
// If you use the event.Content struct, make sure you pass a pointer to the struct,
|
||||
// as JSON serialization will not work correctly otherwise.
|
||||
func (mach *OlmMachine) EncryptMegolmEvent(roomID id.RoomID, evtType event.Type, content interface{}) (*event.EncryptedEventContent, error) {
|
||||
mach.Log.Trace("Encrypting event of type %s for %s", evtType.Type, roomID)
|
||||
session, err := mach.CryptoStore.GetOutboundGroupSession(roomID)
|
||||
|
|
@ -63,10 +72,9 @@ func (mach *OlmMachine) EncryptMegolmEvent(roomID id.RoomID, evtType event.Type,
|
|||
if err != nil {
|
||||
mach.Log.Warn("Failed to update megolm session in crypto store after encrypting: %v", err)
|
||||
}
|
||||
_, idKey := mach.account.Internal.IdentityKeys()
|
||||
return &event.EncryptedEventContent{
|
||||
Algorithm: id.AlgorithmMegolmV1,
|
||||
SenderKey: idKey,
|
||||
SenderKey: mach.account.SigningKey(),
|
||||
DeviceID: mach.Client.DeviceID,
|
||||
SessionID: session.ID(),
|
||||
MegolmCiphertext: ciphertext,
|
||||
|
|
@ -76,11 +84,15 @@ func (mach *OlmMachine) EncryptMegolmEvent(roomID id.RoomID, evtType event.Type,
|
|||
|
||||
func (mach *OlmMachine) newOutboundGroupSession(roomID id.RoomID) *OutboundGroupSession {
|
||||
session := NewOutboundGroupSession(roomID)
|
||||
signingKey, idKey := mach.account.Internal.IdentityKeys()
|
||||
signingKey, idKey := mach.account.Keys()
|
||||
mach.createGroupSession(idKey, signingKey, roomID, session.ID(), session.Internal.Key())
|
||||
return session
|
||||
}
|
||||
|
||||
// ShareGroupSession shares a group session for a specific room with all the devices of the given user list.
|
||||
//
|
||||
// For devices with TrustStateBlacklisted, a m.room_key.withheld event with code=m.blacklisted is sent.
|
||||
// If AllowUnverifiedDevices is false, a similar event with code=m.unverified is sent to devices with TrustStateUnset
|
||||
func (mach *OlmMachine) ShareGroupSession(roomID id.RoomID, users []id.UserID) error {
|
||||
mach.Log.Trace("Sharing group session for room %s to %v", roomID, users)
|
||||
session, err := mach.CryptoStore.GetOutboundGroupSession(roomID)
|
||||
|
|
@ -159,18 +171,33 @@ func (mach *OlmMachine) ShareGroupSession(roomID id.RoomID, users []id.UserID) e
|
|||
func (mach *OlmMachine) encryptGroupSessionForUser(session *OutboundGroupSession, userID id.UserID, devices map[id.DeviceID]*DeviceIdentity, output map[id.DeviceID]*event.Content, missingOutput map[id.DeviceID]*DeviceIdentity) {
|
||||
for deviceID, device := range devices {
|
||||
userKey := UserDevice{UserID: userID, DeviceID: deviceID}
|
||||
if userID == mach.Client.UserID && deviceID == mach.Client.DeviceID {
|
||||
session.Users[userKey] = OGSIgnored
|
||||
}
|
||||
|
||||
// TODO blacklisting and verification checking should be done around here
|
||||
|
||||
if state := session.Users[userKey]; state != OGSNotShared {
|
||||
continue
|
||||
}
|
||||
|
||||
deviceSession, err := mach.CryptoStore.GetLatestSession(device.IdentityKey)
|
||||
if err != nil {
|
||||
} else if userID == mach.Client.UserID && deviceID == mach.Client.DeviceID {
|
||||
session.Users[userKey] = OGSIgnored
|
||||
} else if device.Trust == TrustStateBlacklisted {
|
||||
mach.Log.Debug("Not encrypting group session %s for %s of %s: device is blacklisted", session.ID(), deviceID, userID)
|
||||
output[deviceID] = &event.Content{Parsed: event.RoomKeyWithheldEventContent{
|
||||
RoomID: session.RoomID,
|
||||
Algorithm: id.AlgorithmMegolmV1,
|
||||
SessionID: session.ID(),
|
||||
SenderKey: mach.account.SigningKey(),
|
||||
Code: event.RoomKeyWithheldBlacklisted,
|
||||
Reason: "Device is blacklisted",
|
||||
}}
|
||||
session.Users[userKey] = OGSIgnored
|
||||
} else if !mach.AllowUnverifiedDevices && device.Trust == TrustStateUnset {
|
||||
mach.Log.Debug("Not encrypting group session %s for %s of %s: device is not verified", session.ID(), deviceID, userID)
|
||||
output[deviceID] = &event.Content{Parsed: event.RoomKeyWithheldEventContent{
|
||||
RoomID: session.RoomID,
|
||||
Algorithm: id.AlgorithmMegolmV1,
|
||||
SessionID: session.ID(),
|
||||
SenderKey: mach.account.SigningKey(),
|
||||
Code: event.RoomKeyWithheldUnverified,
|
||||
Reason: "Device is not verified",
|
||||
}}
|
||||
session.Users[userKey] = OGSIgnored
|
||||
} else if deviceSession, err := mach.CryptoStore.GetLatestSession(device.IdentityKey); err != nil {
|
||||
mach.Log.Error("Failed to get session for %s of %s: %v", deviceID, userID, err)
|
||||
} else if deviceSession == nil {
|
||||
mach.Log.Warn("Didn't find a session for %s of %s", deviceID, userID)
|
||||
|
|
|
|||
|
|
@ -18,11 +18,10 @@ import (
|
|||
)
|
||||
|
||||
func (mach *OlmMachine) encryptOlmEvent(session *OlmSession, recipient *DeviceIdentity, evtType event.Type, content event.Content) *event.EncryptedEventContent {
|
||||
selfSigningKey, selfIdentityKey := mach.account.Internal.IdentityKeys()
|
||||
evt := &OlmEvent{
|
||||
evt := &DecryptedOlmEvent{
|
||||
Sender: mach.Client.UserID,
|
||||
SenderDevice: mach.Client.DeviceID,
|
||||
Keys: OlmEventKeys{Ed25519: selfSigningKey},
|
||||
Keys: OlmEventKeys{Ed25519: mach.account.IdentityKey()},
|
||||
Recipient: recipient.UserID,
|
||||
RecipientKeys: OlmEventKeys{Ed25519: recipient.SigningKey},
|
||||
Type: evtType,
|
||||
|
|
@ -39,7 +38,7 @@ func (mach *OlmMachine) encryptOlmEvent(session *OlmSession, recipient *DeviceId
|
|||
}
|
||||
return &event.EncryptedEventContent{
|
||||
Algorithm: id.AlgorithmOlmV1,
|
||||
SenderKey: selfIdentityKey,
|
||||
SenderKey: mach.account.SigningKey(),
|
||||
OlmCiphertext: event.OlmCiphertexts{
|
||||
recipient.IdentityKey: {
|
||||
Type: msgType,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ import (
|
|||
"maunium.net/go/mautrix/event"
|
||||
)
|
||||
|
||||
// Logger is a simple logging struct for OlmMachine.
|
||||
// Implementations are recommended to use fmt.Sprintf and manually add a newline after the message.
|
||||
type Logger interface {
|
||||
Error(message string, args ...interface{})
|
||||
Warn(message string, args ...interface{})
|
||||
|
|
@ -21,6 +23,7 @@ type Logger interface {
|
|||
Trace(message string, args ...interface{})
|
||||
}
|
||||
|
||||
// OlmMachine is the main struct for handling Matrix end-to-end encryption.
|
||||
type OlmMachine struct {
|
||||
Client *mautrix.Client
|
||||
Log Logger
|
||||
|
|
@ -28,23 +31,31 @@ type OlmMachine struct {
|
|||
CryptoStore Store
|
||||
StateStore StateStore
|
||||
|
||||
AllowUnverifiedDevices bool
|
||||
|
||||
account *OlmAccount
|
||||
}
|
||||
|
||||
// StateStore is used by OlmMachine to get room state information that's needed for encryption.
|
||||
type StateStore interface {
|
||||
IsEncrypted(id.RoomID) bool
|
||||
FindSharedRooms(id.UserID) []id.RoomID
|
||||
}
|
||||
|
||||
// NewOlmMachine creates an OlmMachine with the given client, logger and stores.
|
||||
func NewOlmMachine(client *mautrix.Client, log Logger, cryptoStore Store, stateStore StateStore) *OlmMachine {
|
||||
return &OlmMachine{
|
||||
Client: client,
|
||||
Log: log,
|
||||
CryptoStore: cryptoStore,
|
||||
StateStore: stateStore,
|
||||
|
||||
AllowUnverifiedDevices: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Load loads the Olm account information from the crypto store. If there's no olm account, a new one is created.
|
||||
// This must be called before using the machine.
|
||||
func (mach *OlmMachine) Load() (err error) {
|
||||
mach.account, err = mach.CryptoStore.GetAccount()
|
||||
if err != nil {
|
||||
|
|
@ -58,19 +69,21 @@ func (mach *OlmMachine) Load() (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (mach *OlmMachine) SaveAccount() {
|
||||
func (mach *OlmMachine) saveAccount() {
|
||||
err := mach.CryptoStore.PutAccount(mach.account)
|
||||
if err != nil {
|
||||
mach.Log.Error("Failed to save account: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// FlushStore calls the Flush method of the CryptoStore.
|
||||
func (mach *OlmMachine) FlushStore() error {
|
||||
return mach.CryptoStore.Flush()
|
||||
}
|
||||
|
||||
// Fingerprint returns the fingerprint of the Olm account that can be used for non-interactive verification.
|
||||
func (mach *OlmMachine) Fingerprint() string {
|
||||
signingKey, _ := mach.account.Internal.IdentityKeys()
|
||||
signingKey := mach.account.SigningKey()
|
||||
spacedSigningKey := make([]byte, len(signingKey)+(len(signingKey)-1)/4)
|
||||
var ptr = 0
|
||||
for i, chr := range signingKey {
|
||||
|
|
@ -84,6 +97,11 @@ func (mach *OlmMachine) Fingerprint() string {
|
|||
return string(spacedSigningKey)
|
||||
}
|
||||
|
||||
// ProcessSyncResponse processes a single /sync response.
|
||||
//
|
||||
// This can be easily registered into a mautrix client using .OnSync():
|
||||
//
|
||||
// client.Syncer.(*mautrix.DefaultSyncer).OnSync(c.crypto.ProcessSyncResponse)
|
||||
func (mach *OlmMachine) ProcessSyncResponse(resp *mautrix.RespSync, since string) {
|
||||
if len(resp.DeviceLists.Changed) > 0 {
|
||||
mach.Log.Trace("Device list changes in /sync: %v", resp.DeviceLists.Changed)
|
||||
|
|
@ -110,6 +128,11 @@ func (mach *OlmMachine) ProcessSyncResponse(resp *mautrix.RespSync, since string
|
|||
}
|
||||
}
|
||||
|
||||
// HandleMemberEvent handles a single membership event.
|
||||
//
|
||||
// Currently this is not automatically called, so you must add a listener yourself:
|
||||
//
|
||||
// client.Syncer.(*mautrix.DefaultSyncer).OnSync(c.crypto.ProcessSyncResponse)
|
||||
func (mach *OlmMachine) HandleMemberEvent(evt *event.Event) {
|
||||
if !mach.StateStore.IsEncrypted(evt.RoomID) {
|
||||
return
|
||||
|
|
@ -139,6 +162,8 @@ func (mach *OlmMachine) HandleMemberEvent(evt *event.Event) {
|
|||
}
|
||||
}
|
||||
|
||||
// HandleToDeviceEvent handles a single to-device event. This is automatically called by ProcessSyncResponse, so you
|
||||
// don't need to add any custom handlers if you use that method.
|
||||
func (mach *OlmMachine) HandleToDeviceEvent(evt *event.Event) {
|
||||
switch content := evt.Content.Parsed.(type) {
|
||||
case *event.EncryptedEventContent:
|
||||
|
|
@ -176,7 +201,7 @@ func (mach *OlmMachine) createGroupSession(senderKey id.SenderKey, signingKey id
|
|||
mach.Log.Trace("Created inbound group session %s/%s/%s", roomID, senderKey, sessionID)
|
||||
}
|
||||
|
||||
func (mach *OlmMachine) receiveRoomKey(evt *OlmEvent, content *event.RoomKeyEventContent) {
|
||||
func (mach *OlmMachine) receiveRoomKey(evt *DecryptedOlmEvent, content *event.RoomKeyEventContent) {
|
||||
// TODO nio had a comment saying "handle this better" for the case where evt.Keys.Ed25519 is none?
|
||||
if content.Algorithm != id.AlgorithmMegolmV1 || evt.Keys.Ed25519 == "" {
|
||||
return
|
||||
|
|
@ -185,7 +210,11 @@ func (mach *OlmMachine) receiveRoomKey(evt *OlmEvent, content *event.RoomKeyEven
|
|||
mach.createGroupSession(evt.SenderKey, evt.Keys.Ed25519, content.RoomID, content.SessionID, content.SessionKey)
|
||||
}
|
||||
|
||||
// ShareKeys returns a key upload request.
|
||||
// ShareKeys uploads necessary keys to the server.
|
||||
//
|
||||
// If the Olm account hasn't been shared, the account keys will be uploaded.
|
||||
// If currentOTKCount is less than half of the limit (100 / 2 = 50), enough one-time keys will be uploaded so exactly
|
||||
// half of the limit is filled.
|
||||
func (mach *OlmMachine) ShareKeys(currentOTKCount int) error {
|
||||
var deviceKeys *mautrix.DeviceKeys
|
||||
if !mach.account.Shared {
|
||||
|
|
@ -207,7 +236,7 @@ func (mach *OlmMachine) ShareKeys(currentOTKCount int) error {
|
|||
return err
|
||||
}
|
||||
mach.account.Shared = true
|
||||
mach.SaveAccount()
|
||||
mach.saveAccount()
|
||||
mach.Log.Trace("Shared keys and saved account")
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import (
|
|||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
// TrustState determines how trusted a device is.
|
||||
type TrustState int
|
||||
|
||||
const (
|
||||
|
|
@ -24,6 +25,7 @@ const (
|
|||
TrustStateIgnored
|
||||
)
|
||||
|
||||
// DeviceIdentity contains the identity details of a device and some additional info.
|
||||
type DeviceIdentity struct {
|
||||
UserID id.UserID
|
||||
DeviceID id.DeviceID
|
||||
|
|
@ -35,31 +37,68 @@ type DeviceIdentity struct {
|
|||
Name string
|
||||
}
|
||||
|
||||
// Store is used by OlmMachine to store Olm and Megolm sessions, user device lists and message indices.
|
||||
//
|
||||
// General implementation details:
|
||||
// * Get methods should not return errors if the requested data does not exist in the store, they should simply return nil.
|
||||
// * Update methods may assume that the pointer is the same as what has earlier been added to or fetched from the store.
|
||||
type Store interface {
|
||||
// Flush ensures that everything in the store is persisted to disk.
|
||||
// This doesn't have to do anything, e.g. for database-backed implementations that persist everything immediately.
|
||||
Flush() error
|
||||
|
||||
// PutAccount updates the OlmAccount in the store.
|
||||
PutAccount(*OlmAccount) error
|
||||
// GetAccount returns the OlmAccount in the store that was previously inserted with PutAccount.
|
||||
GetAccount() (*OlmAccount, error)
|
||||
|
||||
HasSession(id.SenderKey) bool
|
||||
GetSessions(id.SenderKey) (OlmSessionList, error)
|
||||
GetLatestSession(id.SenderKey) (*OlmSession, error)
|
||||
// AddSession inserts an Olm session into the store.
|
||||
AddSession(id.SenderKey, *OlmSession) error
|
||||
// HasSession returns whether or not the store has an Olm session with the given sender key.
|
||||
HasSession(id.SenderKey) bool
|
||||
// GetSessions returns all Olm sessions in the store with the given sender key.
|
||||
GetSessions(id.SenderKey) (OlmSessionList, error)
|
||||
// GetLatestSession returns the session with the highest session ID (lexiographically sorting).
|
||||
// It's usually safe to return the most recently added session if sorting by session ID is too difficult.
|
||||
GetLatestSession(id.SenderKey) (*OlmSession, error)
|
||||
// UpdateSession updates a session that has previously been inserted with AddSession.
|
||||
UpdateSession(id.SenderKey, *OlmSession) error
|
||||
|
||||
// PutGroupSession inserts an inbound Megolm session into the store.
|
||||
PutGroupSession(id.RoomID, id.SenderKey, id.SessionID, *InboundGroupSession) error
|
||||
// GetGroupSession gets an inbound Megolm session from the store.
|
||||
GetGroupSession(id.RoomID, id.SenderKey, id.SessionID) (*InboundGroupSession, error)
|
||||
|
||||
// AddOutboundGroupSession inserts the given outbound Megolm session into the store.
|
||||
//
|
||||
// The store should index inserted sessions by the RoomID field to support getting and removing sessions.
|
||||
// There will only be one outbound session per room ID at a time.
|
||||
AddOutboundGroupSession(*OutboundGroupSession) error
|
||||
// UpdateOutboundGroupSession updates the given outbound Megolm session in the store.
|
||||
UpdateOutboundGroupSession(*OutboundGroupSession) error
|
||||
// GetOutboundGroupSession gets the stored outbound Megolm session for the given room ID from the store.
|
||||
GetOutboundGroupSession(id.RoomID) (*OutboundGroupSession, error)
|
||||
// RemoveOutboundGroupSession removes the stored outbound Megolm session for the given room ID.
|
||||
RemoveOutboundGroupSession(id.RoomID) error
|
||||
|
||||
// ValidateMessageIndex validates that the given message details aren't from a replay attack.
|
||||
//
|
||||
// Implementations should store a map from (senderKey, sessionID, index) to (eventID, timestamp), then use that map
|
||||
// to check whether or not the message index is valid:
|
||||
//
|
||||
// * If the map key doesn't exist, the given values should be stored and this should return true.
|
||||
// * If the map key exists and the stored values match the given values, this should return true.
|
||||
// * If the map key exists, but the stored values do not match the given values, this should return false.
|
||||
ValidateMessageIndex(senderKey id.SenderKey, sessionID id.SessionID, eventID id.EventID, index uint, timestamp int64) bool
|
||||
|
||||
// GetDevices returns a map from device ID to DeviceIdentity containing all devices of a given user.
|
||||
GetDevices(id.UserID) (map[id.DeviceID]*DeviceIdentity, error)
|
||||
// GetDevice returns a specific device of a given user.
|
||||
GetDevice(id.UserID, id.DeviceID) (*DeviceIdentity, error)
|
||||
// PutDevices overrides the stored device list for the given user with the given list.
|
||||
PutDevices(id.UserID, map[id.DeviceID]*DeviceIdentity) error
|
||||
// FilterTrackedUsers returns a filtered version of the given list that only includes user IDs whose device lists
|
||||
// have been stored with PutDevices. A user is considered tracked even if the PutDevices list was empty.
|
||||
FilterTrackedUsers([]id.UserID) []id.UserID
|
||||
}
|
||||
|
||||
|
|
@ -74,6 +113,7 @@ type messageIndexValue struct {
|
|||
Timestamp int64
|
||||
}
|
||||
|
||||
// GobStore is a simple Store implementation that dumps everything into a .gob file.
|
||||
type GobStore struct {
|
||||
lock sync.RWMutex
|
||||
path string
|
||||
|
|
@ -88,6 +128,7 @@ type GobStore struct {
|
|||
|
||||
var _ Store = (*GobStore)(nil)
|
||||
|
||||
// NewGobStore creates a new GobStore that saves everything to the given file.
|
||||
func NewGobStore(path string) (*GobStore, error) {
|
||||
gs := &GobStore{
|
||||
path: path,
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ var TypeMap = map[Type]reflect.Type{
|
|||
ToDeviceForwardedRoomKey: reflect.TypeOf(ForwardedRoomKeyEventContent{}),
|
||||
ToDeviceRoomKeyRequest: reflect.TypeOf(RoomKeyRequestEventContent{}),
|
||||
ToDeviceEncrypted: reflect.TypeOf(EncryptedEventContent{}),
|
||||
ToDeviceRoomKeyWithheld: reflect.TypeOf(RoomKeyWithheldEventContent{}),
|
||||
}
|
||||
|
||||
// Content stores the content of a Matrix event.
|
||||
|
|
@ -174,6 +175,7 @@ func init() {
|
|||
gob.Register(&RoomKeyEventContent{})
|
||||
gob.Register(&ForwardedRoomKeyEventContent{})
|
||||
gob.Register(&RoomKeyRequestEventContent{})
|
||||
gob.Register(&RoomKeyWithheldEventContent{})
|
||||
}
|
||||
|
||||
// Helper cast functions below
|
||||
|
|
@ -367,3 +369,10 @@ func (content *Content) AsRoomKeyRequest() *RoomKeyRequestEventContent {
|
|||
}
|
||||
return casted
|
||||
}
|
||||
func (content *Content) AsRoomKeyWithheld() *RoomKeyWithheldEventContent {
|
||||
casted, ok := content.Parsed.(*RoomKeyWithheldEventContent)
|
||||
if !ok {
|
||||
return &RoomKeyWithheldEventContent{}
|
||||
}
|
||||
return casted
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,3 +120,22 @@ type RequestedKeyInfo struct {
|
|||
SenderKey id.SenderKey `json:"sender_key"`
|
||||
SessionID id.SessionID `json:"session_id"`
|
||||
}
|
||||
|
||||
type RoomKeyWithheldCode string
|
||||
|
||||
const (
|
||||
RoomKeyWithheldBlacklisted RoomKeyWithheldCode = "m.blacklisted"
|
||||
RoomKeyWithheldUnverified RoomKeyWithheldCode = "m.unverified"
|
||||
RoomKeyWithheldUnauthorized RoomKeyWithheldCode = "m.unauthorized"
|
||||
RoomKeyWithheldUnavailable RoomKeyWithheldCode = "m.unavailable"
|
||||
RoomKeyWithheldNoOlmSession RoomKeyWithheldCode = "m.no_olm"
|
||||
)
|
||||
|
||||
type RoomKeyWithheldEventContent struct {
|
||||
RoomID id.RoomID `json:"room_id,omitempty"`
|
||||
Algorithm id.Algorithm `json:"algorithm"`
|
||||
SessionID id.SessionID `json:"session_id,omitempty"`
|
||||
SenderKey id.SenderKey `json:"sender_key"`
|
||||
Code RoomKeyWithheldCode `json:"code"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ func (et *Type) GuessClass() TypeClass {
|
|||
return AccountDataEventType
|
||||
case EventRedaction.Type, EventMessage.Type, EventEncrypted.Type, EventReaction.Type, EventSticker.Type:
|
||||
return MessageEventType
|
||||
case ToDeviceRoomKey.Type, ToDeviceRoomKeyRequest.Type, ToDeviceForwardedRoomKey.Type:
|
||||
case ToDeviceRoomKey.Type, ToDeviceRoomKeyRequest.Type, ToDeviceForwardedRoomKey.Type, ToDeviceRoomKeyWithheld.Type:
|
||||
return ToDeviceEventType
|
||||
default:
|
||||
return UnknownEventType
|
||||
|
|
@ -176,4 +176,5 @@ var (
|
|||
ToDeviceRoomKeyRequest = Type{"m.room_key_request", ToDeviceEventType}
|
||||
ToDeviceForwardedRoomKey = Type{"m.forwarded_room_key", ToDeviceEventType}
|
||||
ToDeviceEncrypted = Type{"m.room.encrypted", ToDeviceEventType}
|
||||
ToDeviceRoomKeyWithheld = Type{"m.room_key.withheld", ToDeviceEventType}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
package mautrix
|
||||
|
||||
const Version = "v0.5.2"
|
||||
const Version = "v0.5.3"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue