On Fri, Jan 09, 2026 at 07:38:20PM -0700, Andy Bradford wrote:
> Hello,
> 
> After upgrading  to OpenBSD 7.8, the  Yubikey OTP "insert key  and press
> the  button" functionality  no longer  works  in OpenBSD  because it  no
> longer attaches as a keyboard:
> 
> https://cvsweb.openbsd.org/src/sys/dev/usb/ukbd.c?rev=1.91&ipk=jmoO719Mg1QImIC-UsGwegP2ZCM7IeAUQC-ZGChv3ic&content-type=text/x-cvsweb-markup
> 
> I don't see Yubico updating their tools anytime soon, so...
> 
> After  a  suggestion from  mischief  in  IRC,  I  realized I  could  use
> usbhidctl to  get the data upon  pressing the button. Oddly  enough, for
> some  reason on  my system  the data  seems to  be buffered  (when in  a
> pipeline)  and the  script doesn't  produce  output until  I unplug  the
> device, whereupon I  get an error message,  but at least I  also get the
> OTP.
> 
> After  finding your  uhid number  for the  Yubikey by  looking at  dmesg
> output, here's the  sed script I wrote to extract  the OTP upon pressing
> the button (and removal of device due to buffering I'm encountering):
 
You can also read from /dev/uhidN directly with perl and not worry about the
buffering.

https://gist.github.com/afresh1/1a04e771173d960a6507197e188b77df

#!/usr/bin/perl
use v5.36;
use builtin 'indexed';

# Copyright (c) 2026 Andrew Hewus Fresh <[email protected]>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

=head1 NAME

read-yubikey -- Read output from yubikey OTP on OpenBSD

=head1 DESCRIPTION

On OpenBSD Yubikeys no longer attach as keyboards,
but they do still attach as HID devices.
We can read codes from the device with a little work.

Running this script should find the appropriate uhid device
and loop over reading OTP messages from it.

=cut

# https://wiki.osdev.org/USB_Human_Interface_Devices#Report_format
# https://gist.github.com/mildsunrise/4e231346e2078f440969cdefb6d4caa3
my %MODIFIERS = reverse indexed qw<
    LEFT_CTRL  LEFT_SHIFT  LEFT_ALT  LEFT_GUI
    RIGHT_CTRL RIGHT_SHIFT RIGHT_ALT RIGHT_GUI
>;

sub decode( $mod, $code ) {
    return 'ENTER' if $code eq 0x28;

    if ( 0x04 <= $_ <= 0x27 ) {
        my $shift
            = $mod & ( $MODIFIERS{LEFT_SHIFT} | $MODIFIERS{RIGHT_SHIFT} );
        my $offset = $shift ? 0x41 : 0x61;
        return chr( $code + $offset - 0x04 );
    }

    return;
}


while (1) {
    my $uhid = do {
        my $u;
        my $uhidev;

        open my $fh, '-|', qw< /sbin/dmesg >
            or die "Unable to spawn dmesg $!";
        while (readline $fh) {
            if (/^(uhidev\d+) .*Yubikey/) {
                $u = undef;
                $uhidev = $1;
            }
            elsif ($u && /^\Q$u detached/) {
                $u = undef;
                $uhidev = undef;
            }
            elsif ($uhidev && /^(uhid\d+) at \Q$uhidev:/) {
                $u = $1;
            }
        }
        close $fh;

        $u;
    };

    # Could change this to a sleep wait.
    die "Yubikey uhid not in dmesg" unless $uhid;

    open my $fh, '<', "/dev/$uhid" or die "Unable to open /dev/$uhid: $!";
    my $buf = '';
    while ( read $fh, my $rec, 8 ) {
        my ( $mod, undef, @codes ) = unpack "C*", $rec;

        for (@codes) {
            next unless $_;
            my $key = decode( $mod, $_ );

            if ( $key eq "ENTER" ) {
                say $buf;
                $buf = '';
                next;
            }

            $buf .= $key;
        }
    }
    close $fh;
}

Reply via email to