Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save githubaff0/e87ae6f6cbdc24e3d6f3ae34dd94fd78 to your computer and use it in GitHub Desktop.
Save githubaff0/e87ae6f6cbdc24e3d6f3ae34dd94fd78 to your computer and use it in GitHub Desktop.

SSH + Yubikeys via FIDO2 on MacOS

Resources

Prerequisites

Yubikey firmware should be >5.2.3

  • It's supposed to work for ecdsa-sk on earlier versions but it never worked for me.
  • I recommend setting up a PIN code for FIDO2 operations using their Authenticator app like this.

OpenSSH

  • MacOS builtin SSH is supposed to work nowadays but it still demands a FIDO SecurityKeyProvider, and then still fails...
  • Very useful discussion on this topic!
  • Pros
    • Supports ssh-keygen -K to recover the SSH key from a Yubikey for use on a new system
  • Cons
    • MacOS builtin SSH offers the UseKeychain config option or ssh-add --apple-use-keychain, although it's probably a bad practice. Link.

Quick Start

$ brew install libfido2 ykman openssh

# Ensure that openssh server is not running
$ brew services

# Reload the shell
$ exec bash

# Yubikey info
$ ykman info
Device type: YubiKey 5C Nano
Serial number: XXXXX
Firmware version: 5.7.1
Form factor: Nano (USB-C)
Enabled USB interfaces: OTP, FIDO, CCID

Applications
Yubico OTP  	Enabled
FIDO U2F    	Enabled
FIDO2       	Enabled
OATH        	Enabled
PIV         	Enabled
OpenPGP     	Enabled
YubiHSM Auth	Enabled

# Homebrew SSH version
$ ssh -V
OpenSSH_9.9p1, OpenSSL 3.4.0 22 Oct 2024

# MacOS SSH version
$ /usr/bin/ssh -V
OpenSSH_9.8p1, LibreSSL 3.3.6

Pick the correct device if several are connected

$ ykman list

List the current SSH credentials

$ ykman --device XXXXXX fido credentials list --csv

Generate a SSH key

image

The instructions found in most articles won't allow generating more than 1 key. To do this, we must pass -O user=myUser for each key.

For convenience it's best to also append an identifier to the application option, as long as it starts with ssh:.

Passphrase is redundant, overkill and painful to use with Yubikey, it can be empty.

$ function ssh-keygen-yk() { 
  local myUser="$1"
  local myHost="$2"
  ssh-keygen -t ed25519-sk -O resident -N '' -f ~/.ssh/id_ed25519.$myHost.$myUser  -C ${myUser}@${myHost} -O user=${myUser}@${myHost} -O application=ssh:${myUser}@${myHost}
}

$ ssh-keygen-sk jaybuidl machine

Connecting to a remote server

Same as usual

Server hardening

# Support public key cryptography (includes FIDO2)
PubkeyAuthentication yes

# Enforce User Verification: 
# touch-required: touch
# verify-required: touch + PIN
PubkeyAuthOptions touch-required

# Disable password authentication
PasswordAuthentication no
PermitEmptyPasswords no
PermitRootLogin no

Failed Attempts

Old Yubikey with firmware v5.1.2

Everything failed

New Yubikey with firmware v5.7.1

With built-in MacOS SSH

It fails:

$ ssh-keygen -t ecdsa-sk -O resident -O verify-required -C "testing yubikey" -vv
Generating public/private ecdsa-sk key pair.
You may need to touch your authenticator to authorize key generation.
No FIDO SecurityKeyProvider specified

Even after following these instructions, it fails:

$ brew install

$ brew info libfido2
==> libfido2: stable 1.15.0 (bottled)
Provides library functionality for FIDO U2F & FIDO 2.0, including USB
https://developers.yubico.com/libfido2/
Installed
/opt/homebrew/Cellar/libfido2/1.15.0 (562 files, 1.3MB) *
  Poured from bottle using the formulae.brew.sh API on 2024-11-26 at 12:09:33
From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/lib/libfido2.rb


$ SSH_SK_PROVIDER=/opt/homebrew/Cellar/libfido2/1.15.0/lib/libfido2.dylib ssh-keygen -t ecdsa-sk -O resident -O verify-required -C "testing yubikey
Generating public/private ecdsa-sk key pair.
You may need to touch your authenticator to authorize key generation.
lib_contains_symbol: /opt/homebrew/Cellar/libfido2/1.15.0/lib/libfido2.dylib does not contain expected string sk_api_version
provider /opt/homebrew/Cellar/libfido2/1.15.0/lib/libfido2.dylib is not an OpenSSH FIDO library
Key enrollment failed: invalid format

Next?

MAYBE TRY THIS: Yubico/libfido2#464 (comment) ⚠️ BUT it requires depending on a doxxed developer's wrapper around libfido2 which itself depends on another anon developer's package theseal/ssh-askpass image image

ssh-keygen Options Reference

To be provided with -O

From the man page:

FIDO AUTHENTICATOR
     ssh-keygen is able to generate FIDO authenticator-backed keys, after which they may be used much like any other key type supported by OpenSSH, so long as the hardware authenticator is attached when the keys
     are used.  FIDO authenticators generally require the user to explicitly authorise operations by touching or tapping them.  FIDO keys consist of two parts: a key handle part stored in the private key file on
     disk, and a per-device private key that is unique to each FIDO authenticator and that cannot be exported from the authenticator hardware.  These are combined by the hardware at authentication time to derive
     the real key that is used to sign authentication challenges.  Supported key types are ecdsa-sk and ed25519-sk.

     The options that are valid for FIDO keys are:

     application
             Override the default FIDO application/origin string of “ssh:”.  This may be useful when generating host or domain-specific resident keys.  The specified application string must begin with “ssh:”.

     challenge=path
             Specifies a path to a challenge string that will be passed to the FIDO authenticator during key generation.  The challenge string may be used as part of an out-of-band protocol for key enrollment (a
             random challenge is used by default).

     device  Explicitly specify a fido(4) device to use, rather than letting the authenticator middleware select one.

     no-touch-required
             Indicate that the generated private key should not require touch events (user presence) when making signatures.  Note that sshd(8) will refuse such signatures by default, unless overridden via an
             authorized_keys option.

     resident
             Indicate that the key handle should be stored on the FIDO authenticator itself.  This makes it easier to use the authenticator on multiple computers.  Resident keys may be supported on FIDO2
             authenticators and typically require that a PIN be set on the authenticator prior to generation.  Resident keys may be loaded off the authenticator using ssh-add(1).  Storing both parts of a key on
             a FIDO authenticator increases the likelihood of an attacker being able to use a stolen authenticator device.

     user    A username to be associated with a resident key, overriding the empty default username.  Specifying a username may be useful when generating multiple resident keys for the same application name.

     verify-required
             Indicate that this private key should require user verification for each signature.  Not all FIDO authenticators support this option.  Currently PIN authentication is the only supported verification
             method, but other methods may be supported in the future.

     write-attestation=path
             May be used at key generation time to record the attestation data returned from FIDO authenticators during key generation.  This information is potentially sensitive.  By default, this information
             is discarded.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment