mirror of
https://mau.dev/mautrix/go.git
synced 2026-03-14 22:35:52 +01:00
Add more stuff
This commit is contained in:
parent
66a6fab621
commit
ffc8d4de8f
8 changed files with 494 additions and 81 deletions
63
crypto/account.go
Normal file
63
crypto/account.go
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright (c) 2020 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"maunium.net/go/mautrix"
|
||||
"maunium.net/go/mautrix/id"
|
||||
"maunium.net/go/olm"
|
||||
)
|
||||
|
||||
type OlmAccount struct {
|
||||
*olm.Account
|
||||
Shared bool
|
||||
}
|
||||
|
||||
func (account *OlmAccount) getInitialKeys(userID id.UserID, deviceID id.DeviceID) *mautrix.DeviceKeys {
|
||||
ed, curve := account.IdentityKeys()
|
||||
deviceKeys := &mautrix.DeviceKeys{
|
||||
UserID: userID,
|
||||
DeviceID: deviceID,
|
||||
Algorithms: []string{string(olm.AlgorithmMegolmV1)},
|
||||
Keys: map[id.DeviceKeyID]string{
|
||||
id.NewDeviceKeyID("curve25519", deviceID): string(curve),
|
||||
id.NewDeviceKeyID("ed25519", deviceID): string(ed),
|
||||
},
|
||||
}
|
||||
|
||||
signature, err := account.SignJSON(deviceKeys)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
deviceKeys.Signatures = mautrix.Signatures{
|
||||
userID: {
|
||||
id.NewDeviceKeyID("ed25519", deviceID): signature,
|
||||
},
|
||||
}
|
||||
return deviceKeys
|
||||
}
|
||||
|
||||
func (account *OlmAccount) getOneTimeKeys(userID id.UserID, deviceID id.DeviceID) map[id.KeyID]mautrix.OneTimeKey {
|
||||
account.GenOneTimeKeys(account.MaxNumberOfOneTimeKeys() / 3 * 2)
|
||||
oneTimeKeys := make(map[id.KeyID]mautrix.OneTimeKey)
|
||||
// TODO do we need unsigned curve25519 one-time keys at all?
|
||||
// this just signs all of them
|
||||
for keyID, key := range account.OneTimeKeys().Curve25519 {
|
||||
key := mautrix.OneTimeKey{Key: string(key)}
|
||||
signature, _ := account.SignJSON(key)
|
||||
key.Signatures = mautrix.Signatures{
|
||||
userID: {
|
||||
id.NewDeviceKeyID("ed25519", deviceID): signature,
|
||||
},
|
||||
}
|
||||
key.IsSigned = true
|
||||
oneTimeKeys[id.NewKeyID("signed_curve25519", keyID)] = key
|
||||
}
|
||||
account.MarkKeysAsPublished()
|
||||
return oneTimeKeys
|
||||
}
|
||||
194
crypto/crypto.go
194
crypto/crypto.go
|
|
@ -7,83 +7,159 @@
|
|||
package crypto
|
||||
|
||||
import (
|
||||
"maunium.net/go/mautrix"
|
||||
"maunium.net/go/mautrix/id"
|
||||
"maunium.net/go/olm"
|
||||
|
||||
"maunium.net/go/mautrix"
|
||||
"maunium.net/go/mautrix/event"
|
||||
)
|
||||
|
||||
type Logger interface {
|
||||
Debugfln(message string, args ...interface{})
|
||||
}
|
||||
|
||||
type OlmMachine struct {
|
||||
UserID id.UserID
|
||||
DeviceID id.DeviceID
|
||||
Store Store
|
||||
client *mautrix.Client
|
||||
store Store
|
||||
|
||||
account *olm.Account
|
||||
account *OlmAccount
|
||||
sessions map[string][]*OlmSession
|
||||
groupSessions map[id.RoomID]map[string]map[string]*InboundGroupSession
|
||||
log Logger
|
||||
}
|
||||
|
||||
func NewOlmMachine(userID id.UserID, deviceID id.DeviceID, store Store) *OlmMachine {
|
||||
func NewOlmMachine(client *mautrix.Client, store Store) *OlmMachine {
|
||||
return &OlmMachine{
|
||||
UserID: userID,
|
||||
DeviceID: deviceID,
|
||||
Store: store,
|
||||
client: client,
|
||||
store: store,
|
||||
}
|
||||
}
|
||||
|
||||
func (mach *OlmMachine) Load() {
|
||||
mach.account = mach.Store.LoadAccount()
|
||||
func (mach *OlmMachine) Load() (err error) {
|
||||
mach.account, err = mach.store.LoadAccount()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if mach.account == nil {
|
||||
mach.account = olm.NewAccount()
|
||||
mach.account = &OlmAccount{
|
||||
Account: olm.NewAccount(),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mach *OlmMachine) SaveAccount() {
|
||||
err := mach.store.SaveAccount(mach.account)
|
||||
if err != nil {
|
||||
mach.log.Debugfln("Failed to save account: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// NewOneTimeKeys generates new one-time keys and returns a key upload request.
|
||||
// If no new one-time keys are needed, this returns nil. In that case, the upload request should not be made.
|
||||
func (mach *OlmMachine) NewOneTimeKeys() *mautrix.ReqUploadKeys {
|
||||
otks := mach.getOneTimeKeys()
|
||||
if len(otks) == 0 {
|
||||
func (mach *OlmMachine) GetSessions(senderKey string) []*OlmSession {
|
||||
sessions, ok := mach.sessions[senderKey]
|
||||
if !ok {
|
||||
sessions, err := mach.store.LoadSessions(senderKey)
|
||||
if err != nil {
|
||||
mach.log.Debugfln("Failed to load sessions for %s: %v", senderKey, err)
|
||||
sessions = make([]*OlmSession, 0)
|
||||
}
|
||||
mach.sessions[senderKey] = sessions
|
||||
}
|
||||
return sessions
|
||||
}
|
||||
|
||||
func (mach *OlmMachine) SaveSession(senderKey string, session *OlmSession) {
|
||||
mach.sessions[senderKey] = append(mach.sessions[senderKey], session)
|
||||
err := mach.store.SaveSessions(senderKey, mach.sessions[senderKey])
|
||||
if err != nil {
|
||||
mach.log.Debugfln("Failed to save sessions for %s: %v", senderKey, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (mach *OlmMachine) ProcessSyncResponse(resp *mautrix.RespSync) {
|
||||
for _, evt := range resp.ToDevice.Events {
|
||||
evt.Type.Class = event.ToDeviceEventType
|
||||
err := evt.Content.ParseRaw(evt.Type)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
mach.HandleToDeviceEvent(evt)
|
||||
}
|
||||
|
||||
min := mach.account.MaxNumberOfOneTimeKeys() / 2
|
||||
if resp.DeviceOneTimeKeysCount.SignedCurve25519 <= int(min) {
|
||||
err := mach.ShareKeys()
|
||||
if err != nil {
|
||||
mach.log.Debugfln("Failed to share keys: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (mach *OlmMachine) HandleToDeviceEvent(evt *event.Event) {
|
||||
switch evt.Content.Parsed.(type) {
|
||||
case *event.EncryptedEventContent:
|
||||
decryptedEvt, err := mach.DecryptOlmEvent(evt)
|
||||
if err != nil {
|
||||
mach.log.Debugfln("Failed to decrypt to-device event:", err)
|
||||
return
|
||||
}
|
||||
switch content := decryptedEvt.Content.Parsed.(type) {
|
||||
case *event.RoomKeyEventContent:
|
||||
mach.receiveRoomKey(decryptedEvt, content)
|
||||
}
|
||||
// TODO unencrypted to-device events should be handled here. At least m.room_key_request and m.verification.start
|
||||
}
|
||||
}
|
||||
|
||||
func (mach *OlmMachine) getGroupSessions(roomID id.RoomID, senderKey string) map[string]*InboundGroupSession {
|
||||
roomGroupSessions, ok := mach.groupSessions[roomID]
|
||||
if !ok {
|
||||
roomGroupSessions = make(map[string]map[string]*InboundGroupSession)
|
||||
mach.groupSessions[roomID] = roomGroupSessions
|
||||
}
|
||||
senderGroupSessions, ok := roomGroupSessions[senderKey]
|
||||
if !ok {
|
||||
senderGroupSessions = make(map[string]*InboundGroupSession)
|
||||
roomGroupSessions[senderKey] = senderGroupSessions
|
||||
}
|
||||
return senderGroupSessions
|
||||
}
|
||||
|
||||
func (mach *OlmMachine) createGroupSession(senderKey, signingKey string, roomID id.RoomID, sessionID, sessionKey string) {
|
||||
igs, err := NewInboundGroupSession(senderKey, signingKey, roomID, sessionID, sessionKey)
|
||||
if err != nil {
|
||||
mach.log.Debugfln("Failed to create inbound group session: %v", err)
|
||||
} else if string(igs.ID()) != sessionID {
|
||||
mach.log.Debugfln("Mismatched session ID while creating inbound group session")
|
||||
} else {
|
||||
mach.getGroupSessions(roomID, senderKey)[sessionID] = igs
|
||||
// TODO save mach.groupSessions
|
||||
}
|
||||
}
|
||||
|
||||
func (mach *OlmMachine) receiveRoomKey(evt *OlmEvent, content *event.RoomKeyEventContent) {
|
||||
// TODO nio had a comment saying "handle this better" for the case where evt.Keys.Ed25519 is none?
|
||||
if content.Algorithm != event.AlgorithmMegolmV1 || evt.Keys.Ed25519 == "" {
|
||||
return
|
||||
}
|
||||
|
||||
mach.createGroupSession(evt.SenderKey, evt.Keys.Ed25519, content.RoomID, content.SessionID, content.SessionKey)
|
||||
}
|
||||
|
||||
// ShareKeys returns a key upload request.
|
||||
func (mach *OlmMachine) ShareKeys() error {
|
||||
var deviceKeys *mautrix.DeviceKeys
|
||||
if !mach.account.Shared {
|
||||
deviceKeys = mach.account.getInitialKeys(mach.client.UserID, mach.client.DeviceID)
|
||||
}
|
||||
oneTimeKeys := mach.account.getOneTimeKeys(mach.client.UserID, mach.client.DeviceID)
|
||||
if len(oneTimeKeys) == 0 && deviceKeys == nil {
|
||||
return nil
|
||||
}
|
||||
return &mautrix.ReqUploadKeys{
|
||||
OneTimeKeys: otks,
|
||||
}
|
||||
}
|
||||
|
||||
// InitialKeys returns the initial key upload request, including signed device keys and unsigned one-time keys.
|
||||
func (mach *OlmMachine) InitialKeys() (*mautrix.ReqUploadKeys, error) {
|
||||
ed, curve := mach.account.IdentityKeys()
|
||||
deviceKeys := &mautrix.DeviceKeys{
|
||||
UserID: mach.UserID,
|
||||
DeviceID: mach.DeviceID,
|
||||
Algorithms: []string{string(olm.AlgorithmMegolmV1)},
|
||||
Keys: map[id.DeviceKeyID]string{
|
||||
id.NewDeviceKeyID("curve25519", mach.DeviceID): string(curve),
|
||||
id.NewDeviceKeyID("ed25519", mach.DeviceID): string(ed),
|
||||
},
|
||||
Signatures: map[id.UserID]map[id.DeviceKeyID]string{
|
||||
mach.UserID: {
|
||||
// This is filled below.
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
signature, err := mach.account.SignJSON(deviceKeys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deviceKeys.Signatures[mach.UserID][id.NewDeviceKeyID("ed25519", mach.DeviceID)] = signature
|
||||
|
||||
return &mautrix.ReqUploadKeys{
|
||||
req := &mautrix.ReqUploadKeys{
|
||||
DeviceKeys: deviceKeys,
|
||||
OneTimeKeys: mach.getOneTimeKeys(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (mach *OlmMachine) getOneTimeKeys() map[id.KeyID]string {
|
||||
mach.account.GenOneTimeKeys(mach.account.MaxNumberOfOneTimeKeys() / 2)
|
||||
oneTimeKeys := make(map[id.KeyID]string)
|
||||
for keyID, key := range mach.account.OneTimeKeys().Curve25519 {
|
||||
oneTimeKeys[id.NewKeyID("curve25519", keyID)] = string(key)
|
||||
OneTimeKeys: oneTimeKeys,
|
||||
}
|
||||
mach.account.MarkKeysAsPublished()
|
||||
return oneTimeKeys
|
||||
_, err := mach.client.UploadKeys(req)
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
170
crypto/decrypt.go
Normal file
170
crypto/decrypt.go
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
// Copyright (c) 2020 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/id"
|
||||
"maunium.net/go/olm"
|
||||
)
|
||||
|
||||
var (
|
||||
IncorrectEncryptedContentType = errors.New("event content is not instance of *event.EncryptedEventContent")
|
||||
UnsupportedAlgorithm = errors.New("unsupported event encryption algorithm")
|
||||
NotEncryptedForMe = errors.New("olm event doesn't contain ciphertext for this device")
|
||||
UnsupportedOlmMessageType = errors.New("unsupported olm message type")
|
||||
DecryptionFailedWithMatchingSession = errors.New("decryption failed with matching session")
|
||||
DecryptionFailedForNormalMessage = errors.New("decryption failed for normal message")
|
||||
|
||||
SenderMismatch = errors.New("mismatched sender in olm payload")
|
||||
RecipientMismatch = errors.New("mismatched recipient in olm payload")
|
||||
RecipientKeyMismatch = errors.New("mismatched recipient key in olm payload")
|
||||
)
|
||||
|
||||
func (mach *OlmMachine) DecryptMegolmEvent(evt *event.Event) (*event.Event, error) {
|
||||
content, ok := evt.Content.Parsed.(*event.EncryptedEventContent)
|
||||
if !ok {
|
||||
return nil, IncorrectEncryptedContentType
|
||||
}
|
||||
fmt.Println(content.Algorithm)
|
||||
// TODO
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type OlmEventKeys struct {
|
||||
Ed25519 string `json:"ed25519"`
|
||||
}
|
||||
|
||||
type OlmEvent struct {
|
||||
Source *event.Event `json:"-"`
|
||||
|
||||
SenderKey string `json:"-"`
|
||||
|
||||
Sender id.UserID `json:"sender"`
|
||||
SenderDevice id.DeviceID `json:"sender_device"`
|
||||
Keys OlmEventKeys `json:"keys"`
|
||||
Recipient id.UserID `json:"recipient"`
|
||||
RecipientKeys OlmEventKeys `json:"recipient_keys"`
|
||||
|
||||
Type event.Type `json:"type"`
|
||||
Content event.Content `json:"content"`
|
||||
}
|
||||
|
||||
func (mach *OlmMachine) createInboundSession(senderKey, ciphertext string) (*OlmSession, error) {
|
||||
session, err := mach.account.NewInboundSessionFrom(senderKey, ciphertext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mach.SaveAccount()
|
||||
mach.SaveSession(senderKey, session)
|
||||
return session, nil
|
||||
}
|
||||
|
||||
func (mach *OlmMachine) markDeviceForUnwedging(sender id.UserID, senderKey string) {
|
||||
// TODO implement
|
||||
}
|
||||
|
||||
func (mach *OlmMachine) DecryptOlmEvent(evt *event.Event) (*OlmEvent, error) {
|
||||
content, ok := evt.Content.Parsed.(*event.EncryptedEventContent)
|
||||
if !ok {
|
||||
return nil, IncorrectEncryptedContentType
|
||||
} else if content.Algorithm != event.AlgorithmOlmV1 {
|
||||
return nil, UnsupportedAlgorithm
|
||||
}
|
||||
_, ownKey := mach.account.IdentityKeys()
|
||||
ownContent, ok := content.OlmCiphertext[string(ownKey)]
|
||||
if !ok {
|
||||
return nil, NotEncryptedForMe
|
||||
}
|
||||
return mach.decryptOlmEvent(evt, content.SenderKey, ownContent.Type, ownContent.Body)
|
||||
}
|
||||
|
||||
func (mach *OlmMachine) decryptOlmEvent(evt *event.Event, senderKey string, olmType event.OlmMessageType, ciphertext string) (*OlmEvent, error) {
|
||||
if olmType != event.OlmPreKeyMessage && olmType != event.OlmNormalMessage {
|
||||
return nil, UnsupportedOlmMessageType
|
||||
}
|
||||
|
||||
plaintext, err := mach.tryDecryptOlmEvent(senderKey, olmType, ciphertext)
|
||||
if err != nil {
|
||||
if err == DecryptionFailedWithMatchingSession {
|
||||
mach.log.Debugfln("Found matching session yet decryption failed for sender %s with key %s", evt.Sender, senderKey)
|
||||
mach.markDeviceForUnwedging(evt.Sender, senderKey)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Decryption failed with every known session or no known sessions, let's try to create a new session.
|
||||
if plaintext == nil {
|
||||
// New sessions can only be created if it's a prekey message, we can't decrypt the message
|
||||
// if it isn't one at this point in time anymore, so return early.
|
||||
if olmType != event.OlmNormalMessage {
|
||||
mach.markDeviceForUnwedging(evt.Sender, senderKey)
|
||||
return nil, DecryptionFailedForNormalMessage
|
||||
}
|
||||
|
||||
session, err := mach.createInboundSession(senderKey, ciphertext)
|
||||
if err != nil {
|
||||
mach.markDeviceForUnwedging(evt.Sender, senderKey)
|
||||
return nil, errors.Wrap(err, "failed to create new session from prekey message")
|
||||
}
|
||||
|
||||
plaintext, err = session.Decrypt(ciphertext, olm.MsgType(olmType))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decrypt message with session created from prekey message")
|
||||
}
|
||||
}
|
||||
|
||||
var olmEvt OlmEvent
|
||||
err = json.Unmarshal(plaintext, &olmEvt)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse olm payload")
|
||||
}
|
||||
if evt.Sender != olmEvt.Sender {
|
||||
return nil, SenderMismatch
|
||||
} else if mach.client.UserID != olmEvt.Recipient {
|
||||
return nil, RecipientMismatch
|
||||
} else if ed25519, _ := mach.account.IdentityKeys(); string(ed25519) != olmEvt.RecipientKeys.Ed25519 {
|
||||
return nil, RecipientKeyMismatch
|
||||
}
|
||||
|
||||
err = olmEvt.Content.ParseRaw(olmEvt.Type)
|
||||
if err != nil && !event.IsUnsupportedContentType(err) {
|
||||
return nil, errors.Wrap(err, "failed to parse content of olm payload event")
|
||||
}
|
||||
|
||||
olmEvt.Source = evt
|
||||
olmEvt.SenderKey = senderKey
|
||||
|
||||
return &olmEvt, nil
|
||||
}
|
||||
|
||||
func (mach *OlmMachine) tryDecryptOlmEvent(senderKey string, olmType event.OlmMessageType, ciphertext string) ([]byte, error) {
|
||||
for _, session := range mach.GetSessions(senderKey) {
|
||||
if olmType == event.OlmPreKeyMessage {
|
||||
matches, err := session.MatchesInboundSession(ciphertext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !matches {
|
||||
continue
|
||||
}
|
||||
}
|
||||
plaintext, err := session.Decrypt(ciphertext, olm.MsgType(olmType))
|
||||
if err != nil {
|
||||
if olmType == event.OlmPreKeyMessage {
|
||||
return nil, DecryptionFailedWithMatchingSession
|
||||
}
|
||||
} else {
|
||||
return plaintext, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
|
@ -7,11 +7,13 @@
|
|||
package crypto
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"maunium.net/go/mautrix/id"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"maunium.net/go/olm"
|
||||
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -24,40 +26,43 @@ type UserDevice struct {
|
|||
DeviceID id.DeviceID
|
||||
}
|
||||
|
||||
type OlmAccount struct {
|
||||
*olm.Account
|
||||
Shared bool
|
||||
}
|
||||
|
||||
type OlmSession struct {
|
||||
*olm.Session
|
||||
ExpirationMixin
|
||||
}
|
||||
|
||||
func wrapSession(session *olm.Session) *OlmSession {
|
||||
return &OlmSession{
|
||||
Session: session,
|
||||
ExpirationMixin: ExpirationMixin{
|
||||
TimeMixin: TimeMixin{
|
||||
CreationTime: time.Now(),
|
||||
UseTime: time.Now(),
|
||||
},
|
||||
MaxAge: 7 * 24 * time.Hour,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (account *OlmAccount) NewInboundSessionFrom(senderKey, ciphertext string) (*OlmSession, error) {
|
||||
session, err := account.Account.NewInboundSessionFrom(olm.Curve25519(senderKey), ciphertext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_ = account.RemoveOneTimeKeys(session)
|
||||
return wrapSession(session), nil
|
||||
}
|
||||
|
||||
func (session *OlmSession) Encrypt(plaintext string) (olm.MsgType, string) {
|
||||
session.UseTime = time.Now()
|
||||
return session.Session.Encrypt(plaintext)
|
||||
}
|
||||
|
||||
func (session *OlmSession) Decrypt(ciphertext string, msgType olm.MsgType) (string, error) {
|
||||
func (session *OlmSession) Decrypt(ciphertext string, msgType olm.MsgType) ([]byte, error) {
|
||||
session.UseTime = time.Now()
|
||||
return session.Session.Decrypt(ciphertext, msgType)
|
||||
}
|
||||
|
||||
type TimeMixin struct {
|
||||
CreationTime time.Time
|
||||
UseTime time.Time
|
||||
}
|
||||
|
||||
type ExpirationMixin struct {
|
||||
TimeMixin
|
||||
MaxAge time.Duration
|
||||
}
|
||||
|
||||
func (exp *ExpirationMixin) Expired() bool {
|
||||
return exp.CreationTime.Add(exp.MaxAge).Before(time.Now())
|
||||
}
|
||||
|
||||
type InboundGroupSession struct {
|
||||
*olm.InboundGroupSession
|
||||
|
||||
|
|
@ -68,6 +73,17 @@ type InboundGroupSession struct {
|
|||
ForwardingChains []string
|
||||
}
|
||||
|
||||
func NewInboundGroupSession(senderKey, signingKey string, roomID id.RoomID, sessionID, sessionKey string) (*InboundGroupSession, error) {
|
||||
igs, err := olm.NewInboundGroupSession([]byte(sessionKey))
|
||||
return &InboundGroupSession{
|
||||
InboundGroupSession: igs,
|
||||
SigningKey: signingKey,
|
||||
SenderKey: senderKey,
|
||||
RoomID: roomID,
|
||||
ForwardingChains: nil,
|
||||
}, err
|
||||
}
|
||||
|
||||
type OutboundGroupSession struct {
|
||||
*olm.OutboundGroupSession
|
||||
|
||||
|
|
|
|||
|
|
@ -7,15 +7,74 @@
|
|||
package crypto
|
||||
|
||||
import (
|
||||
"maunium.net/go/olm"
|
||||
"encoding/gob"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Store interface {
|
||||
Key() []byte
|
||||
SaveAccount(*OlmAccount) error
|
||||
LoadAccount() (*OlmAccount, error)
|
||||
|
||||
SaveAccount(*olm.Account)
|
||||
LoadAccount() *olm.Account
|
||||
|
||||
LoadSessions() []*olm.Session
|
||||
SaveSession(string, *olm.Session)
|
||||
SaveSessions(string, []*OlmSession) error
|
||||
LoadSessions(string) ([]*OlmSession, error)
|
||||
}
|
||||
|
||||
type GobStore struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
func (gs *GobStore) LoadAccount() (*OlmAccount, error) {
|
||||
file, err := os.Open(filepath.Join(gs.Path, "account.gob"))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
dec := gob.NewDecoder(file)
|
||||
var account OlmAccount
|
||||
err = dec.Decode(&account)
|
||||
_ = file.Close()
|
||||
return &account, err
|
||||
}
|
||||
|
||||
func (gs *GobStore) SaveAccount(account *OlmAccount) error {
|
||||
file, err := os.OpenFile(filepath.Join(gs.Path, "account.gob"), os.O_CREATE|os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = gob.NewEncoder(file).Encode(account)
|
||||
_ = file.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
func pathSafe(val string) string {
|
||||
return strings.ReplaceAll(val, "/", "-")
|
||||
}
|
||||
|
||||
func (gs *GobStore) LoadSessions(senderKey string) ([]*OlmSession, error) {
|
||||
file, err := os.Open(filepath.Join(gs.Path, "sessions", pathSafe(senderKey) + ".gob"))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return []*OlmSession{}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
dec := gob.NewDecoder(file)
|
||||
var sessions []*OlmSession
|
||||
err = dec.Decode(&sessions)
|
||||
_ = file.Close()
|
||||
return sessions, err
|
||||
}
|
||||
|
||||
func (gs *GobStore) SaveSessions(senderKey string, sessions []*OlmSession) error {
|
||||
file, err := os.OpenFile(filepath.Join(gs.Path, "sessions", pathSafe(senderKey) + ".gob"), os.O_CREATE|os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = gob.NewEncoder(file).Encode(sessions)
|
||||
_ = file.Close()
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
25
crypto/time.go
Normal file
25
crypto/time.go
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) 2020 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type TimeMixin struct {
|
||||
CreationTime time.Time
|
||||
UseTime time.Time
|
||||
}
|
||||
|
||||
type ExpirationMixin struct {
|
||||
TimeMixin
|
||||
MaxAge time.Duration
|
||||
}
|
||||
|
||||
func (exp *ExpirationMixin) Expired() bool {
|
||||
return exp.CreationTime.Add(exp.MaxAge).Before(time.Now())
|
||||
}
|
||||
3
go.mod
3
go.mod
|
|
@ -4,8 +4,9 @@ go 1.14
|
|||
|
||||
require (
|
||||
github.com/fatih/structs v1.1.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/russross/blackfriday/v2 v2.0.1
|
||||
github.com/stretchr/testify v1.5.1
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a
|
||||
maunium.net/go/olm v0.0.0-20200419222421-d050af0532a1
|
||||
maunium.net/go/olm v0.0.0-20200420235207-35b7cc0d340c
|
||||
)
|
||||
|
|
|
|||
3
go.sum
3
go.sum
|
|
@ -4,6 +4,8 @@ github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
|||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/mitchellh/mapstructure v1.2.2 h1:dxe5oCinTXiTIcfgmZecdCzPmAJKd46KsCWc35r0TV4=
|
||||
github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
|
|
@ -33,3 +35,4 @@ maunium.net/go/canonicaljson v0.1.1 h1:5G+jhiFG/wHPPcZ79tSxbZgiE577vPz0nVfXfKnG0
|
|||
maunium.net/go/canonicaljson v0.1.1/go.mod h1:0X1niXxzCoKAURvqbYmimuu6IQpbqd8fIOiwPU5JwRg=
|
||||
maunium.net/go/olm v0.0.0-20200419222421-d050af0532a1 h1:6EHqKE4e/osCwhri1lr9u8hzTGFOjei6fGry/cEkA9s=
|
||||
maunium.net/go/olm v0.0.0-20200419222421-d050af0532a1/go.mod h1:SaLfmFDzrmqovMPeXoVJOZqij88nuJI9ZWR4/hV03v4=
|
||||
maunium.net/go/olm v0.0.0-20200420235207-35b7cc0d340c/go.mod h1:SaLfmFDzrmqovMPeXoVJOZqij88nuJI9ZWR4/hV03v4=
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue