YubiKey Manager and PowerShell: TOTP credentials

Title image of YubiKey and Powershell logos

All product names, logos, and brands used in this post are property of their respective owners.

In this quick post, I will highlight a process to generate an OATH-TOTP credential in a YubiKey using YubiKey Manager for Windows (specifically the ykman.exe CLI) and PowerShell. The TOTP credential appears in Yubico Authenticator when the YubiKey is inserted and can be used with services that require you to provide your own OATH-TOTP secret (Duo, Azure MFA / O365, etc.).

I will use Duo Security to test the generated credential (token), and as a bonus, I will demonstrate retrieving YubiKey TOTP codes via the command line using PowerShell and ykman.

To use this example, you must have YubiKey Manager downloaded and installed. I used the default install path in my example.

The PowerShell script

I will be the first to admit that the mathematics behind OATH-TOTP and base32 conversion is a little out of my wheelhouse. I have infinite respect for the engineers and developers who devised these!

Based on some excellent code by others (noted in the script below), I assembled the following PowerShell snippet which generates a TOTP secret (in hex, then converts to base32) and uses the secret to create an OATH-TOTP credential in the YubiKey. To test with Duo Security, the YubiKey’s serial number and the hex representation of the secret are output as CSV. These PowerShell commands can be modified slightly to create TOTP base32 secrets for bulk administrator import into Azure AD (O365 or Azure MFA).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# Create an alias for ykman pointing the install location
set-alias ykman "$env:programfiles\Yubico\YubiKey Manager\ykman.exe"

# Get the Yubikey's serial number with ykman
# Not required but will be used to import into Duo
# later (to test)
$serialNumber = ykman info | where { $_ -like "Serial number:*" } |
    %{ $_ -replace "Serial number: ",""}

# Generate a random 40 character hex secret
# See https://codegolf.stackexchange.com/questions/58442/generate-random-uuid
# Thanks Forty3! The length of the hex secret must be
# divisible by 5 to leverage HumanEquivalentUnit's byte to
# base32 conversion code below - I used a 40 digit secret
$hexSecret = (((40)|%{((1..$_)|%{('{0:X}' -f (random(16)))})}) -Join "").ToLower()

# Convert the hex secret key to base32 (with byte array
# as an intermediary). This seemed like the easier path
# vs. generating a base32 secret and converting back to hex

# First, from hex to bytes
# See https://gist.github.com/jonfriesen/234c7471c3e3199f97d5
# ( function Convert-HexToByteArray ) - thanks Jon Friesen
$byteSecret = $hexSecret -replace '^0x', '' -split "(?<=\G\w{2})(?=\w{2})" |
    %{ [Convert]::ToByte( $_, 16 ) }

# Then, from bytes to base32
# See https://humanequivalentunit.github.io/Base32-coding-the-scripting-way/
# Thanks humanequivalentunit
$byteArrayAsBinaryString = -join $byteSecret.ForEach{
    [Convert]::ToString($_, 2).PadLeft(8, '0')
}
$base32Secret = [regex]::Replace($byteArrayAsBinaryString, '.{5}', {
    param($Match)
    'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'[[Convert]::ToInt32($Match.Value, 2)]
}).ToLower()

# Create a new OATH-TOTP credential in the Yubikey for
# Yubico Authenticator
ykman oath add -o TOTP -d 6 -p 30 "DuoTest" "$base32Secret"

# Write out the TOTP token info in Duo CSV format
# Not required but will be used to import into Duo for
# testing
write-host "$serialNumber,$hexSecret"

# Clear variables
$serialNumber = ""
$hexSecret = ""
$byteSecret = ""
$base32Secret = ""

Once the script runs, the new credential should appear in Yubico Authenticator when the tokens refresh (or when you remove and re-insert the YubiKey):

Yubico Authenticator screenshot highlighting the newly generated OATH-TOTP credential

Be sure to take note of the comma-separated value output by the script - it will be imported into Duo in the next section:

> write-host "$serialNumber,$hexSecret"
6169235,c841ff5870e161da5d2f9e1af95962

Testing with Duo

I am a Duo fan and it provides a quick and frictionless way to test the token. Note that Duo does not support OATH-TOTP token drift or resync. This does not apply to YubiKey TOTP credentials though - that guidance applies to traditional hardware tokens (with their own clock). In the case of YubiKey, the “token” does not have its own clock. It simply securely stores the OATH-TOTP secret and relies on your computer or phone clock (via Yubico Authenticator) to generate the one-time passcode.

First, import the credential as a Hardware Token in the Duo Console (2FA Devices -> Hardware Tokens) as follows:

Screenshot of Hardware Token import screen in the Duo Console

Ensure TOTP 6-digit is selected and paste the CSV line from the PowerShell script.

Then, attach the token to a user or administrator (I used my Duo admin account for the test):

Screenshot of administrator settings in Duo and attaching a hardware token

Finally, logout and back into the Duo Console and provide the TOTP passcode in one of the following ways:

Yubico Authenticator Screenshot of copying and pasting TOTP code from Yubico Authenticator onto the Duo login page

PowerShell (command line)

> ykman oath code "DuoTest"
DuoTest 500575

If all went well, you should be authenticated to the Duo console.