Display raw PGP signature in verification dialog

Simplified approach: When pressing 'V' in note details, the TUI temporarily
exits to terminal mode and prints the raw PGP signature directly to stdout.

This allows users to:
- Select and copy the signature using their terminal's native copy/paste
- Works with any shell (bash, fish, zsh, etc.)
- No dependency on clipboard tools (xclip, xsel, pbcopy, clip)
- No complex shell-specific commands needed

The signature is displayed with verification status and clear formatting,
then waits for Enter to return to the TUI.

This is the simplest and most universal solution for viewing and copying
PGP signatures for external verification in Kleopatra or GPG tools.
This commit is contained in:
Claude
2025-12-14 19:50:56 +00:00
parent fe3c0710c6
commit 070e76467c

View File

@@ -119,7 +119,7 @@ class TUI:
self.flash_time = time.time() self.flash_time = time.time()
def verify_note_signature(self): def verify_note_signature(self):
"""Show detailed verification dialog for current note with signature export""" """Show signature verification and print raw signature to terminal"""
if not self.current_note: if not self.current_note:
return return
@@ -137,139 +137,31 @@ class TUI:
self._show_simple_dialog(title, message) self._show_simple_dialog(title, message)
return return
# Save signature to file and attempt clipboard copy # Temporarily exit curses to print signature to terminal
import subprocess curses.endwin()
import platform
from pathlib import Path
sig_file = Path.home() / ".trace" / "last_signature.txt"
sig_file.parent.mkdir(parents=True, exist_ok=True)
try: try:
sig_file.write_text(self.current_note.signature) # Print verification status
file_saved = True print("\n" + "=" * 70)
except Exception:
file_saved = False
# Try to copy to clipboard
clipboard_success = False
clipboard_method = None
try:
system = platform.system()
if system == "Linux":
# Try xclip first, then xsel
try:
subprocess.run(['xclip', '-selection', 'clipboard'],
input=self.current_note.signature.encode(),
check=True, timeout=2, capture_output=True)
clipboard_success = True
clipboard_method = "xclip"
except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
try:
subprocess.run(['xsel', '--clipboard', '--input'],
input=self.current_note.signature.encode(),
check=True, timeout=2, capture_output=True)
clipboard_success = True
clipboard_method = "xsel"
except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
pass
elif system == "Darwin": # macOS
try:
subprocess.run(['pbcopy'],
input=self.current_note.signature.encode(),
check=True, timeout=2, capture_output=True)
clipboard_success = True
clipboard_method = "pbcopy"
except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
pass
elif system == "Windows":
try:
subprocess.run(['clip'],
input=self.current_note.signature.encode(),
check=True, timeout=2, capture_output=True)
clipboard_success = True
clipboard_method = "clip"
except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
pass
except Exception:
pass
# Build dialog message based on verification and export status
if verified: if verified:
title = "✓ Signature Verified" print(f"✓ SIGNATURE VERIFIED - Signed by: {info}")
message = [
"The note's signature is valid.",
"",
f"Signed by: {info}",
"",
"This note has not been tampered with since signing.",
]
else: else:
title = "✗ Signature Verification Failed" print(f"✗ SIGNATURE VERIFICATION FAILED - Reason: {info}")
message = [ print("=" * 70)
"The note's signature could not be verified.", print("\nRAW PGP SIGNATURE (select and copy from terminal):")
"", print("-" * 70)
f"Reason: {info}",
"",
"Possible causes:",
"- Public key not in keyring",
"- Note content was modified after signing",
"- Signature is corrupted",
]
# Add export status information # Print the actual signature
message.append("") print(self.current_note.signature)
message.append("" * 60)
message.append("EXPORT STATUS:")
message.append("")
# Clipboard status with clear feedback print("-" * 70)
if clipboard_success: print("\nPress Enter to return to trace...")
message.append(f"✓ Clipboard: Copied successfully (using {clipboard_method})") input()
message.append("")
message.append(" You can paste directly into Kleopatra or GPG tools.")
else:
message.append("✗ Clipboard: Failed to copy")
message.append("")
if system == "Linux":
message.append(" Install xclip or xsel for clipboard support:")
message.append(" sudo apt install xclip # Debian/Ubuntu")
message.append(" sudo dnf install xclip # Fedora")
else:
message.append(" Clipboard tool not available on this system.")
message.append("") finally:
# Restore curses mode
# File save status self.stdscr.refresh()
if file_saved: curses.doupdate()
message.append(f"✓ File: Saved to {sig_file}")
else:
message.append("✗ File: Failed to save (check permissions)")
# Add GPG verification commands if file was saved
if file_saved:
message.append("")
message.append("" * 60)
message.append("VERIFY WITH GPG:")
message.append("")
# Linux/macOS commands
message.append("Linux/macOS:")
message.append(f" gpg --verify <(cat {sig_file})")
message.append("")
message.append(" # Or view the signature:")
message.append(f" cat {sig_file}")
message.append("")
# Windows PowerShell commands
message.append("Windows PowerShell:")
message.append(f" Get-Content {sig_file} | gpg --verify")
message.append("")
message.append(" # Or view the signature:")
message.append(f" Get-Content {sig_file}")
self._show_simple_dialog(title, message)
def _show_simple_dialog(self, title, message_lines): def _show_simple_dialog(self, title, message_lines):
"""Display a simple scrollable dialog with the given title and message lines""" """Display a simple scrollable dialog with the given title and message lines"""