François' Blog

YubiKey OTP Validation

Published on 2024-08-07

#software #php #archeology #yubikey #otp #security #mfa

Yes, I do realize it is 2024, and that we, well, at least some of us, have "State Of The Art" Multi Factor Authentication (MFA) using WebAuthn, using hardware tokens, or in some cases integrated directly in the OS with passkeys. That being said, let's figure out how YubiKey OTPs work, and whether or not they can still offer any value in 2024.

Note that this article is not an endorsement for using YubiKeys in OTP mode! It is mostly an adventure in software legacy and archaeology.

Petite France, Strasbourg, France

One of the reasons for looking into YubiKey OTPs in 2024 is that the "standards" involved with WebAuthn are so convoluted, that it is really no fun to implement any of that. A list of tragedies: 1, 2, 3, 4.

Of course, WebAuthn has one major (security) benefit compared to YubiKey OTPs (and TOTP) and that has to do with preventing phishing. WebAuthn "credentials" are bound, by the browser, to the domain name. Therefore a phishing site can't trick the user into using their MFA on a fake site, or if they can, it will be credentials specific to the phishing domain, which can't be used on the "real" site afterwards.

YubiKey OTPs are not the only 2FA method supported by YubiKeys, most of their (recent) keys also support WebAuthn, which does prevent phishing.

YubiKey OTP already exists for a long time, and I did implement support for them, together with TOTP, some years ago, as part of one of the projects I'm working on.

I still had two YubiKeys lying around, and they still work, so this should be easy to experiment with. YubiKey OTPs work by plugging in the YubiKey into a USB port and pressing the button. This generates a "One Time Password" (OTP) which is then sent to the computer. The YubiKey "emulates" a keyboard, so when the button on the YubiKey is pressed it simply "dumps" a bunch of characters, ending with an "enter", where the cursor is at that time. An OTP looks like this:

vvlvhrffevhctdtguvlgcbvclrnrhevnudlbdfgkigfk

This OTP consists of a "Public ID", here vvlvhrffevhc which are the first 6 bytes (encoded as "modhex", and thus 12 characters).

"modhex" is a way to encode data as "hex", but in such a way that for a few different keyboard layout settings the result is not mangled.

The last 16 bytes (32 "modhex" characters) are the encrypted OTP. The format is documented here.

Most services, if they still use YubiKey OTPs, will use the API service hosted by Yubico on https://api.yubico.com to verify the OTPs using the protocol described here.

There are a number of benefits to having a central service to verify the OTP tokens:

  1. Easy to implement: call an API to verify an OTP that returns OK, or REPLAYED_OTP. As a service implementing YubiKey OTP only the "Public ID" needs to be stored, bound to a user account;
  2. Allows using the same YubiKey with multiple services without needing to customize the token per service / organization boundary;
  3. Make sure the OTP comes from real hardware, not software emulation, I am not actually sure the Yubico API service does this, but it probably could, next project 😉;

There are also drawbacks:

  1. Requires the API to be online at all times (SPoF);
  2. Requires Yubico to remain interested in keeping the API online;
  3. A log can be kept of all OTP validations performed, creating a list of all services being used (and how often);
  4. Yubico must be trusted (they have a copy of the AES key);
  5. Amazon AWS (hosts the API service) needs to be trusted as well.

So, looking at this list of drawbacks, at the very least, it would be good to have the option for an organization using YubiKey OTPs to be able to "self host" a validation service if this becomes necessary, or perhaps already is, considering the drawbacks.

Yubico offers, by now, EOL software for doing this called yubikey-val and yubikey-ksm. A quick look reveals that this code is probably not in the best state, and most likely did not get (properly) audited. So, running that as-is in 2024 is not really an option.

As a first step, I wrote a small PHP script that is able to validate the OTP codes from your own YubiKey(s). For this to work, you do need to reprogram your YubiKey with a new AES key and Private ID which are then configured in the script to by able to decrypt and validate the OTP.

A tiny implementation for decoding YubiKey OTPs can be found here. It contains (inline) documentation on how to set things up, including programming your YubiKey using ykman which is packaged in Debian/Ubuntu and Fedora as yubikey-manager.

Perhaps in the future this implementation could be enhanced to become production ready, offer a (HTTP) API and allow for completely independent YubiKey OTP validation. By writing this script, and making it work, I'm now rather confident that, if ever required, it is relatively straightforward to implement a more or less secure YubiKey OTP validator. Famous last words! 🤣

As to whether or not anyone should depend on YubiKey OTPs in 2024, I am not sure. It is probably better than nothing, but one has to be (very) careful about phishing...

History