mirror of
https://mau.dev/mautrix/go.git
synced 2026-03-14 14:25:53 +01:00
176 lines
6.5 KiB
Go
176 lines
6.5 KiB
Go
// Copyright (c) 2020 Nikos Filippakis
|
|
//
|
|
// 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 (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"maunium.net/go/mautrix"
|
|
"maunium.net/go/mautrix/crypto/ssss"
|
|
"maunium.net/go/mautrix/crypto/utils"
|
|
"maunium.net/go/mautrix/event"
|
|
"maunium.net/go/mautrix/id"
|
|
)
|
|
|
|
// FetchCrossSigningKeysFromSSSS fetches all the cross-signing keys from SSSS, decrypts them using the given key and stores them in the olm machine.
|
|
func (mach *OlmMachine) FetchCrossSigningKeysFromSSSS(ctx context.Context, key *ssss.Key) error {
|
|
masterKey, err := mach.retrieveDecryptXSigningKey(ctx, event.AccountDataCrossSigningMaster, key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
selfSignKey, err := mach.retrieveDecryptXSigningKey(ctx, event.AccountDataCrossSigningSelf, key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
userSignKey, err := mach.retrieveDecryptXSigningKey(ctx, event.AccountDataCrossSigningUser, key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return mach.ImportCrossSigningKeys(CrossSigningSeeds{
|
|
MasterKey: masterKey[:],
|
|
SelfSigningKey: selfSignKey[:],
|
|
UserSigningKey: userSignKey[:],
|
|
})
|
|
}
|
|
|
|
// retrieveDecryptXSigningKey retrieves the requested cross-signing key from SSSS and decrypts it using the given SSSS key.
|
|
func (mach *OlmMachine) retrieveDecryptXSigningKey(ctx context.Context, keyName event.Type, key *ssss.Key) ([utils.AESCTRKeyLength]byte, error) {
|
|
var decryptedKey [utils.AESCTRKeyLength]byte
|
|
var encData ssss.EncryptedAccountDataEventContent
|
|
|
|
// retrieve and parse the account data for this key type from SSSS
|
|
err := mach.Client.GetAccountData(ctx, keyName.Type, &encData)
|
|
if err != nil {
|
|
return decryptedKey, err
|
|
}
|
|
|
|
decrypted, err := encData.Decrypt(keyName.Type, key)
|
|
if err != nil {
|
|
return decryptedKey, err
|
|
}
|
|
copy(decryptedKey[:], decrypted)
|
|
return decryptedKey, nil
|
|
}
|
|
|
|
func (mach *OlmMachine) GenerateAndUploadCrossSigningKeysWithPassword(ctx context.Context, userPassword, passphrase string) (string, *CrossSigningKeysCache, error) {
|
|
return mach.GenerateAndUploadCrossSigningKeys(ctx, func(uiResp *mautrix.RespUserInteractive) interface{} {
|
|
return &mautrix.ReqUIAuthLogin{
|
|
BaseAuthData: mautrix.BaseAuthData{
|
|
Type: mautrix.AuthTypePassword,
|
|
Session: uiResp.Session,
|
|
},
|
|
User: mach.Client.UserID.String(),
|
|
Password: userPassword,
|
|
}
|
|
}, passphrase)
|
|
}
|
|
|
|
func (mach *OlmMachine) VerifyWithRecoveryKey(ctx context.Context, recoveryKey string) error {
|
|
keyID, keyData, err := mach.SSSS.GetDefaultKeyData(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get default SSSS key data: %w", err)
|
|
}
|
|
key, err := keyData.VerifyRecoveryKey(keyID, recoveryKey)
|
|
if errors.Is(err, ssss.ErrUnverifiableKey) {
|
|
mach.machOrContextLog(ctx).Warn().
|
|
Str("key_id", keyID).
|
|
Msg("SSSS key is unverifiable, trying to use without verifying")
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
err = mach.FetchCrossSigningKeysFromSSSS(ctx, key)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to fetch cross-signing keys from SSSS: %w", err)
|
|
}
|
|
err = mach.SignOwnDevice(ctx, mach.OwnIdentity())
|
|
if err != nil {
|
|
return fmt.Errorf("failed to sign own device: %w", err)
|
|
}
|
|
err = mach.SignOwnMasterKey(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to sign own master key: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (mach *OlmMachine) GenerateAndVerifyWithRecoveryKey(ctx context.Context) (recoveryKey string, err error) {
|
|
recoveryKey, _, err = mach.GenerateAndUploadCrossSigningKeys(ctx, nil, "")
|
|
if err != nil {
|
|
err = fmt.Errorf("failed to generate and upload cross-signing keys: %w", err)
|
|
} else if err = mach.SignOwnDevice(ctx, mach.OwnIdentity()); err != nil {
|
|
err = fmt.Errorf("failed to sign own device: %w", err)
|
|
} else if err = mach.SignOwnMasterKey(ctx); err != nil {
|
|
err = fmt.Errorf("failed to sign own master key: %w", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
// GenerateAndUploadCrossSigningKeys generates a new key with all corresponding cross-signing keys.
|
|
//
|
|
// A passphrase can be provided to generate the SSSS key. If the passphrase is empty, a random key
|
|
// is used. The base58-formatted recovery key is the first return parameter.
|
|
//
|
|
// The account password of the user is required for uploading keys to the server.
|
|
func (mach *OlmMachine) GenerateAndUploadCrossSigningKeys(ctx context.Context, uiaCallback mautrix.UIACallback, passphrase string) (string, *CrossSigningKeysCache, error) {
|
|
key, err := mach.SSSS.GenerateAndUploadKey(ctx, passphrase)
|
|
if err != nil {
|
|
return "", nil, fmt.Errorf("failed to generate and upload SSSS key: %w", err)
|
|
}
|
|
|
|
// generate the three cross-signing keys
|
|
keysCache, err := mach.GenerateCrossSigningKeys()
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
|
|
// Store the private keys in SSSS
|
|
if err := mach.UploadCrossSigningKeysToSSSS(ctx, key, keysCache); err != nil {
|
|
return "", nil, fmt.Errorf("failed to upload cross-signing keys to SSSS: %w", err)
|
|
}
|
|
|
|
// Publish cross-signing keys
|
|
err = mach.PublishCrossSigningKeys(ctx, keysCache, uiaCallback)
|
|
if err != nil {
|
|
return key.RecoveryKey(), keysCache, fmt.Errorf("failed to publish cross-signing keys: %w", err)
|
|
}
|
|
|
|
err = mach.SSSS.SetDefaultKeyID(ctx, key.ID)
|
|
if err != nil {
|
|
return key.RecoveryKey(), keysCache, fmt.Errorf("failed to mark %s as the default key: %w", key.ID, err)
|
|
}
|
|
|
|
return key.RecoveryKey(), keysCache, nil
|
|
}
|
|
|
|
// UploadCrossSigningKeysToSSSS stores the given cross-signing keys on the server encrypted with the given key.
|
|
func (mach *OlmMachine) UploadCrossSigningKeysToSSSS(ctx context.Context, key *ssss.Key, keys *CrossSigningKeysCache) error {
|
|
if err := mach.SSSS.SetEncryptedAccountData(ctx, event.AccountDataCrossSigningMaster, keys.MasterKey.Seed(), key); err != nil {
|
|
return err
|
|
}
|
|
if err := mach.SSSS.SetEncryptedAccountData(ctx, event.AccountDataCrossSigningSelf, keys.SelfSigningKey.Seed(), key); err != nil {
|
|
return err
|
|
}
|
|
if err := mach.SSSS.SetEncryptedAccountData(ctx, event.AccountDataCrossSigningUser, keys.UserSigningKey.Seed(), key); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Also store these locally
|
|
if err := mach.CryptoStore.PutCrossSigningKey(ctx, mach.Client.UserID, id.XSUsageMaster, keys.MasterKey.PublicKey()); err != nil {
|
|
return err
|
|
}
|
|
if err := mach.CryptoStore.PutCrossSigningKey(ctx, mach.Client.UserID, id.XSUsageSelfSigning, keys.SelfSigningKey.PublicKey()); err != nil {
|
|
return err
|
|
}
|
|
if err := mach.CryptoStore.PutCrossSigningKey(ctx, mach.Client.UserID, id.XSUsageUserSigning, keys.UserSigningKey.PublicKey()); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|