| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -19,6 +19,7 @@
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				package ui
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				import (
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					"errors"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					"fmt"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					"io/ioutil"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					"path/filepath"
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -26,7 +27,10 @@ import (
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					"time"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					"unicode"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					ifc "maunium.net/go/gomuks/interface"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					"maunium.net/go/mautrix"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					"maunium.net/go/mautrix/crypto"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					"maunium.net/go/mautrix/crypto/ssss"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					"maunium.net/go/mautrix/id"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -77,15 +81,20 @@ func autocompleteDeviceDeviceID(cmd *CommandAutocomplete) (completions []string,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func autocompleteUser(cmd *CommandAutocomplete) ([]string, string) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if len(cmd.Args) == 1 && !unicode.IsSpace(rune(cmd.RawArgs[len(cmd.RawArgs)-1])) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return autocompleteDeviceUserID(cmd)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					return []string{}, ""
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func autocompleteDevice(cmd *CommandAutocomplete) ([]string, string) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if len(cmd.Args) == 0 {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return []string{}, ""
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					} else if len(cmd.Args) == 1 && !unicode.IsSpace(rune(cmd.RawArgs[len(cmd.RawArgs)-1])) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return autocompleteDeviceUserID(cmd)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					} else if cmd.Command != "devices" {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return autocompleteDeviceDeviceID(cmd)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					return []string{}, ""
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					return autocompleteDeviceDeviceID(cmd)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func getDevice(cmd *Command) *crypto.DeviceIdentity {
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -134,7 +143,11 @@ func cmdDevices(cmd *Command) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					var buf strings.Builder
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					for _, device := range devices {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						_, _ = fmt.Fprintf(&buf, "%s (%s) - %s\n    Fingerprint: %s\n", device.DeviceID, device.Name, device.Trust.String(), device.Fingerprint())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						trust := device.Trust.String()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if device.Trust == crypto.TrustStateUnset && mach.IsDeviceTrusted(device) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							trust = "verified (transitive)"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						_, _ = fmt.Fprintf(&buf, "%s (%s) - %s\n    Fingerprint: %s\n", device.DeviceID, device.Name, trust, device.Fingerprint())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					resp := buf.String()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					cmd.Reply("%s", resp[:len(resp)-1])
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -149,13 +162,28 @@ func cmdDevice(cmd *Command) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if device.Deleted {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						deviceType = "Deleted device"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					mach := cmd.Matrix.Crypto().(*crypto.OlmMachine)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					trustState := device.Trust.String()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if device.Trust == crypto.TrustStateUnset && mach.IsDeviceTrusted(device) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						trustState = "verified (transitive)"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					cmd.Reply("%s %s of %s\nFingerprint: %s\nIdentity key: %s\nDevice name: %s\nTrust state: %s",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						deviceType, device.DeviceID, device.UserID,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						device.Fingerprint(), device.IdentityKey,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						device.Name, device.Trust.String())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						device.Name, trustState)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func cmdVerify(cmd *Command) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func crossSignDevice(cmd *Command, device *crypto.DeviceIdentity) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					mach := cmd.Matrix.Crypto().(*crypto.OlmMachine)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					err := mach.SignOwnDevice(device)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Failed to upload cross-signing signature: %v", err)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					} else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Successfully cross-signed %s (%s)", device.DeviceID, device.Name)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func cmdVerifyDevice(cmd *Command) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					device := getDevice(cmd)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if device == nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -184,11 +212,49 @@ func cmdVerify(cmd *Command) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if device.Trust == crypto.TrustStateBlacklisted {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							action = "unblacklisted and verified"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						device.Trust = crypto.TrustStateVerified
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						putDevice(cmd, device, action)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if device.UserID == cmd.Matrix.Client().UserID {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							crossSignDevice(cmd, device)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							device.Trust = crypto.TrustStateVerified
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							putDevice(cmd, device, action)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						} else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							putDevice(cmd, device, action)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							cmd.Reply("Warning: verifying individual devices of other users is not synced with cross-signing")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func cmdVerify(cmd *Command) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if len(cmd.Args) < 1 {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Usage: /%s <user ID> [--force]", cmd.OrigCommand)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					force := len(cmd.Args) >= 2 && strings.ToLower(cmd.Args[1]) == "--force"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					userID := id.UserID(cmd.Args[0])
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					room := cmd.Room.Room
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if !room.Encrypted {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("In-room verification is only supported in encrypted rooms")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if (!room.IsDirect || room.OtherUser != userID) && !force {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("This doesn't seem to be a direct chat. Either switch to a direct chat with %s, "+
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							"or use `--force` to start the verification anyway.", userID)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					mach := cmd.Matrix.Crypto().(*crypto.OlmMachine)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if mach.CrossSigningKeys == nil && !force {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Cross-signing private keys not cached. Generate or fetch cross-signing keys with `/cross-signing`, " +
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							"or use `--force` to start the verification anyway")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					modal := NewVerificationModal(cmd.MainView, &crypto.DeviceIdentity{UserID: userID}, mach.DefaultSASTimeout)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					_, err := mach.NewInRoomSASVerificationWith(cmd.Room.Room.ID, userID, modal, 120*time.Second)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Failed to start in-room verification: %v", err)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					cmd.MainView.ShowModal(modal)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func cmdUnverify(cmd *Command) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					device := getDevice(cmd)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if device == nil {
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -243,7 +309,7 @@ func cmdImportKeys(cmd *Command) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Failed to read %s: %v", path, err)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					passphrase, ok := cmd.MainView.AskPassword("Key import", false)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					passphrase, ok := cmd.MainView.AskPassword("Key import", "passphrase", "", false)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if !ok {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Passphrase entry cancelled")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -263,7 +329,7 @@ func exportKeys(cmd *Command, sessions []*crypto.InboundGroupSession) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Failed to get absolute path: %v", err)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					passphrase, ok := cmd.MainView.AskPassword("Key export", true)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					passphrase, ok := cmd.MainView.AskPassword("Key export", "passphrase", "", true)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if !ok {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Passphrase entry cancelled")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -299,3 +365,333 @@ func cmdExportRoomKeys(cmd *Command) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					exportKeys(cmd, sessions)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				const ssssHelp = `Usage: /%s <subcommand> [...]
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				Subcommands:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				* status [key ID] - Check the status of your SSSS.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				* generate [--set-default] - Generate a SSSS key and optionally set it as the default.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				* set-default <key ID> - Set a SSSS key as the default.`
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func cmdSSSS(cmd *Command) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if len(cmd.Args) == 0 {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply(ssssHelp, cmd.OrigCommand)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					mach := cmd.Matrix.Crypto().(*crypto.OlmMachine)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					switch strings.ToLower(cmd.Args[0]) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					case "status":
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						keyID := ""
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if len(cmd.Args) > 1 {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							keyID = cmd.Args[1]
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmdS4Status(cmd, mach, keyID)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					case "generate":
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						setDefault := len(cmd.Args) > 1 && strings.ToLower(cmd.Args[1]) == "--set-default"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmdS4Generate(cmd, mach, setDefault)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					case "set-default":
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if len(cmd.Args) < 2 {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							cmd.Reply("Usage: /%s set-default <key ID>", cmd.OrigCommand)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmdS4SetDefault(cmd, mach, cmd.Args[1])
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					default:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply(ssssHelp, cmd.OrigCommand)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func cmdS4Status(cmd *Command, mach *crypto.OlmMachine, keyID string) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					var keyData *ssss.KeyMetadata
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					var err error
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if len(keyID) == 0 {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						keyID, keyData, err = mach.SSSS.GetDefaultKeyData()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					} else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						keyData, err = mach.SSSS.GetKeyData(keyID)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if errors.Is(err, ssss.ErrNoDefaultKeyAccountDataEvent) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("SSSS is not set up: no default key set")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					} else if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Failed to get key data: %v", err)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					hasPassphrase := "no"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if keyData.Passphrase != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						hasPassphrase = fmt.Sprintf("yes (alg=%s,bits=%d,iter=%d)", keyData.Passphrase.Algorithm, keyData.Passphrase.Bits, keyData.Passphrase.Iterations)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					algorithm := keyData.Algorithm
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if algorithm != ssss.AlgorithmAESHMACSHA2 {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						algorithm += " (not supported!)"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					cmd.Reply("Default key is set.\n  Key ID: %s\n  Has passphrase: %s\n  Algorithm: %s", keyID, hasPassphrase, algorithm)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func cmdS4Generate(cmd *Command, mach *crypto.OlmMachine, setDefault bool) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					passphrase, ok := cmd.MainView.AskPassword("Passphrase", "", "", true)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if !ok {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					key, err := ssss.NewKey(passphrase)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Failed to generate new key: %v", err)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					err = mach.SSSS.SetKeyData(key.ID, key.Metadata)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Failed to upload key metadata: %v", err)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					// TODO if we start persisting command replies, the recovery key needs to be moved into a popup
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					cmd.Reply("Successfully generated key %s\nRecovery key: %s", key.ID, key.RecoveryKey())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if setDefault {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						err = mach.SSSS.SetDefaultKeyID(key.ID)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							cmd.Reply("Failed to set key as default: %v", err)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					} else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("You can use `/%s set-default %s` to set it as the default", cmd.OrigCommand, key.ID)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func cmdS4SetDefault(cmd *Command, mach *crypto.OlmMachine, keyID string) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					_, err := mach.SSSS.GetKeyData(keyID)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if errors.Is(err, mautrix.MNotFound) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							cmd.Reply("Couldn't find key data on server")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						} else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							cmd.Reply("Failed to fetch key data: %v", err)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					err = mach.SSSS.SetDefaultKeyID(keyID)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Failed to set key as default: %v", err)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					} else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Successfully set key %s as default", keyID)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				const crossSigningHelp = `Usage: /%s <subcommand> [...]
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				Subcommands:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				* status
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    Check the status of your own cross-signing keys.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				* generate [--force]
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    Generate and upload new cross-signing keys.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    This will prompt you to enter your account password.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    If you already have existing keys, --force is required.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				* self-sign
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    Sign the current device with cached cross-signing keys.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				* fetch [--save-to-disk]
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    Fetch your cross-signing keys from SSSS and decrypt them.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    If --save-to-disk is specified, the keys are saved to disk.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				* upload
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    Upload your cross-signing keys to SSSS.`
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func cmdCrossSigning(cmd *Command) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if len(cmd.Args) == 0 {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply(crossSigningHelp, cmd.OrigCommand)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					client := cmd.Matrix.Client()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					mach := cmd.Matrix.Crypto().(*crypto.OlmMachine)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					switch strings.ToLower(cmd.Args[0]) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					case "status":
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmdCrossSigningStatus(cmd, mach)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					case "generate":
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						force := len(cmd.Args) > 1 && strings.ToLower(cmd.Args[1]) == "--force"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmdCrossSigningGenerate(cmd, cmd.Matrix, mach, client, force)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					case "fetch":
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						saveToDisk := len(cmd.Args) > 1 && strings.ToLower(cmd.Args[1]) == "--save-to-disk"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmdCrossSigningFetch(cmd, mach, saveToDisk)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					case "upload":
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmdCrossSigningUpload(cmd, mach)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					case "self-sign":
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmdCrossSigningSelfSign(cmd, mach)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					default:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply(crossSigningHelp, cmd.OrigCommand)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func cmdCrossSigningStatus(cmd *Command, mach *crypto.OlmMachine) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					keys := mach.GetOwnCrossSigningPublicKeys()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if keys == nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if mach.CrossSigningKeys != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							cmd.Reply("Cross-signing keys are cached, but not published")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						} else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							cmd.Reply("Didn't find published cross-signing keys")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if mach.CrossSigningKeys != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Cross-signing keys are published and private keys are cached")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					} else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Cross-signing keys are published, but private keys are not cached")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					cmd.Reply("Master key: %s", keys.MasterKey)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					cmd.Reply("User signing key: %s", keys.UserSigningKey)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					cmd.Reply("Self-signing key: %s", keys.SelfSigningKey)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func cmdCrossSigningFetch(cmd *Command, mach *crypto.OlmMachine, saveToDisk bool) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					key := getSSSS(cmd, mach)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if key == nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					err := mach.FetchCrossSigningKeysFromSSSS(key)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Error fetching cross-signing keys: %v", err)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if saveToDisk {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Saving keys to disk is not yet implemented")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					cmd.Reply("Successfully unlocked cross-signing keys")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func cmdCrossSigningGenerate(cmd *Command, container ifc.MatrixContainer, mach *crypto.OlmMachine, client *mautrix.Client, force bool) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if !force {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						existingKeys := mach.GetOwnCrossSigningPublicKeys()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if existingKeys != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							cmd.Reply("Found existing cross-signing keys. Use `--force` if you want to overwrite them.")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					keys, err := mach.GenerateCrossSigningKeys()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Failed to generate cross-signing keys: %v", err)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					err = mach.PublishCrossSigningKeys(keys, func(uia *mautrix.RespUserInteractive) interface{} {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if !uia.HasSingleStageFlow(mautrix.AuthTypePassword) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							for _, flow := range uia.Flows {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								if len(flow.Stages) != 1 {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									return nil
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								cmd.Reply("Opening browser for authentication")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								err := container.UIAFallback(flow.Stages[0], uia.Session)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									cmd.Reply("Authentication failed: %v", err)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									return nil
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								return &mautrix.ReqUIAuthFallback{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									Session: uia.Session,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									User:    mach.Client.UserID.String(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							cmd.Reply("No supported authentication mechanisms found")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							return nil
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						password, ok := cmd.MainView.AskPassword("Account password", "", "correct horse battery staple", false)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if !ok {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							return nil
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return &mautrix.ReqUIAuthLogin{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							BaseAuthData: mautrix.BaseAuthData{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								Type:    mautrix.AuthTypePassword,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								Session: uia.Session,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							},
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							User:     mach.Client.UserID.String(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							Password: password,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					})
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Failed to publish cross-signing keys: %v", err)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					cmd.Reply("Successfully generated and published cross-signing keys")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					err = mach.SignOwnMasterKey()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Failed to sign master key with device key: %v", err)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func getSSSS(cmd *Command, mach *crypto.OlmMachine) *ssss.Key {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					_, keyData, err := mach.SSSS.GetDefaultKeyData()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if errors.Is(err, mautrix.MNotFound) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							cmd.Reply("SSSS not set up, use `!ssss generate --set-default` first")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						} else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							cmd.Reply("Failed to fetch default SSSS key data: %v", err)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return nil
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					var key *ssss.Key
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if keyData.Passphrase != nil && keyData.Passphrase.Algorithm == ssss.PassphraseAlgorithmPBKDF2 {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						passphrase, ok := cmd.MainView.AskPassword("Passphrase", "", "correct horse battery staple", false)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if !ok {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							return nil
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						key, err = keyData.VerifyPassphrase(passphrase)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if errors.Is(err, ssss.ErrIncorrectSSSSKey) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							cmd.Reply("Incorrect passphrase")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							return nil
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					} else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						recoveryKey, ok := cmd.MainView.AskPassword("Recovery key", "", "tDAK LMRH PiYE bdzi maCe xLX5 wV6P Nmfd c5mC wLef 15Fs VVSc", false)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if !ok {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							return nil
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						key, err = keyData.VerifyRecoveryKey(recoveryKey)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if errors.Is(err, ssss.ErrInvalidRecoveryKey) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							cmd.Reply("Malformed recovery key")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							return nil
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						} else if errors.Is(err, ssss.ErrIncorrectSSSSKey) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							cmd.Reply("Incorrect recovery key")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							return nil
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					// All the errors should already be handled above, this is just for backup
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Failed to get SSSS key: %v", err)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return nil
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					return key
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func cmdCrossSigningUpload(cmd *Command, mach *crypto.OlmMachine) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if mach.CrossSigningKeys == nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Cross-signing keys not cached, use `!%s generate` first", cmd.OrigCommand)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					key := getSSSS(cmd, mach)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if key == nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					err := mach.UploadCrossSigningKeysToSSSS(key, mach.CrossSigningKeys)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Failed to upload keys to SSSS: %v", err)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					} else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Successfully uploaded cross-signing keys to SSSS")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func cmdCrossSigningSelfSign(cmd *Command, mach *crypto.OlmMachine) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if mach.CrossSigningKeys == nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Cross-signing keys not cached")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					err := mach.SignOwnDevice(mach.OwnIdentity())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Failed to self-sign: %v", err)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					} else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						cmd.Reply("Successfully self-signed. This device is now trusted by other devices")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
	
		
			
				
					
					| 
						 
							
							
							
						 
					 | 
				
			
			 | 
			 | 
			
				 
 |