--- /dev/null
+/*****************************************************************************************
+** **
+** Y K D E F - Common Yubikey project header **
+** **
+** Date / Rev / Sign / Remark **
+** 06-06-03 / 0.9.0 / J E / Main **
+** 06-08-25 / 1.0.0 / J E / Rewritten for final spec **
+** 08-06-03 / 1.3.0 / J E / Added static OTP feature **
+** **
+*****************************************************************************************/
+
+#ifndef __YKDEF_H_INCLUDED__
+#define __YKDEF_H_INCLUDED__
+
+// Slot entries
+
+#define SLOT_CONFIG 1
+#define SLOT_NAV 2
+
+#define SLOT_DATA_SIZE 64
+
+// Ticket structure
+
+#define UID_SIZE 6 // Size of secret ID field
+
+typedef struct {
+ unsigned char uid[UID_SIZE]; // Unique (secret) ID
+ unsigned short useCtr; // Use counter (incremented by 1 at first use after power up) + usage flag in msb
+ unsigned short tstpl; // Timestamp incremented by approx 8Hz (low part)
+ unsigned char tstph; // Timestamp (high part)
+ unsigned char sessionCtr; // Number of times used within session. 0 for first use. After it wraps from 0xff to 1
+ unsigned short rnd; // Pseudo-random value
+ unsigned short crc; // CRC16 value of all fields
+} TICKET;
+
+// Activation modifier of sessionUse field (bitfields not uses as they are not portable)
+
+#define TICKET_ACT_HIDRPT 0x8000 // Ticket generated at activation by keyboard (scroll/num/caps)
+#define TICKET_CTR_MASK 0x7fff // Mask for useCtr value (except HID flag)
+
+// Configuration structure
+
+#define FIXED_SIZE 16 // Max size of fixed field
+#define KEY_SIZE 16 // Size of AES key
+#define ACC_CODE_SIZE 6 // Size of access code to re-program device
+
+typedef struct {
+ unsigned char fixed[FIXED_SIZE];// Fixed data in binary format
+ unsigned char uid[UID_SIZE]; // Fixed UID part of ticket
+ unsigned char key[KEY_SIZE]; // AES key
+ unsigned char accCode[ACC_CODE_SIZE]; // Access code to re-program device
+ unsigned char fixedSize; // Number of bytes in fixed field (0 if not used)
+ unsigned char pgmSeq; // Program sequence number (ignored at programming - updated by firmware)
+ unsigned char tktFlags; // Ticket configuration flags
+ unsigned char cfgFlags; // General configuration flags
+ unsigned short ctrOffs; // Counter offset value (ignored at programming - updated by firmware)
+ unsigned short crc; // CRC16 value of all fields
+} CONFIG;
+
+// Ticket flags
+
+#define TKTFLAG_TAB_FIRST 0x01 // Send TAB before first part
+#define TKTFLAG_APPEND_TAB1 0x02 // Send TAB after first part
+#define TKTFLAG_APPEND_TAB2 0x04 // Send TAB after second part
+#define TKTFLAG_APPEND_DELAY1 0x08 // Add 0.5s delay after first part
+#define TKTFLAG_APPEND_DELAY2 0x10 // Add 0.5s delay after second part
+#define TKTFLAG_APPEND_CR 0x20 // Append CR as final character
+
+// Configuration flags
+
+#define CFGFLAG_SEND_REF 0x01 // Send reference string (0..F) before data
+#define CFGFLAG_TICKET_FIRST 0x02 // Send ticket first (default is fixed part)
+#define CFGFLAG_PACING_10MS 0x04 // Add 10ms intra-key pacing
+#define CFGFLAG_PACING_20MS 0x08 // Add 20ms intra-key pacing
+#define CFGFLAG_ALLOW_HIDTRIG 0x10 // Allow trigger through HID/keyboard
+#define CFGFLAG_STATIC_TICKET 0x20 // Static ticket generation
+
+// Navigation
+
+#define MAX_URL 48
+
+typedef struct {
+ unsigned char scancode[MAX_URL];// Scancode (lower 7 bits)
+ unsigned char scanmod[MAX_URL >> 2]; // Modifier fields (packed 2 bits each)
+ unsigned char flags; // NAVFLAG_xxx flags
+ unsigned char filler; // Filler byte
+ unsigned short crc; // CRC16 value of all fields
+} NAV;
+
+#define SCANMOD_SHIFT 0x80 // Highest bit in scancode
+#define SCANMOD_ALT_GR 0x01 // Lowest bit in mod
+#define SCANMOD_WIN 0x02 // WIN key
+
+// Navigation flags
+
+#define NAVFLAG_INSERT_TRIG 0x01 // Automatic trigger when device is inserted
+#define NAVFLAG_APPEND_TKT 0x02 // Append ticket to URL
+#define NAVFLAG_DUAL_KEY_USAGE 0x04 // Dual usage of key: Short = ticket Long = Navigate
+
+// Status block
+
+typedef struct {
+ unsigned char versionMajor; // Firmware version information
+ unsigned char versionMinor;
+ unsigned char versionBuild;
+ unsigned char pgmSeq; // Programming sequence number. 0 if no valid configuration
+ unsigned short touchLevel; // Level from touch detector
+} STATUS;
+
+// Modified hex string mapping
+
+#define MODHEX_MAP "cbdefghijklnrtuv"
+
+#endif // __YKDEF_H_INCLUDED__
--- /dev/null
+/*************************************************************************\r
+** **\r
+** Y K U T I L - Yubikey utilities **\r
+** **\r
+** Copyright 2008 Yubico AB **\r
+** **\r
+** Date / Sig / Rev / History **\r
+** 2008-06-05 / J E / 0.00 / Main **\r
+** **\r
+*************************************************************************/\r
+\r
+#include "ykutil.h"\r
+#include "yubikey.h"\r
+#include <string.h>\r
+#include <ctype.h>\r
+#include <aes128.h>\r
+\r
+/*************************************************************************\r
+** function getCRC **\r
+** Calculate ISO13239 checksum of buffer **\r
+** **\r
+** unsigned short getCRC(const unsigned char *buf, int bcnt) **\r
+** **\r
+** Where: **\r
+** "buf" is pointer to buffer **\r
+** "bcnt" is size of the buffer **\r
+** **\r
+** Returns: ISO13239 checksum **\r
+** **\r
+*************************************************************************/\r
+\r
+unsigned short getCRC(const unsigned char *buf, int bcnt)\r
+{\r
+ unsigned short crc = 0xffff;\r
+ int i;\r
+\r
+ while (bcnt--) {\r
+ crc ^= *buf++;\r
+ for (i = 0; i < 8; i++) crc = (crc & 1) ? ((crc >> 1) ^ 0x8408) : (crc >> 1);\r
+ }\r
+\r
+ return crc;\r
+}\r
+\r
+/*************************************************************************\r
+** function modhexDecode **\r
+** Decodes modhex string into binary **\r
+** **\r
+** int modhexDecode(unsigned char *dst, const unsigned char *src, **\r
+** int dstSize) **\r
+** **\r
+** Where: **\r
+** "dst" is pointer to decoded binary data **\r
+** "src" is pointer to modhex string **\r
+** "dstSize" is size of the destination buffer **\r
+** **\r
+** Returns: Number of bytes decoded **\r
+** **\r
+*************************************************************************/\r
+\r
+int modhexDecode(unsigned char *dst, const unsigned char *src, int dstSize)\r
+{\r
+ static const char trans[] = MODHEX_MAP;\r
+ unsigned char b, flag = 0;\r
+ int bcnt;\r
+ char *p1;\r
+\r
+ for (bcnt = 0; *src && (bcnt < dstSize); src++) {\r
+ if (p1 = strchr(trans, tolower(*src)))\r
+ b = (unsigned char) (p1 - trans);\r
+ else\r
+ b = 0;\r
+\r
+ if (flag = !flag) \r
+ *dst = b;\r
+ else {\r
+ *dst = (*dst << 4) | b;\r
+ dst++;\r
+ bcnt++;\r
+ }\r
+ }\r
+\r
+ return bcnt;\r
+}\r
+\r
+/*************************************************************************\r
+** function parseOTP **\r
+** Parses OTP string and inserts result in TICKET structure **\r
+** **\r
+** int parseOTP(TICKET *tkt, unsigned char *fixed, **\r
+** int *fixedSize, const char *str, **\r
+** const char *key) **\r
+** **\r
+** Where: **\r
+** "tkt" is pointer to receiving TICKET structure **\r
+** "fixed" is pointer to receiving fixed part buffer **\r
+** "fixedSize" is pointer to receiving size of fixed part **\r
+** "str" is pointer to ascii OTP string **\r
+** "key" is pointer to AES key **\r
+** **\r
+** Returns: Nonzero if successful, zero otherwise **\r
+** **\r
+*************************************************************************/\r
+\r
+int parseOTP(TICKET *tkt, unsigned char *fixed, int *fixedSize, const char *str, const unsigned char *key)\r
+{\r
+ int i, j;\r
+ unsigned char bin[FIXED_SIZE + sizeof(TICKET)];\r
+\r
+ // Convert from modhex to binary. Must be at least sizeof(TICKET) bytes\r
+\r
+ if ((i = modhexDecode(bin, str, sizeof(bin))) < sizeof(TICKET)) return 0;\r
+\r
+ // The ticket is located in the last 16 bytes\r
+\r
+ memcpy(tkt, bin + i - sizeof(TICKET), sizeof(TICKET));\r
+\r
+ // Decrypt the stuff\r
+\r
+ aesDecrypt((unsigned char *) tkt, key);\r
+\r
+ // Is the checksum okay ?\r
+\r
+ j = getCRC((unsigned char *) tkt, sizeof(TICKET));\r
+ ENDIAN_SWAP(j);\r
+ if (j != CRC_OK_RESIDUE) return 0;\r
+\r
+ // Shape up little-endian fields (if applicable)\r
+\r
+ ENDIAN_SWAP(tkt->rnd);\r
+ ENDIAN_SWAP(tkt->tstpl);\r
+ ENDIAN_SWAP(tkt->useCtr);\r
+\r
+ // Insert fixed id (if present)\r
+\r
+ *fixedSize = i - sizeof(TICKET);\r
+ \r
+ if (*fixedSize) memcpy(fixed, bin, *fixedSize);\r
+\r
+ return 1;\r
+}\r
--- /dev/null
+/*************************************************************************
+** **
+** Y U B I K E Y - Basic LibUSB programming API for the Yubikey **
+** **
+** Copyright 2008 Yubico AB **
+** **
+** Date / Sig / Rev / History **
+** 2008-06-05 / J E / 0.00 / Main **
+** **
+*************************************************************************/
+
+#include <usb.h> // Rename to avoid clash with windows USBxxx headers
+#include "yubikey.h"
+#include <ykutil.h>
+
+#define YUBICO_VID 0x1050
+#define YUBIKEY_PID 0x0010
+
+#define HID_GET_REPORT 0x01
+#define HID_SET_REPORT 0x09
+
+#define FEATURE_RPT_SIZE 8
+
+#define REPORT_TYPE_FEATURE 0x03
+
+/*************************************************************************
+** function hidSetReport **
+** Set HID report **
+** **
+** int hidSetReport(YUBIKEY yk, int reportType, int reportNumber, **
+** char *buffer, int size) **
+** **
+** Where: **
+** "yk" is handle to open Yubikey **
+** "reportType" is HID report type (in, out or feature) **
+** "reportNumber" is report identifier **
+** "buffer" is pointer to in buffer **
+** "size" is size of the buffer **
+** **
+** Returns: Nonzero if successful, zero otherwise **
+** **
+*************************************************************************/
+
+static int hidSetReport(YUBIKEY yk, int reportType, int reportNumber, char *buffer, int size)
+{
+ return usb_control_msg(yk, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_ENDPOINT_OUT, HID_SET_REPORT,
+ reportType << 8 | reportNumber, 0, buffer, size, 1000) > 0;
+}
+
+/*************************************************************************
+** function hidGetReport **
+** Get HID report **
+** **
+** int hidGetReport(YUBIKEY yk, int reportType, int reportNumber, **
+** char *buffer, int size) **
+** **
+** Where: **
+** "yk" is handle to open Yubikey **
+** "reportType" is HID report type (in, out or feature) **
+** "reportNumber" is report identifier **
+** "buffer" is pointer to in buffer **
+** "size" is size of the buffer **
+** **
+** Returns: Number of bytes read. Zero if failure **
+** **
+*************************************************************************/
+
+static int hidGetReport(YUBIKEY yk, int reportType, int reportNumber, char *buffer, int size)
+{
+ int m = usb_claim_interface(yk, 0);
+ printf ("m %d: %s\n", m, usb_strerror ());
+
+ return usb_control_msg(yk, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_ENDPOINT_IN, HID_GET_REPORT,
+ reportType << 8 | reportNumber, 0, buffer, size, 1000) > 0;
+}
+
+/*************************************************************************
+** function ykInit **
+** Initiates libUsb and other stuff. Call this one first **
+** **
+** void ykInit(void) **
+** **
+*************************************************************************/
+
+int ykInit(void)
+{
+ usb_init();
+
+ if (usb_find_busses()) return usb_find_devices();
+
+ return 0;
+}
+
+/*************************************************************************
+** function ykOpen **
+** Opens first Yubikey found for subsequent operations **
+** **
+** YUBIKEY ykOpen(void) **
+** **
+** Returns: Handle to opened Yubikey **
+** **
+*************************************************************************/
+
+YUBIKEY ykOpen(void)
+{
+ struct usb_bus *bus;
+ struct usb_device *dev;
+
+ // Find first instance of the Yubikey
+
+ for (bus = usb_get_busses(); bus; bus = bus->next)
+ for (dev = bus->devices; dev; dev = dev->next)
+ if (dev->descriptor.idVendor == YUBICO_VID && dev->descriptor.idProduct == YUBIKEY_PID) return usb_open(dev);
+
+ return (YUBIKEY) 0;
+}
+
+/*************************************************************************
+** function ykClose **
+** Closes open Yubikey handle **
+** **
+** void ykClose(void) **
+** **
+*************************************************************************/
+
+void ykClose(YUBIKEY *yk)
+{
+ usb_close((usb_dev_handle *) yk);
+}
+
+/*************************************************************************
+** function ykGetStatus **
+** Read the Yubikey status structure **
+** **
+** int ykGetStatus(YUBIKEY *yk, STATUS *status, int forceUpdate) **
+** **
+** Where: **
+** "yk" is handle to open Yubikey **
+** "status" is pointer to returned status structure **
+** "forceUpdate" is set to nonzero to force update of dynamic fields **
+** **
+** Returns: Nonzero if successful, zero otherwise **
+** **
+*************************************************************************/
+
+int ykGetStatus(YUBIKEY yk, STATUS *status, int forceUpdate)
+{
+ unsigned char buf[FEATURE_RPT_SIZE];
+
+ // Read status structure
+
+ memset(buf, 0, sizeof(buf));
+
+ if (!hidGetReport(yk, REPORT_TYPE_FEATURE, 0, buf, FEATURE_RPT_SIZE)) return 0;
+
+ memcpy(status, buf + 1, sizeof(STATUS));
+ ENDIAN_SWAP(status->touchLevel);
+
+ // If force update, force Yubikey to update its dynamic
+ // status value(s)
+
+ if (forceUpdate) {
+ memset(buf, 0, sizeof(buf));
+ buf[FEATURE_RPT_SIZE - 1] = 0x8a; // Invalid partition = update only
+ hidSetReport(yk, REPORT_TYPE_FEATURE, 0, buf, FEATURE_RPT_SIZE);
+ }
+
+ return 1;
+}
+
+/*************************************************************************
+** function ykWriteSlot **
+** Writes data to Yubikey slot **
+** **
+** static int ykWriteSlot(YUBIKEY *yk, unsigned char slot, **
+** const void *buf, int bcnt) **
+** **
+** Where: **
+** "yk" is handle to open Yubikey **
+** "slot" is slot number to write to **
+** "buf" is pointer to write data buffer **
+** "bcnt" is number of bytes to write **
+** **
+** Returns: Nonzero if successful, zero otherwise **
+** **
+*************************************************************************/
+
+static int ykWriteSlot(YUBIKEY *yk, unsigned char slot, const void *dt, int bcnt)
+{
+ unsigned char buf[FEATURE_RPT_SIZE], data[SLOT_DATA_SIZE + FEATURE_RPT_SIZE];
+ int i, j, pos, part;
+
+ // Insert data and set slot #
+
+ memset(data, 0, sizeof(data));
+ memcpy(data, dt, bcnt);
+ data[SLOT_DATA_SIZE] = slot;
+
+ // Append slot checksum
+
+ i = getCRC(data, SLOT_DATA_SIZE);
+ data[SLOT_DATA_SIZE + 1] = (unsigned char) (i & 0xff);
+ data[SLOT_DATA_SIZE + 2] = (unsigned char) (i >> 8);
+
+ // Chop up the data into parts that fits into the payload of a
+ // feature report. Set the part number | 0x80 in the end
+ // of the feature report. When the Yubikey has processed it,
+ // it will clear this byte, signaling that the next part can be sent
+
+ for (pos = 0, part = 0x80; pos < (SLOT_DATA_SIZE + 4); part++) {
+
+ // Ignore parts that are all zeroes except first and last
+ // to speed up the transfer
+
+ for (i = j = 0; i < (FEATURE_RPT_SIZE - 1); i++) if (buf[i] = data[pos++]) j = 1;
+ if (!j && (part > 0x80) && (pos < SLOT_DATA_SIZE)) continue;
+
+ buf[i] = part;
+
+ if (!hidSetReport(yk, REPORT_TYPE_FEATURE, 0, buf, FEATURE_RPT_SIZE)) return 0;
+
+ // When the last byte in the feature report is cleared by
+ // the Yubikey, the next part can be sent
+
+ for (i = 0; i < 50; i++) {
+ memset(buf, 0, sizeof(buf));
+ if (!hidGetReport(yk, REPORT_TYPE_FEATURE, 0, buf, FEATURE_RPT_SIZE)) return 0;
+ if (!buf[FEATURE_RPT_SIZE - 1]) break;
+ sleep(10);
+ }
+
+ // If timeout, something has gone wrong
+
+ if (i >= 50) return 0;
+ }
+
+ return 1;
+}
+
+/*************************************************************************
+** function ykWriteConfig **
+** Writes key config structure **
+** **
+** int ykGetStatus(YUBIKEY *yk, STATUS *status, unsigned char accCode) **
+** **
+** Where: **
+** "yk" is handle to open Yubikey **
+** "cfg" is pointer to configuration structure. NULL to zap **
+** "accCode" is current program access code. NULL if none **
+** **
+** Returns: Nonzero if successful, zero otherwise **
+** **
+*************************************************************************/
+
+int ykWriteConfig(YUBIKEY *yk, CONFIG *cfg, unsigned char *accCode)
+{
+ unsigned char buf[sizeof(CONFIG) + ACC_CODE_SIZE];
+ STATUS stat;
+ int seq;
+
+ // Get current seqence # from status block
+
+ if (!ykGetStatus(yk, &stat, 0)) return 0;
+
+ seq = stat.pgmSeq;
+
+ // Update checksum and insert config block in buffer if present
+
+ memset(buf, 0, sizeof(buf));
+
+ if (cfg) {
+ cfg->crc = ~getCRC((unsigned char *) cfg, sizeof(CONFIG) - sizeof(cfg->crc));
+ ENDIAN_SWAP(cfg->crc);
+ memcpy(buf, cfg, sizeof(CONFIG));
+ }
+
+ // Append current access code if present
+
+ if (accCode) memcpy(buf + sizeof(CONFIG), accCode, ACC_CODE_SIZE);
+
+ // Write to Yubikey
+
+ if (!ykWriteSlot(yk, SLOT_CONFIG, buf, sizeof(buf))) return 0;
+
+ // Verify update
+
+ if (!ykGetStatus(yk, &stat, 0)) return 0;
+
+ if (cfg) return stat.pgmSeq != seq;
+
+ return stat.pgmSeq == 0;
+}