// Command verify sets up cross-signing keys for a Matrix bot user. // This eliminates the "Encrypted by a device not verified by its owner" warning. // // Usage: // // go run -tags goolm ./cmd/verify --homeserver https://... --username asistente-2 --password --token package main import ( "context" "crypto/sha256" "fmt" "os" "path/filepath" "strings" "github.com/spf13/cobra" "maunium.net/go/mautrix" "maunium.net/go/mautrix/crypto" "maunium.net/go/mautrix/crypto/cryptohelper" "maunium.net/go/mautrix/id" ) func main() { var ( homeserver string username string password string token string storePath string ) root := &cobra.Command{ Use: "verify", Short: "Set up cross-signing keys for a Matrix bot", Long: `Generates and uploads cross-signing keys so the bot's device is verified. This removes the "Encrypted by a device not verified by its owner" warning. Requires the bot's access token and password (for UIA during key upload).`, RunE: func(cmd *cobra.Command, args []string) error { homeserver = strings.TrimRight(homeserver, "/") serverName := homeserver serverName = strings.TrimPrefix(serverName, "https://") serverName = strings.TrimPrefix(serverName, "http://") userID := id.UserID(fmt.Sprintf("@%s:%s", username, serverName)) fmt.Printf("→ Setting up cross-signing for %s\n", userID) // Create mautrix client client, err := mautrix.NewClient(homeserver, userID, token) if err != nil { return fmt.Errorf("create client: %w", err) } ctx := context.Background() // Resolve device ID whoami, err := client.Whoami(ctx) if err != nil { return fmt.Errorf("whoami: %w", err) } client.DeviceID = whoami.DeviceID fmt.Printf("→ Device ID: %s\n", client.DeviceID) // Initialize crypto sum := sha256.Sum256([]byte(token)) pickleKey := sum[:] dbPath := filepath.Join(storePath, "crypto.db") if err := os.MkdirAll(filepath.Dir(dbPath), 0700); err != nil { return fmt.Errorf("create store dir: %w", err) } helper, err := cryptohelper.NewCryptoHelper(client, pickleKey, dbPath) if err != nil { return fmt.Errorf("create crypto helper: %w", err) } helper.DBAccountID = username if err := helper.Init(ctx); err != nil { return fmt.Errorf("init crypto: %w", err) } defer helper.Close() client.Crypto = helper // Get the OlmMachine to generate cross-signing keys olmMachine := helper.Machine() if olmMachine == nil { return fmt.Errorf("olm machine not available") } fmt.Println("→ Generating and uploading cross-signing keys...") _, _, err = olmMachine.GenerateAndUploadCrossSigningKeysWithPassword(ctx, password, "") if err != nil { // If keys already exist, try to just sign our device fmt.Printf(" Note: %v\n", err) fmt.Println("→ Attempting to sign own device with existing keys...") return signOwnDevice(ctx, olmMachine, client) } fmt.Println("✓ Cross-signing keys uploaded successfully") fmt.Printf("✓ Device %s is now verified by %s\n", client.DeviceID, userID) return nil }, } root.Flags().StringVar(&homeserver, "homeserver", "", "Matrix homeserver URL") root.Flags().StringVar(&username, "username", "", "Bot username (without @ or server)") root.Flags().StringVar(&password, "password", "", "Bot password (for UIA auth)") root.Flags().StringVar(&token, "token", "", "Bot access token") root.Flags().StringVar(&storePath, "store", "./data/verify-crypto/", "Crypto store path") _ = root.MarkFlagRequired("homeserver") _ = root.MarkFlagRequired("username") _ = root.MarkFlagRequired("password") _ = root.MarkFlagRequired("token") if err := root.Execute(); err != nil { os.Exit(1) } } func signOwnDevice(ctx context.Context, mach *crypto.OlmMachine, client *mautrix.Client) error { device := &id.Device{ UserID: client.UserID, DeviceID: client.DeviceID, } err := mach.SignOwnDevice(ctx, device) if err != nil { return fmt.Errorf("sign own device: %w", err) } fmt.Printf("✓ Device %s signed with cross-signing key\n", client.DeviceID) return nil }