Allow any UI auth for uploading cross signing keys

Fix endless loop with UI auth causing 401 when uploading keys. Use any
type for key backup setup request auth data so that unmarshaled objects
can be used that have signatures embedded.

Generating keys will now also return them if we also want to setup key
backup without storing the keys in-between.
pull/175/head
Toni Spets 2024-02-02 14:44:22 +02:00 committed by Toni Spets
parent 11c2907f2e
commit b131dab9de
3 changed files with 57 additions and 41 deletions

View File

@ -2061,7 +2061,7 @@ func (cli *Client) GetKeyBackupLatestVersion(ctx context.Context) (resp *RespRoo
// CreateKeyBackupVersion creates a new key backup.
//
// See: https://spec.matrix.org/v1.9/client-server-api/#post_matrixclientv3room_keysversion
func (cli *Client) CreateKeyBackupVersion(ctx context.Context, req *ReqRoomKeysVersionCreate) (resp *RespRoomKeysVersionCreate, err error) {
func (cli *Client) CreateKeyBackupVersion(ctx context.Context, req *ReqRoomKeysVersionCreate[backup.MegolmAuthData]) (resp *RespRoomKeysVersionCreate, err error) {
urlPath := cli.BuildClientURL("v3", "room_keys", "version")
_, err = cli.MakeRequest(ctx, http.MethodPost, urlPath, req, &resp)
return
@ -2080,7 +2080,7 @@ func (cli *Client) GetKeyBackupVersion(ctx context.Context, version id.KeyBackup
// the auth_data can be modified.
//
// See: https://spec.matrix.org/v1.9/client-server-api/#put_matrixclientv3room_keysversionversion
func (cli *Client) UpdateKeyBackupVersion(ctx context.Context, version id.KeyBackupVersion, req *ReqRoomKeysVersionUpdate) error {
func (cli *Client) UpdateKeyBackupVersion(ctx context.Context, version id.KeyBackupVersion, req *ReqRoomKeysVersionUpdate[backup.MegolmAuthData]) error {
urlPath := cli.BuildClientURL("v3", "room_keys", "version", version)
_, err := cli.MakeRequest(ctx, http.MethodPut, urlPath, nil, nil)
return err
@ -2145,7 +2145,7 @@ func (cli *Client) UploadCrossSigningKeys(ctx context.Context, keys *UploadCross
RequestJSON: keys,
SensitiveContent: keys.Auth != nil,
})
if respErr, ok := err.(HTTPError); ok && respErr.IsStatus(http.StatusUnauthorized) {
if respErr, ok := err.(HTTPError); ok && respErr.IsStatus(http.StatusUnauthorized) && uiaCallback != nil {
// try again with UI auth
var uiAuthResp RespUserInteractive
if err := json.Unmarshal(content, &uiAuthResp); err != nil {
@ -2154,7 +2154,7 @@ func (cli *Client) UploadCrossSigningKeys(ctx context.Context, keys *UploadCross
auth := uiaCallback(&uiAuthResp)
if auth != nil {
keys.Auth = auth
return cli.UploadCrossSigningKeys(ctx, keys, uiaCallback)
return cli.UploadCrossSigningKeys(ctx, keys, nil)
}
}
return err

View File

@ -14,6 +14,7 @@ import (
"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.
@ -57,33 +58,8 @@ func (mach *OlmMachine) retrieveDecryptXSigningKey(ctx context.Context, keyName
return decryptedKey, nil
}
// 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, userPassword, passphrase string) (string, error) {
key, err := mach.SSSS.GenerateAndUploadKey(ctx, passphrase)
if err != nil {
return "", 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 "", err
}
recoveryKey := key.RecoveryKey()
// Store the private keys in SSSS
if err := mach.UploadCrossSigningKeysToSSSS(ctx, key, keysCache); err != nil {
return recoveryKey, fmt.Errorf("failed to upload cross-signing keys to SSSS: %w", err)
}
// Publish cross-signing keys
err = mach.PublishCrossSigningKeys(ctx, keysCache, func(uiResp *mautrix.RespUserInteractive) interface{} {
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,
@ -92,17 +68,44 @@ func (mach *OlmMachine) GenerateAndUploadCrossSigningKeys(ctx context.Context, u
User: mach.Client.UserID.String(),
Password: userPassword,
}
})
}, passphrase)
}
// 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 recoveryKey, fmt.Errorf("failed to publish cross-signing keys: %w", err)
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 "", nil, fmt.Errorf("failed to publish cross-signing keys: %w", err)
}
err = mach.SSSS.SetDefaultKeyID(ctx, key.ID)
if err != nil {
return recoveryKey, fmt.Errorf("failed to mark %s as the default key: %w", key.ID, err)
return "", nil, fmt.Errorf("failed to mark %s as the default key: %w", key.ID, err)
}
return recoveryKey, nil
return key.RecoveryKey(), keysCache, nil
}
// UploadCrossSigningKeysToSSSS stores the given cross-signing keys on the server encrypted with the given key.
@ -116,5 +119,17 @@ func (mach *OlmMachine) UploadCrossSigningKeysToSSSS(ctx context.Context, key *s
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
}

View File

@ -97,8 +97,9 @@ type ReqUIAuthFallback struct {
type ReqUIAuthLogin struct {
BaseAuthData
User string `json:"user"`
Password string `json:"password"`
User string `json:"user,omitempty"`
Password string `json:"password,omitempty"`
Token string `json:"token,omitempty"`
}
// ReqCreateRoom is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3createroom
@ -428,14 +429,14 @@ type ReqBeeperSplitRoom struct {
Parts []BeeperSplitRoomPart `json:"parts"`
}
type ReqRoomKeysVersionCreate struct {
type ReqRoomKeysVersionCreate[A any] struct {
Algorithm id.KeyBackupAlgorithm `json:"algorithm"`
AuthData json.RawMessage `json:"auth_data"`
AuthData A `json:"auth_data"`
}
type ReqRoomKeysVersionUpdate struct {
type ReqRoomKeysVersionUpdate[A any] struct {
Algorithm id.KeyBackupAlgorithm `json:"algorithm"`
AuthData json.RawMessage `json:"auth_data"`
AuthData A `json:"auth_data"`
Version id.KeyBackupVersion `json:"version,omitempty"`
}