Skip to content

Instantly share code, notes, and snippets.

@githubaff0
Forked from BertanT/guide_macskeyinstaller.md
Last active February 2, 2025 14:45
Show Gist options
  • Save githubaff0/40f09b21570c5875a22adacf31739003 to your computer and use it in GitHub Desktop.
Save githubaff0/40f09b21570c5875a22adacf31739003 to your computer and use it in GitHub Desktop.
macOS OpenSSH Client Patcher for Hardware Security Key Support (ED25519-SK With YubiKey Etc.)

🔐 macOS OpenSSH Patcher for Hardware Security Keys

Supports ED25519-SK with Yubikey and other FIDO2 hardware security keys!

🤔 Discussion

Despite being compiled to support hardware security keys that take advantage of the FIDO2 protocol, the built-in OpenSSH client on macOS Sonoma and above lacks the middleware/library to support these devices. To keep using the built-in client - which is often the most stable and secure method for SSH connections - we need to compile the Security Key Provider from OpenSSH source and tell the macOS client about it ourselves.

This script does all of that for you on both Apple Silicon and Intel Mac computers!

The script installs openssl and libfido2 along with the required build tools from Homebrew. It then clones the latest main branch of OpenSSH Portable and builds from it the Security Key Provider library: sk-libfido2.dylib. It finally moves the built library to /usr/local/lib/, modifies ~/.zshenv to export the environment variable SSH_SK_PROVIDER=/usr/local/lib/sk-libfido2.dylib to tell the SSH client about it, and cleans everything up!

githubaff0 tweaks:

  • Prefer building latest tag rather than master if latest tag has commit for 'standalone sk-libfido2'. Unfortunately, the latest tag as of 2025-01-29 (V_9_9_P1) does not.
  • Create/install README-sk-libfido2.dylib.txt to track what version/commit was built.
  • Use make target 'sk-libfido2.dylib' instead of a full build.
  • Exit early if build failed.

📝 Instructions

Before getting started, make sure you:

  • are running macOS 14.0 Sonoma or higher. See the next section for solutions on older versions.
  • have Xcode Command Line Tools installed. You can check/install it by typing xcode-select --install in your Terminal.
  • have Homebrew installed. Click here for instructions.

Then, proceed with the installation:

  • Save macskeyinstaller.sh below to your Downloads folder. You can do this by right-clicking the Raw button and selecting Download Linked File.
  • Open a new Terminal and go to your Downloads folder by executing cd Downloads.
  • Run the script by executing bash macskeyinstaller.sh. Do not run with sudo.
  • Enter your login password when prompted as copying the library to /usr/local/lib/ requires root privileges.
  • When the script is done running, restart your Terminal for the changes to take effect.

Use your hardware security key to create a new ED25519-SK SSH Key:

There are great instructions on this on the Yubico website! Follow the instructions for Linux instead of macOS! Click here to see them.

📕Helpful Reading

These discussions and forum posts were what enabled and inspired me to create this script. I highly suggest checking them out for more technical details.

