package cmd import ( "fmt" "os" "github.com/frenocorp/pop/internal/pgp" "github.com/spf13/cobra" ) func pgpCmd() *cobra.Command { cmd := &cobra.Command{ Use: "pgp", Short: "Manage PGP keys", Long: `Import, export, list, and manage PGP keys for ProtonMail encryption.`, } cmd.AddCommand(pgpListCmd()) cmd.AddCommand(pgpImportCmd()) cmd.AddCommand(pgpExportCmd()) cmd.AddCommand(pgpRemoveCmd()) cmd.AddCommand(pgpEncryptCmd()) cmd.AddCommand(pgpDecryptCmd()) cmd.AddCommand(pgpSignCmd()) cmd.AddCommand(pgpVerifyCmd()) return cmd } func pgpListCmd() *cobra.Command { return &cobra.Command{ Use: "list", Short: "List all imported PGP keys", RunE: func(cmd *cobra.Command, args []string) error { store, err := pgp.NewKeyStore() if err != nil { return fmt.Errorf("failed to create PGP store: %w", err) } keys, err := store.ListKeys() if err != nil { return fmt.Errorf("failed to list keys: %w", err) } if len(keys) == 0 { fmt.Println("No PGP keys imported.") return nil } for _, key := range keys { fmt.Printf("Key ID: %s\n Fingerprint: %s\n Emails: %v\n Trust: %s\n Encrypt: %t Sign: %t\n\n", key.KeyID, key.Fingerprint, key.Emails, key.TrustLevel, key.CanEncrypt, key.CanSign) } return nil }, } } func pgpImportCmd() *cobra.Command { var trustLevel, filePath string cmd := &cobra.Command{ Use: "import ", Short: "Import a PGP key from file", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { filePath = args[0] store, err := pgp.NewKeyStore() if err != nil { return fmt.Errorf("failed to create PGP store: %w", err) } key, err := store.ImportKeyFromFile(filePath, trustLevel) if err != nil { return fmt.Errorf("failed to import key: %w", err) } fmt.Printf("Imported key:\n ID: %s\n Fingerprint: %s\n Emails: %v\n", key.KeyID, key.Fingerprint, key.Emails) return nil }, } cmd.Flags().StringVar(&trustLevel, "trust", "unknown", "Trust level for the key") return cmd } func pgpExportCmd() *cobra.Command { var outputPath string cmd := &cobra.Command{ Use: "export ", Short: "Export a PGP key", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { keyID := args[0] store, err := pgp.NewKeyStore() if err != nil { return fmt.Errorf("failed to create PGP store: %w", err) } if outputPath == "" { outputPath = keyID + ".asc" } if err := store.ExportKey(keyID, outputPath); err != nil { return fmt.Errorf("failed to export key: %w", err) } fmt.Printf("Exported key to: %s\n", outputPath) return nil }, } cmd.Flags().StringVarP(&outputPath, "output", "o", "", "Output file path") return cmd } func pgpRemoveCmd() *cobra.Command { return &cobra.Command{ Use: "remove ", Short: "Remove a PGP key", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { keyID := args[0] store, err := pgp.NewKeyStore() if err != nil { return fmt.Errorf("failed to create PGP store: %w", err) } if err := store.RemoveKey(keyID); err != nil { return fmt.Errorf("failed to remove key: %w", err) } fmt.Printf("Removed key: %s\n", keyID) return nil }, } } func pgpEncryptCmd() *cobra.Command { var plaintext, keyID string cmd := &cobra.Command{ Use: "encrypt --plaintext \"text\"", Short: "Encrypt plaintext with a public key", RunE: func(cmd *cobra.Command, args []string) error { if len(args) > 0 { keyID = args[0] } if plaintext == "" { return fmt.Errorf("plaintext is required (--plaintext)") } if keyID == "" { return fmt.Errorf("key ID is required (positional argument)") } store, err := pgp.NewKeyStore() if err != nil { return fmt.Errorf("failed to create PGP store: %w", err) } encrypted, err := store.EncryptData(keyID, plaintext) if err != nil { return fmt.Errorf("failed to encrypt: %w", err) } fmt.Println(encrypted) return nil }, } cmd.Flags().StringVarP(&plaintext, "plaintext", "p", "", "Plaintext to encrypt") cmd.MarkFlagRequired("plaintext") return cmd } func pgpDecryptCmd() *cobra.Command { var keyID, passphrase, encryptedData string cmd := &cobra.Command{ Use: "decrypt ", Short: "Decrypt PGP-encrypted data", RunE: func(cmd *cobra.Command, args []string) error { if len(args) > 0 { keyID = args[0] } store, err := pgp.NewKeyStore() if err != nil { return fmt.Errorf("failed to create PGP store: %w", err) } decrypted, err := store.DecryptData(keyID, encryptedData, passphrase) if err != nil { return fmt.Errorf("failed to decrypt: %w", err) } fmt.Println(decrypted) return nil }, } cmd.Flags().StringVarP(&encryptedData, "encrypted", "e", "", "Encrypted data (armored)") cmd.Flags().StringVarP(&passphrase, "passphrase", "P", "", "Passphrase for private key") cmd.MarkFlagRequired("encrypted") return cmd } func pgpSignCmd() *cobra.Command { var plaintext, keyID, passphrase string cmd := &cobra.Command{ Use: "sign --plaintext \"text\"", Short: "Sign plaintext with a private key", RunE: func(cmd *cobra.Command, args []string) error { if len(args) > 0 { keyID = args[0] } if plaintext == "" { return fmt.Errorf("plaintext is required (--plaintext)") } if keyID == "" { return fmt.Errorf("key ID is required (positional argument)") } store, err := pgp.NewKeyStore() if err != nil { return fmt.Errorf("failed to create PGP store: %w", err) } signature, err := store.SignData(keyID, plaintext, passphrase) if err != nil { return fmt.Errorf("failed to sign: %w", err) } fmt.Println(signature) return nil }, } cmd.Flags().StringVarP(&plaintext, "plaintext", "p", "", "Plaintext to sign") cmd.Flags().StringVarP(&passphrase, "passphrase", "P", "", "Passphrase for private key") cmd.MarkFlagRequired("plaintext") return cmd } func pgpVerifyCmd() *cobra.Command { var keyID, message, signature string cmd := &cobra.Command{ Use: "verify ", Short: "Verify a detached signature", RunE: func(cmd *cobra.Command, args []string) error { if len(args) > 0 { keyID = args[0] } store, err := pgp.NewKeyStore() if err != nil { return fmt.Errorf("failed to create PGP store: %w", err) } verified, err := store.VerifySignature(keyID, message, signature) if err != nil { return fmt.Errorf("verification failed: %w", err) } if verified { fmt.Println("Signature is valid.") } else { fmt.Println("Signature is INVALID.") } return nil }, } cmd.Flags().StringVarP(&message, "message", "m", "", "Message to verify") cmd.Flags().StringVarP(&signature, "signature", "s", "", "Detached signature") cmd.MarkFlagRequired("message") cmd.MarkFlagRequired("signature") return cmd } func init() { _ = os.Getenv }