Merge branch 'cross-signing'
This commit is contained in:
+18
-11
@@ -104,19 +104,23 @@ func NewCommandProcessor(parent *MainView) *CommandProcessor {
|
||||
"e": {"edit"},
|
||||
"dl": {"download"},
|
||||
"o": {"open"},
|
||||
"4s": {"ssss"},
|
||||
"s4": {"ssss"},
|
||||
"cs": {"cross-signing"},
|
||||
},
|
||||
autocompleters: map[string]CommandAutocompleter{
|
||||
"devices": autocompleteDevice,
|
||||
"device": autocompleteDevice,
|
||||
"verify": autocompleteDevice,
|
||||
"unverify": autocompleteDevice,
|
||||
"blacklist": autocompleteDevice,
|
||||
"upload": autocompleteFile,
|
||||
"download": autocompleteFile,
|
||||
"open": autocompleteFile,
|
||||
"import": autocompleteFile,
|
||||
"export": autocompleteFile,
|
||||
"export-room": autocompleteFile,
|
||||
"devices": autocompleteUser,
|
||||
"device": autocompleteDevice,
|
||||
"verify": autocompleteUser,
|
||||
"verify-device": autocompleteDevice,
|
||||
"unverify": autocompleteDevice,
|
||||
"blacklist": autocompleteDevice,
|
||||
"upload": autocompleteFile,
|
||||
"download": autocompleteFile,
|
||||
"open": autocompleteFile,
|
||||
"import": autocompleteFile,
|
||||
"export": autocompleteFile,
|
||||
"export-room": autocompleteFile,
|
||||
},
|
||||
commands: map[string]CommandHandler{
|
||||
"unknown-command": cmdUnknownCommand,
|
||||
@@ -164,6 +168,7 @@ func NewCommandProcessor(parent *MainView) *CommandProcessor {
|
||||
|
||||
"fingerprint": cmdFingerprint,
|
||||
"devices": cmdDevices,
|
||||
"verify-device": cmdVerifyDevice,
|
||||
"verify": cmdVerify,
|
||||
"device": cmdDevice,
|
||||
"unverify": cmdUnverify,
|
||||
@@ -172,6 +177,8 @@ func NewCommandProcessor(parent *MainView) *CommandProcessor {
|
||||
"import": cmdImportKeys,
|
||||
"export": cmdExportKeys,
|
||||
"export-room": cmdExportRoomKeys,
|
||||
"ssss": cmdSSSS,
|
||||
"cross-signing": cmdCrossSigning,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
+406
-10
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ func cmdNoCrypto(cmd *Command) {
|
||||
var (
|
||||
cmdDevices = cmdNoCrypto
|
||||
cmdDevice = cmdNoCrypto
|
||||
cmdVerifyDevice = cmdNoCrypto
|
||||
cmdVerify = cmdNoCrypto
|
||||
cmdUnverify = cmdNoCrypto
|
||||
cmdBlacklist = cmdNoCrypto
|
||||
|
||||
+20
-10
@@ -17,6 +17,9 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"maunium.net/go/mauview"
|
||||
"maunium.net/go/tcell"
|
||||
)
|
||||
@@ -41,13 +44,20 @@ type PasswordModal struct {
|
||||
parent *MainView
|
||||
}
|
||||
|
||||
func (view *MainView) AskPassword(title string, isNew bool) (string, bool) {
|
||||
pwm := NewPasswordModal(view, title, isNew)
|
||||
func (view *MainView) AskPassword(title, thing, placeholder string, isNew bool) (string, bool) {
|
||||
pwm := NewPasswordModal(view, title, thing, placeholder, isNew)
|
||||
view.ShowModal(pwm)
|
||||
view.parent.Render()
|
||||
return pwm.Wait()
|
||||
}
|
||||
|
||||
func NewPasswordModal(parent *MainView, title string, isNew bool) *PasswordModal {
|
||||
func NewPasswordModal(parent *MainView, title, thing, placeholder string, isNew bool) *PasswordModal {
|
||||
if placeholder == "" {
|
||||
placeholder = "correct horse battery staple"
|
||||
}
|
||||
if thing == "" {
|
||||
thing = strings.ToLower(title)
|
||||
}
|
||||
pwm := &PasswordModal{
|
||||
parent: parent,
|
||||
form: mauview.NewForm(),
|
||||
@@ -64,13 +74,13 @@ func NewPasswordModal(parent *MainView, title string, isNew bool) *PasswordModal
|
||||
|
||||
pwm.text = mauview.NewTextField()
|
||||
if isNew {
|
||||
pwm.text.SetText("Create a passphrase")
|
||||
pwm.text.SetText(fmt.Sprintf("Create a %s", thing))
|
||||
} else {
|
||||
pwm.text.SetText("Enter the passphrase")
|
||||
pwm.text.SetText(fmt.Sprintf("Enter the %s", thing))
|
||||
}
|
||||
pwm.input = mauview.NewInputField().
|
||||
SetMaskCharacter('*').
|
||||
SetPlaceholder("correct horse battery staple")
|
||||
SetPlaceholder(placeholder)
|
||||
pwm.form.AddComponent(pwm.text, 1, 1, 3, 1)
|
||||
pwm.form.AddFormItem(pwm.input, 1, 2, 3, 1)
|
||||
|
||||
@@ -78,10 +88,10 @@ func NewPasswordModal(parent *MainView, title string, isNew bool) *PasswordModal
|
||||
height += 3
|
||||
pwm.confirmInput = mauview.NewInputField().
|
||||
SetMaskCharacter('*').
|
||||
SetPlaceholder("correct horse battery staple").
|
||||
SetPlaceholder(placeholder).
|
||||
SetChangedFunc(pwm.HandleChange)
|
||||
pwm.input.SetChangedFunc(pwm.HandleChange)
|
||||
pwm.confirmText = mauview.NewTextField().SetText("Confirm passphrase")
|
||||
pwm.confirmText = mauview.NewTextField().SetText(fmt.Sprintf("Confirm %s", thing))
|
||||
|
||||
pwm.form.SetRow(3, 1).SetRow(4, 1).SetRow(5, 1)
|
||||
pwm.form.AddComponent(pwm.confirmText, 1, 4, 3, 1)
|
||||
@@ -125,9 +135,9 @@ func (pwm *PasswordModal) ClickSubmit() {
|
||||
|
||||
func (pwm *PasswordModal) Wait() (string, bool) {
|
||||
select {
|
||||
case result := <- pwm.outputChan:
|
||||
case result := <-pwm.outputChan:
|
||||
return result, true
|
||||
case <- pwm.cancelChan:
|
||||
case <-pwm.cancelChan:
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -771,7 +771,7 @@ func (view *RoomView) getRelationForNewEvent() *ifc.Relation {
|
||||
}
|
||||
} else if view.replying != nil {
|
||||
return &ifc.Relation{
|
||||
Type: event.RelReference,
|
||||
Type: event.RelReply,
|
||||
Event: view.replying,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +148,8 @@ func (vm *VerificationModal) VerificationMethods() []crypto.VerificationMethod {
|
||||
return []crypto.VerificationMethod{crypto.VerificationMethodEmoji{}, crypto.VerificationMethodDecimal{}}
|
||||
}
|
||||
|
||||
func (vm *VerificationModal) VerifySASMatch(_ *crypto.DeviceIdentity, data crypto.SASData) bool {
|
||||
func (vm *VerificationModal) VerifySASMatch(device *crypto.DeviceIdentity, data crypto.SASData) bool {
|
||||
vm.device = device
|
||||
var typeName string
|
||||
if data.Type() == event.SASDecimal {
|
||||
typeName = "numbers"
|
||||
|
||||
Reference in New Issue
Block a user