Original work Copyright 2025 Mehmet Bertan Tarakcioglu (github.com/BertanT)under the MIT License.
Modified work Copyright 2025 github.com/aff0 under the MIT License.
#!/bin/bash
################################################################################################
# macskeyinstaller.sh
# Last Updated: January 13, 2025
#
# macOS OpenSSH Client Patcher for Hardware Security Key Support (ED25519-SK With YubiKey Etc.)
# Check out https://gist.github.com/BertanT/9d222da115ca2d1274ef34735c4260cf for details!
#
# Original work Copyright 2025 Mehmet Bertan Tarakcioglu (github.com/BertanT), under the MIT License.
# Modified work Copyright 2025 github.com/aff0, under the MIT License.
################################################################################################
printf "\n* Hello! This script compiles and installs the security key provider for the built-in macOS Open SSH client to enable support for hardware security keys (such as a YubiKey)."
# Check if the script is running as root
if [ "$(id -u)" -eq 0 ]; then
printf "\n!!! Error: For safety reasons, this script cannot be run as root. If using sudo, please try again without it.\n" >&2
exit 1
fi
# Function to compare macOS version strings
version_ge() {
[[ "$(echo -e "$1\n$2" | sort -V | head -n1)" == "$2" ]]
}
# function to get the current macOS Version
macos_version=$(sw_vers -productVersion | cut -d '.' -f1,2)
# Check if running at least macOS Sonoma
if ! version_ge "$macos_version" "14.0"; then
printf "\n!!! Error: macOS version is $macos_version. This script requires at least macOS Sonoma (14.0).\n" >&2
exit 1
fi
printf "\n* macOS version is supported!"
# Check if Homebrew is installed
if ! which brew &>/dev/null; then
printf "\n!!! Error: This script requires Homebrew to install dependencies! Please install Homebrew and try again\n" >&2
exit 1
fi
printf "\n* Homebrew is installed!"
printf "\n* Cloning the latest main branch of openssh-portable from GitHub. This may take a while..."
git clone --quiet https://github.com/openssh/openssh-portable.git
cd openssh-portable
# Prefer building the latest tag instead of an arbitrary latest main branch commit
# AS LONG AS that tag includes the changes for '--with-security-key-standalone'.
# Unfortunately, the latest tag as of 2025-01-29 (V_9_9_P1) does not.
VERSION=$(git tag --contains 'HEAD^{/Add make target for standalone sk-libfido2}' $(git tag -l 'V_*' | sort -V | tail -1))
if [ -n "${VERSION}" ]; then
printf "\n* Checking out tag '${VERSION}'"
git checkout "${VERSION}" >/dev/null 2>&1
fi
# Document version we are building, even if its an arbitrary commit. Use --dirty
# in case the user messed with the script and working dir to make local changes.
commit_description=$(git describe --dirty)
printf "\n* Building version '${VERSION}'"
printf "\n* Creating README-sk-libfido2.dylib.txt"
cat >../README-sk-libfido2.dylib.txt <<-EOF
Name: sk-libfido2.dylib
Description: OpenSSH Portable - Standalone Security Key Provider library
Version: ${commit_description}
Build OS: $(uname -m -r -s -v)
Build Date: $(date -u -Iminutes)
URL: https://github.com/openssh/openssh-portable
URL: https://gist.github.com/BertanT/9d222da115ca2d1274ef34735c4260cf
URL: https://github.com/Yubico/libfido2/issues/464
URL: https://github.com/Yubico/libfido2/issues/464#issuecomment-1616718407
URL: https://github.com/Yubico/libfido2/issues/464#issuecomment-2588422088
EOF
printf "\n* Installing dependencies from Homebrew: libfido2, openssl, autoconf, automake, libtool, pkgconf\n"
brew install libfido2 openssl autoconf automake libtool pkgconf
printf "\n* Generating configuration script."
autoreconf -i
printf "\n* Exporting flags."
# Determine the CPU architecture and set the appropriate Homebrew base path
if [[ "$(uname -m)" == "arm64" ]]; then
BREW_PREFIX="/opt/homebrew"
elif [[ "$(uname -m)" == "x86_64" ]]; then
BREW_PREFIX="/usr/local"
else
echo "!!! Error: Unknown CPU architecture $(uname -m).\n" >&2
exit 1
fi
# Get the appropriate paths for the Homebrew dependecies as they differ through version
BREW_OPENSSL_PATH=$(ls -d $BREW_PREFIX/Cellar/openssl@3/*)
BREW_LIBFIDO2_PATH=$(ls -d $BREW_PREFIX/Cellar/libfido2/*)
export CFLAGS="-L$BREW_OPENSSL_PATH/lib -I$BREW_OPENSSL_PATH/include -L$BREW_LIBFIDO2_PATH/lib -I$BREW_LIBFIDO2_PATH/include -Wno-error=implicit-function-declaration"
export LDFLAGS="-L$BREW_OPENSSL_PATH/lib -L$BREW_LIBFIDO2_PATH/lib"
printf "\n* Configuring build. This may take a while..."
./configure --quiet --with-security-key-standalone
printf "\n* Cleaning previous build for good measure."
make --quiet clean
printf "\n* Building OpenSSH Portable - Standalone Security Key Provider library."
make --quiet sk-libfido2.dylib
test -f 'sk-libfido2.dylib' || {
printf "\n* ERROR: Build failed - build manually without --quiet to investigate.\n\n"
exit 1
}
printf "\n* Copying the Security Key Provider library to /usr/local/lib."
printf "\n We need root privileges to modify system files.\n"
sudo mkdir -p /usr/local/lib
sudo mv sk-libfido2.dylib ../README-sk-libfido2.dylib.txt /usr/local/lib/
# Check if the script is running in ZSH. If not, give the user instructions.
# Otherwise, modify .zshenv to set the environment variable if not already present.
if [[ "$SHELL" == *zsh* ]]; then
printf "\n* Configuring the ~/.zshenv for the System SSH use the Security Key Provider we just built"
if ! grep -q "export SSH_SK_PROVIDER=/usr/local/lib/sk-libfido2.dylib" "$HOME/.zshenv" &>/dev/null; then
echo "export SSH_SK_PROVIDER=/usr/local/lib/sk-libfido2.dylib" >> "$HOME/.zshenv"
printf "\n* Added SSH_SK_PROVIDER to ~/.zshenv."
else
printf "\n* SSH_SK_PROVIDER is already configured in ~/.zshenv."
fi
else
printf "*\n Since you are not on ZSH, you will need to manually configure your shell profile to tell the System SSH client to use the Security Key Provider we just built."
printf "\n The shell profile should export the environment variable as follows:"
printf "\n export SSH_SK_PROVIDER=/usr/local/lib/sk-libfido2.dylib"
fi
printf "\n* Exiting the directory and deleting the repository we cloned. We don't need it anymore"
cd ..
rm -rf openssh-portable
printf "\n\n* That's it! After restarting your terminal session, you can plug in your hardware security key and test the installation using:"
printf "\n ssh-keygen -t ed25519-sk -O resident -O verify-required -C \"Your Comment\""
printf "\n\nHave a nice day! :)\n\n"
@githubaff0
Copy link
Author

githubaff0 commented Jan 29, 2025

My changes

$ diff ../BertanT-guide_macskeyinstaller.md/macskeyinstaller.sh macskeyinstaller.sh
10c10,11
< # Copyright 2025 Mehmet Bertan Tarakcioglu (github.com/BertanT), under the MIT License.
---
> # Original work Copyright 2025 Mehmet Bertan Tarakcioglu (github.com/BertanT), under the MIT License.
> # Modified work Copyright 2025 github.com/aff0, under the MIT License.
47c48,75
< printf "\n* Installing dependencies from Homebrew: libfido2, openssl, autoconf, automake, libtool\n"
---
> # Prefer building the latest tag instead of an arbitrary latest main branch commit
> # AS LONG AS that tag includes the changes for '--with-security-key-standalone'.
> # Unfortunately, the latest tag as of 2025-01-29 (V_9_9_P1) does not.
> VERSION=$(git tag --contains 'HEAD^{/Add make target for standalone sk-libfido2}' $(git tag -l 'V_*' | sort -V | tail -1))
> if [ -n "${VERSION}" ]; then
>   printf "\n* Checking out tag '${VERSION}'"
>   git checkout "${VERSION}" >/dev/null 2>&1
> fi
> 
> # Document version we are building, even if its an arbitrary commit. Use --dirty
> # in case the user messed with the script and working dir to make local changes.
> commit_description=$(git describe --dirty)
> printf "\n* Building version '${VERSION}'"
> printf "\n* Creating README-sk-libfido2.dylib.txt"
> cat >../README-sk-libfido2.dylib.txt <<-EOF
> 	Name: sk-libfido2.dylib
> 	Description: OpenSSH Portable - Standalone Security Key Provider library
> 	Version: ${commit_description}
> 	Build OS: $(uname -m -r -s -v)
> 	Build Date: $(date -u -Iminutes)
> 	URL: https://github.com/openssh/openssh-portable
> 	URL: https://gist.github.com/BertanT/9d222da115ca2d1274ef34735c4260cf
> 	URL: https://github.com/Yubico/libfido2/issues/464
> 	URL: https://github.com/Yubico/libfido2/issues/464#issuecomment-1616718407
> 	URL: https://github.com/Yubico/libfido2/issues/464#issuecomment-2588422088
> 	EOF
> 
> printf "\n* Installing dependencies from Homebrew: libfido2, openssl, autoconf, automake, libtool, pkgconf\n"
77,78c105,110
< printf "\n* Building OpenSSH Portable."
< make --quiet
---
> printf "\n* Building OpenSSH Portable - Standalone Security Key Provider library."
> make --quiet sk-libfido2.dylib
> test -f 'sk-libfido2.dylib' || {
>   printf "\n* ERROR: Build failed - build manually without --quiet to investigate.\n\n"
>   exit 1
> }
83c115
< sudo mv sk-libfido2.dylib /usr/local/lib/
---
> sudo mv sk-libfido2.dylib ../README-sk-libfido2.dylib.txt /usr/local/lib/

Sample README-sk-libfido2.dylib.txt

$ cat /usr/local/lib/README-sk-libfido2.dylib.txt 
Name: sk-libfido2.dylib
Description: OpenSSH Portable - Standalone Security Key Provider library
Version: V_9_7_P1-389-g826483d51
Build OS: Darwin 24.2.0 Darwin Kernel Version 24.2.0: Fri Dec  6 19:01:59 PST 2024; root:xnu-11215.61.5~2/RELEASE_ARM64_T6000 arm64
Build Date: 2025-01-29T16:04+00:00
URL: https://github.com/openssh/openssh-portable
URL: https://gist.github.com/BertanT/9d222da115ca2d1274ef34735c4260cf
URL: https://github.com/Yubico/libfido2/issues/464
URL: https://github.com/Yubico/libfido2/issues/464#issuecomment-1616718407
URL: https://github.com/Yubico/libfido2/issues/464#issuecomment-2588422088

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment