Total Pageviews

Friday, 12 February 2016

YubiKey OTP Validation Server

The YubiKey Validation Server (YK-VAL) is a server that validates Yubikey One-Time Passwords (OTPs). YK-VAL is written in PHP, for use behind web servers such as Apache.


The server implements the Yubico API protocol as defined in doc/ValidationProtocol* and further documentation is also available in the doc/ subdirectory.
This server talks to a KSM service for decrypting the OTPs, to avoid storing any AES keys on the validation server. One implementation of this service is the YubiKey-KSM, and another implementation using the YubiHSM hardware is PyHSM.
Note that version 1.x is a minimal centralized server. Version 2.x is a replicated system that uses multiple machines.

Auth_Yubico PHP class

The Yubico authentication PHP class provides an easy way to integrate the Yubikey into your existing PHP-based user authentication infrastructure. Installation is simple using PEAR.
The development community is co-ordinated via the Github project.


Make sure your PHP installation supports curl, and that you have installed PEAR. On Debian/Ubuntu systems:
$ apt-get install libapache2-mod-php5 php5-curl php-pear
Install the component by invoking:
$ wget
$ pear install Auth_Yubico-2.5.tgz


Make sure that you have read Using a YubiKey OTP library before continuing.
This is an example of how you can use the Yubico.php inside an HTML *.php file:
 require_once 'Auth/Yubico.php';
 $otp = "ccbbddeertkrctjkkcglfndnlihhnvekchkcctif";

 # Generate a new id+key from
 $yubi = new Auth_Yubico('42', 'FOOBAR=');
 $auth = $yubi->verify($otp);
 if (PEAR::isError($auth)) {
    print "<p>Authentication failed: " . $auth->getMessage();
    print "<p>Debug output from server: " . $yubi->getLastResponse();
 } else {
    print "<p>You are authenticated!";

API demo

Provided in the package is also a script demo.php that demonstrates how you may use the package. The script is deployed here.

Example site

There is also a complete example site that demonstrates one-factor and two-factor mode. It requires a database to store the username and passwords. Database schema is in example/db.sql and configuration for the database needs to go into example/config.php.
The example site is deployed here.

Modhex Calculator

This package also includes Modhex_Calculator.php, a web page illustrating how to work with the modhex encoding. It is deployed here

Using the Yubikey for two-factor authentication on Linux

The Yubikey is a nice little device. It’s quite simple in design and operation. Yubikey
The key actually emulating a USB keyboard, which makes it instantly usable on any modern OS. You just press the button on the key to generate a one-time-password (OTP) to validate you. The method works by typing in your password, but before hitting the return key, you press the Yubikey button to finish it off. At the end of the OTP generation, it sends a carriage return itself.
The OTP is then sent to a validation server, either hosted by Yubico themselves, or you can host your own.
I’m going to walk through how you can set the infrastructre for doing two-factor authentication on Debian. In my specific case, the requirement was two-factor with an Active Directory username/password combination and the Yubikey as the second factor.
Unfortunately, the documentation from Yubico is quite average. To top it off, they insist on using multiple Google Code project sites for hosting their software.
This would normally be fine, but in this case, they have a Google Code project for every single little piece of code. Much of the documentation I found relates to older projects which are not supported by Yubico. This makes working out exactly what you need difficult. Within the Google Code project sites, documentation often runs in circles between projects.
In this document, I’ll look at using PAM to auth again the Yubico auth servers first. Once that’s working, I’ll move onto flashing the Yubikey with a new key and using our own Validation System.
NOTE: This is just some rough notes I put together. You should definitely read the Yubico documentation for this to really make sense.

Authenticating with the Yubikey with PAM

Get some dependencies
apt-get install libpam-dev libcurl4-openssl-dev libpam-radius-auth
Make ourselves a source directory
mkdir ~/yubikey; cd ~/yubikey
Get the current tarball of libyubikey, and install it
tar xf libyubikey-1.5.tar.gz
cd libyubikey-1.5
make check install
Get the current tarball of the Yubico C client, and install it
tar -xf ykclient-2.3.tar.gz
cd ykclient-2.3
make install
Get the current tarball of the Yubico PAM module, and install it
tar -xf pam_yubico-2.3.tar.gz
cd pam_yubico-2.3
make install
You should end up with your Yubico PAM module ‘/usr/local/lib/security/’
We’ll refer to this in our PAM config /etc/pam.d/openvpn
# /etc/pam.d/openvpn - OpenVPN pam configuiration
# We fall back to the system default in /etc/pam.d/common-*
auth required /usr/local/lib/security/ id=1 debug authfile=/etc/yubikeyid
auth required no_warn try_first_pass
@include common-account
@include common-password
@include common-session
This configuration will tell PAM to hit the Yubico module first. This splits apart your password field into your password and OTP. The OTP is validated against the Validation Servers, and the password is then passed onto the next module. This configuration will use the Yubico auth servers to check your token.
Once you have a working config, we’ll move to setting up our own Validation Servers. We’ll need to specify the URL for that in this config later on.
In that case, we’re also using RADIUS. This could be LDAP if you had an LDAP server available. You should be able to use the standard UNIX credentials (/etc/password, /etc/shadow) also.
The other important piece to note here is the authfile, /etc/yubikeyid
This file lists the mapping between username and the fixed part of your Yubikey. This is the first 12 chars of the Yubikey OTP (e.g. when you press the button)

FreeRADIUS authenticating against Active Directory 2008.

I banged my head against a wall for a while on this one. The trick is that you need at least FreeRADIUS 2.1.6 for AD authentication to work properly.
Add Debian backports to your /etc/apt/sources.list
deb lenny-backports main contrib non-free
Import the backports key
wget -O - | apt-key add -
Update and install the new freeradius
apt-get update
apt-get -t lenny-backports install freeradius freeradius-ldap
In your radiusd.conf
ldap {
    # Define the LDAP server and the base domain name
    server = ""
    basedn = "dc=ad, dc=yourcompany, dc=com"

    # Active Directory doesn't allow for Anonymous Binding
    identity = ""
    password = password

    password_attribute = "userPassword"
    filter = "(&(sAMAccountname=%{Stripped-User-Name:-%{User-Name}})(memberOf=CN=Users,DC=ad,DC=yourcompany,DC=com))"

    # This fixes Active Directory 2008 access
    chase_referrals = yes
    rebind = yes

    # The following are RADIUS defaults
    start_tls = no
    dictionary_mapping = ${raddbdir}/ldap.attrmap
    ldap_connections_number = 5
    timeout = 4
    timelimit = 3
    net_timeout = 1
In our FreeRADIUS client file /etc/freeradius/clients.conf:
client localhost {
    ipaddr =
    secret = testing123
    nastype = other
Use radtest to test our RADIUS is authenticating properly
radtest <username> <password> localhost 1 testing123
Should return Accept.
Set the address and shared secret of the radius server in /etc/pam_radius_auth.conf. The password of testing123 was defined in our RADIUS client config.
# server[:port] shared_secret   timeout (s)       testing123      1
OpenVPN has an issue with PAM loading the Yubikey module, so we have to LD_PRELOAD the pam module before starting OpenVPN.
export LD_PRELOAD=/lib/; openvpn --config openvpn.conf
For a permanent fix, at the end of the start_vpn function in /etc/init.d/openvpn, just before the $DAEMON line:
    export LD_PRELOAD=/lib/
    $DAEMON $OPTARGS --writepid /var/run/openvpn.$ \
        --config $CONFIG_DIR/$NAME.conf || STATUS=1
Change the path of /lib/ to suit your own system.
I won’t go into the OpenVPN configuration, except that for PAM authentication you need these options in your server config:
plugin /usr/lib/openvpn/ openvpn
ns-cert-type server

Personalising your Yubikey

To host your own Yubikey validation system, you require the secret AES key of your Yubikey. In the past, Yubico could provide this to you. Now, you’re required to flash your Yubikey yourself which will generate a new AES key.
Yubico provide a personalisation tool for Linux, Mac and Windows. If you’re on Windows, you get a nice little GUI. For Linux and Mac, you have a CLI based tool. It’s worth having a look at the ‘Personalization Tool’ page at:

Installing the Personalisation Tool

Install some dependencies:
apt-get install libusb-1.0.0-dev
Grab the latest Pesonalisation Tool tarball from:
cd ~/yubikey
Extract, build and install libyubikey
tar xf libyubikey-1.5.tar.gz
cd libyubikey-1.5
make install
You’ll need to provide a UID value for flashing your Yubikey. It needs to be 6 characters, and in hexadecimal. You can use this command to generate one for you.
dd if=/dev/urandom of=/dev/stdout count=100 2>/dev/null | xargs -0 modhex | cut -c 1-10 | awk '{print "vv" $1}'
You must provide the public name (fixed) parameter in modhex format. The modhex format is a special encoding used to ensure characters sent by the key are always correctly interpreted whatever keyboard layout you use.
You also need to generate yourself a public name for your key. This is known as the ‘fixed’ part, and it’ll be the first 16 chars when you generate your OTP. This will identify your key from anybody else’s.
dd if=/dev/urandom of=/dev/stdout count=100 2>/dev/null | xargs -0 modhex | cut -c 1-10 | awk '{print "vv" $1}'
This comamnd generate some random text, does a modhex operation, grabs the first 10 chars, then adds ‘vv’ to the front to make it up to 12.
You’ll be prompted for a passphrase on your AES key. I leave mine blank, but if you do set one, don’t ever lose it. I believe it’ll stop you from re-personalising your Yubikey.
ykpersonalize -ouid=74657374696e -ofixed=vvcnrdkvevtj
Firmware version 2.1.2 Touch level 1793 Program sequence 1
Passphrase to create AES key:
Configuration data to be written to key configuration 1:
fixed: m:vvcnrdkvevtj
uid: h:74657374696e
key: h:fcaad309a20ne1809c2db2f7f0e8d6ea
acc_code: h:000000000000
ticket_flags: APPEND_CR

Commit? (y/n) [n]: y
Save this information, as we’ll need it later.

Setting up yor own YubiKey OTP Validation Server

You need to install two things: The Key Storage Module and the Yubico Validation Server. The Key Storage Module (KSM) holds the secret AES key of your Yubikey token, while the Validation Server does the OTP check against the KSM.
In their 2.0 architecture, you can have multiple KSM’s and Validation servers with work together for reduncancy.

KSM Installation

Make a working directory, and get the KSM package
mkdir ~/yubikey && cd ~/yubikey
tar xfz yubikey-ksm-1.3.tgz
Install the KSM files
cd yubikey-ksm-1.3
make install

Install Apache2 and PHP

Install Apache2, PHP and MySQL
apt-get install apache2 php5 php5-mcrypt php5-curl mysql-server php5-mysql libdbd-mysql-perl
Create the ykksm table
echo "CREATE DATABASE ykksm;" | mysql -u root -p
Import the DB schema
mysql -u root -p ykksm < /usr/share/doc/ykksm/ykksm-db.sql
Set up some MySQL permissions
CREATE USER 'ykksmreader';
GRANT SELECT ON ykksm.yubikeys TO 'ykksmreader'@'localhost';
SET PASSWORD FOR 'ykksmreader'@'localhost' = PASSWORD('hYea3Inb');

CREATE USER 'ykksmimporter';
GRANT INSERT ON ykksm.yubikeys TO 'ykksmimporter'@'localhost';
SET PASSWORD FOR 'ykksmimporter'@'localhost' = PASSWORD('ikSab29');


Include path configuration

Set the include path by creating a file /etc/php5/conf.d/ykksm.ini
cat > /etc/php5/conf.d/ykksm.ini << EOF
include_path = "/etc/ykksm:/usr/share/ykksm"
Make a web server symlink
make -f /usr/share/doc/ykksm/ symlink
Set your configuration settings in /etc/ykksm/ykksm-config.php
  $db_dsn      = "mysql:dbname=ykksm;host=";
  $db_username = "ykksmreader";
  $db_password = "hYe63Inb";
  $db_options  = array();
  $logfacility = LOG_LOCAL0;
Restart Apache2
/etc/init.d/apache2 restart

Test the KSM Server

Try this URL:
curl 'http://localhost/wsapi/decrypt?otp=dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh'
ERR Unknown yubikey
It should return ‘Unknown Key’ until we have imported our Yubikey into the database.

Install the Yubico Validation Server

The latest version, and documentation can be found at:


Go to our working source directory, and grab the package
cd ~/yubikey
Extract, build and install the server
tar -zxf yubikey-val-2.4.tgz
cd yubikey-val-2.4
make install
Create the ykval database and import the schema
echo 'create database ykval' | mysql -u root -p
mysql -u root -p ykval < /usr/share/doc/ykval/ykval-db.sql
Install the symlink
make symlink
Include path configuration
cat > /etc/default/ykval-queue << EOF
Create a htaccess file: /var/www/wsapi/2.0/.htaccess
RewriteEngine on
RewriteRule ^([^/\.\?]+)(\?.*)?$ $1.php$2 [L]
php_value include_path ".:/etc/ykval:/usr/share/ykval"
Symlink the htaccess file
cd /var/www/wsapi; ln -s 2.0/.htaccess /var/www/wsapi/.htaccess
Copy the template config file for the Validation Server
cp /etc/ykval/ykval-config.php-template /etc/ykval/ykval-config.php
Edit the file and configure settings in /etc/ykval/ykval-config.php

  # For the validation interface.
  $baseParams = array ();
  $baseParams['__YKVAL_DB_DSN__'] = "mysql:dbname=ykval;host=";
  $baseParams['__YKVAL_DB_USER__'] = 'ykvalverifier';
  $baseParams['__YKVAL_DB_PW__'] = 'password';
  $baseParams['__YKVAL_DB_OPTIONS__'] = array();

  # For the validation server sync
  $baseParams['__YKVAL_SYNC_POOL__'] = array("http://localhost/wsapi/2.0/sync");

  # An array of IP addresses allowed to issue sync requests
  # NOTE: You must use IP addresses here.
  $baseParams['__YKVAL_ALLOWED_SYNC_POOL__'] = array("");

  # Specify how often the sync daemon awakens
  $baseParams['__YKVAL_SYNC_INTERVAL__'] = 10;

  # Specify how long the sync daemon will wait for response
  $baseParams['__YKVAL_SYNC_RESYNC_TIMEOUT__'] = 30;

  # Specify how old entries in the database should be considered aborted attempts
  $baseParams['__YKVAL_SYNC_OLD_LIMIT__'] = 10;

  # These are settings for the validation server.
  $baseParams['__YKVAL_SYNC_FAST_LEVEL__'] = 1;
  $baseParams['__YKVAL_SYNC_SECURE_LEVEL__'] = 40;
  $baseParams['__YKVAL_SYNC_DEFAULT_LEVEL__'] = 60;
  $baseParams['__YKVAL_SYNC_DEFAULT_TIMEOUT__'] = 1;

  // otp2ksmurls: Return array of YK-KSM URLs for decrypting OTP for
  // CLIENT.  The URLs must be fully qualified, i.e., contain the OTP
  // itself.
  function otp2ksmurls ($otp, $client) {
    return array("http://localhost/wsapi/decrypt?otp=$otp",);
In the above configuration, we’re only expecting to use one Validation Server and one KSM. If you’re planning on having multiple Validation servers and KSM’s, then you’ll be including the other Validation Servers in the SYNC_POOL, and your KSM’s in the URLs at the bottom, returned by the otp2ksmurls function.
Enable the mod_rewrite
a2enmod rewrite
Create the ykval database user
CREATE USER 'ykvalverifier'@'localhost' IDENTIFIED BY  'password';
GRANT ALL PRIVILEGES ON `ykval`. * TO  'ykvalverifier'@'localhost';
Fix some privileges on our config file
chgrp www-data /etc/ykval/ykval-config.php
The Sync Daemon uses the PEAR module System_Daemon so you need to install it:
apt-get install php-pear
pear install System_Daemon-0.9.2
Install the init.d script
ykval-queue install
update-rc.d -f ykval-queue defaults
Start the daemon
/etc/init.d/ykval-queue start


Use CURL to test our server is working
curl 'http://localhost/wsapi/verify?id=1&otp=vvcnrdkvevtefjbrjnlnldnhcujvddbikngjrtgh'
It should return something like this:
Once we import our Yubikey into the database, we should get a nice ‘status=OK’ message.

Importing your keys into the KSM server

Refer back to the output from personalising your Yubikey. You’ll need the fixed part (referred to as publicname in the DB), internal name (UID) and our AES key.
This is an entry for our newly personalised Yubikey.
USE ykksm;
INSERT INTO `yubikeys` (`serialnr`, `publicname`, `created`, `internalname`, `aeskey`, `lockcode`, `creator`, `active`, `hardware`)
VALUES (101209, 'vvcnrdkvevtj', '2010-05-07 15:18:40', '74657374696e', 'fcaad309a20ne1809c2db2f7f0e8d6ea', '000000000000', '', 1, 1);
This entry is required for our systems to authenticate against the Validation server. I’m not exactly sure about this, as the documentation is somewhat bare. I think you need an administrator-type person’s key details in here. The imporant part is the ID. This values corresponds the the ‘id=’ value in our CURL requests and in our PAM config.
USE ykval;
INSERT INTO `clients`
(`id`, `active`, `created`, `secret`, `email`, `notes`, `otp`)
(1, 1, 1, 'fcaad309a20ne1809c2db2f7f0e8d6ea', 'your@email.addr', 'Any text your want', 'vvcnrdkvevterfbtelvnvkkueenecrlfnlhdjetrhgnk');
We’ll hit our new Validation Server to make sure it’s working
curl "http://localhost/wsapi/2.0/verify?id=1&nonce=askjdnvajsndjkasndvjsnad&otp=vvcnrdkvevtjkreuvvlhtubjecbrticjneckgrigkck"
It should return something like this:
In this URL, we’ve added the ‘nonce’ parameter. This just a test to make sure the v2.0 API is working. ‘status=OK’ means it’s all good! If you get ‘NOT_ENOUGH_ANSWERS’, it means it has trouble trying to sync with other Validation Servers.
We’ll get PAM using our new Validation Servers for auth
auth required /usr/local/lib/security/ id=1 authfile=/etc/yubikeyid url= debug
If you watch /var/log/auth.log, you should see the PAM module spitting out some debugging information which may be useful. It also spits out your plain text password too, while you have the debug option on. Make sure you remove this later.


If you see an error like this:
PAM unable to dlopen(/lib/security/ /lib/security/ undefined symbol: pam_set_data
you’ll need the LD_PRELOAD trick from above. Something to do with dlopening the PAM module I believe。