Git Signing

Git Commit Signing: Methods & Evolution

This document tracks the evolution of Git commit signing in this repository, comparing different methods for security, convenience, and user experience.

📊 Comparison Matrix

MethodProtocolStorageUX: InteractionUX: CachingSecurity
GnuPG (PGP)OpenPGPDisk (~/.gnupg)Passphrase promptVia gpg-agentHigh
YubiKey (SK)SSH (-sk)HardwarePhysical TouchN/A (Hardware enforced)Ultra High
SSH Key (Disk)SSHDisk (~/.ssh)None (if no pass)N/AHigh
SSH Key (Agent)SSHDisk (Encrypted)Passphrase (Once)Via ssh-agentExcellent Balance

🕰️ History of Signing in this Repo

1. The GnuPG Era (gpgSign = true)

Initially, the repository used standard GnuPG.

  • The Pain: Every commit triggered a pinentry popup. While gpg-agent can cache passwords, it often feels “heavy” and separate from the SSH workflow.
  • The Config: signingKey was a hex ID like 1A7FAEC8....

2. The YubiKey Era (ed25519-sk)

To maximize security, we moved to hardware-backed SSH keys.

  • The Pain: The -sk (Security Key) protocol is designed for User Presence. This means for every single signature (every commit), the hardware mandates a physical touch. While highly secure against remote attacks, it is exhausting for frequent commits.
  • The Config: signingKey pointed to a .pub file for a key with the -sk suffix.

3. The Modern SSH Era (Current)

We now use standard SSH signing (gpg.format = ssh).

  • The Solution: A standard ed25519 key stored on disk. This removes the hardware touch requirement while maintaining strong cryptographic verification.

🔐 Passwords vs. Passphrases

There is often confusion between these terms in the context of Git signing:

  1. GnuPG Password/Passphrase:
    • Used to unlock your PGP private key in the GPG keyring.
    • Managed by gpg-agent.
  2. SSH Passphrase:
    • Used to encrypt the key file itself on your disk.
    • If you didn’t set one during ssh-keygen, the file is stored in plaintext (Solution 1).
    • If you did set one, you must “unlock” the file to use it.
  3. The Difference:
    • Solution 1 (No Passphrase): Convenience at the cost of disk-level security.
    • Solution 2 (With Passphrase + Agent): The key is encrypted on disk. You type the passphrase once at login, the ssh-agent decrypts it into memory, and Git uses it instantly for the rest of the session.

🏗️ Agent Architecture & Conflict Resolution

This repository is configured to allow GnuPG and SSH to co-exist perfectly without conflicts.

1. Agent Independence

In modules/shell/gnupg.nix, we set enableSSHSupport = false.

  • Why?: By default, gpg-agent can try to act as an SSH agent. This often causes conflicts with the standard OpenSSH agent. By disabling this, we ensure the OpenSSH agent handles all SSH keys, while GnuPG only handles encryption/decryption.

2. Hardware Bridge (scdaemon)

We use disable-ccid in scdaemon.conf.

  • Why?: This prevents GnuPG from trying to “monopolize” the YubiKey’s smartcard reader. It allows the system’s pcscd daemon to manage the hardware, so that both SSH-SK (FIDO2) and GPG operations can share the device gracefully.

3. Git Protocol Selection

In modules/shell/git.nix, we explicitly set gpg.format = "ssh".

  • Why?: This tells Git to ignore the GPG binary entirely for signing operations. It uses the SSH protocol and your SSH agent directly, bypassing any GPG-related delays or prompts.

🛠️ Configuration Details

To use SSH signing, the following must be set in .gitconfig:

[user]
    signingKey = /home/user/.ssh/id_ed25519.pub
[gpg]
    format = ssh
[commit]
    gpgSign = true

Why is this better than GnuPG?

  1. Simplicity: You only manage one type of key (SSH) for both server access and Git signing.
  2. Performance: SSH agents are generally faster and more integrated into Linux shells than the GPG stack.
  3. Auditability: Verification uses a simple allowed_signers file instead of a complex PGP web of trust.

✅ Signature Verification (allowed_signers)

To ensure your commits (and others’) show up as “Verified” in your Git log or on platforms like GitHub, you must maintain an allowed_signers file.

  • Role: While the signingKey is used to create a signature, the allowed_signers file is used to verify it. Without it, Git won’t know if a signature belongs to a trusted user.
  • Config: In this repository, the file is managed at ~/.config/git/allowed_signers and enabled for all signing methods (SSH and YubiKey).
  • Format: Each line contains the user’s email, the key type, and the public key string:
    [email protected] namespaces="git" ssh-ed25519 AAAAC3Nza...
  • Sync: Ensure your latest signing keys are added to config/git/allowed_signers to maintain a “Verified” status across all your devices.