matrix-prometheus/vendor/maunium.net/go/mautrix/crypto/encryptolm.go

119 lines
3.9 KiB
Go

// 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"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/crypto/olm"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
func (mach *OlmMachine) encryptOlmEvent(session *OlmSession, recipient *DeviceIdentity, evtType event.Type, content event.Content) *event.EncryptedEventContent {
evt := &DecryptedOlmEvent{
Sender: mach.Client.UserID,
SenderDevice: mach.Client.DeviceID,
Keys: OlmEventKeys{Ed25519: mach.account.SigningKey()},
Recipient: recipient.UserID,
RecipientKeys: OlmEventKeys{Ed25519: recipient.SigningKey},
Type: evtType,
Content: content,
}
plaintext, err := json.Marshal(evt)
if err != nil {
panic(err)
}
mach.Log.Trace("Encrypting olm message for %s with session %s: %s", recipient.IdentityKey, session.ID(), session.Describe())
msgType, ciphertext := session.Encrypt(plaintext)
err = mach.CryptoStore.UpdateSession(recipient.IdentityKey, session)
if err != nil {
mach.Log.Warn("Failed to update olm session in crypto store after encrypting: %v", err)
}
return &event.EncryptedEventContent{
Algorithm: id.AlgorithmOlmV1,
SenderKey: mach.account.IdentityKey(),
OlmCiphertext: event.OlmCiphertexts{
recipient.IdentityKey: {
Type: msgType,
Body: string(ciphertext),
},
},
}
}
func (mach *OlmMachine) shouldCreateNewSession(identityKey id.IdentityKey) bool {
if !mach.CryptoStore.HasSession(identityKey) {
return true
}
mach.devicesToUnwedgeLock.Lock()
_, shouldUnwedge := mach.devicesToUnwedge[identityKey]
if shouldUnwedge {
delete(mach.devicesToUnwedge, identityKey)
}
mach.devicesToUnwedgeLock.Unlock()
return shouldUnwedge
}
func (mach *OlmMachine) createOutboundSessions(input map[id.UserID]map[id.DeviceID]*DeviceIdentity) error {
request := make(mautrix.OneTimeKeysRequest)
for userID, devices := range input {
request[userID] = make(map[id.DeviceID]id.KeyAlgorithm)
for deviceID, identity := range devices {
if mach.shouldCreateNewSession(identity.IdentityKey) {
request[userID][deviceID] = id.KeyAlgorithmSignedCurve25519
}
}
if len(request[userID]) == 0 {
delete(request, userID)
}
}
if len(request) == 0 {
return nil
}
resp, err := mach.Client.ClaimKeys(&mautrix.ReqClaimKeys{
OneTimeKeys: request,
Timeout: 10 * 1000,
})
if err != nil {
return fmt.Errorf("failed to claim keys: %w", err)
}
for userID, user := range resp.OneTimeKeys {
for deviceID, oneTimeKeys := range user {
var oneTimeKey mautrix.OneTimeKey
var keyID id.KeyID
for keyID, oneTimeKey = range oneTimeKeys {
break
}
keyAlg, keyIndex := keyID.Parse()
if keyAlg != id.KeyAlgorithmSignedCurve25519 {
mach.Log.Warn("Unexpected key ID algorithm in one-time key response for %s of %s: %s", deviceID, userID, keyID)
continue
}
identity := input[userID][deviceID]
if ok, err := olm.VerifySignatureJSON(oneTimeKey, userID, deviceID.String(), identity.SigningKey); err != nil {
mach.Log.Error("Failed to verify signature for %s of %s: %v", deviceID, userID, err)
} else if !ok {
mach.Log.Warn("Invalid signature for %s of %s", deviceID, userID)
} else if sess, err := mach.account.Internal.NewOutboundSession(identity.IdentityKey, oneTimeKey.Key); err != nil {
mach.Log.Error("Failed to create outbound session for %s of %s: %v", deviceID, userID, err)
} else {
wrapped := wrapSession(sess)
err = mach.CryptoStore.AddSession(identity.IdentityKey, wrapped)
if err != nil {
mach.Log.Error("Failed to store created session for %s of %s: %v", deviceID, userID, err)
} else {
mach.Log.Debug("Created new Olm session with %s/%s (OTK ID: %d)", userID, deviceID, keyIndex)
}
}
}
}
return nil
}