[PATCH v1 3/3] yubikey authentication in login, ykpasswd command to manage the database
gp at high-consulting.de
gp at high-consulting.de
Thu Sep 14 06:36:25 PDT 2017
From: Gerd Pauli <gp at high-consulting.de>
Signed-off-by: Gerd Pauli <gp at high-consulting.de>
---
commands/Kconfig | 8 +
commands/Makefile | 1 +
commands/ykpasswd.c | 236 +++++++++
common/Makefile | 1 +
common/password.c | 29 +-
common/yubikey.c | 195 +++++++
include/yubikey.h | 9 +
include/yubikey/yubikey_common.h | 140 +++++
include/yubikey/yubikey_db.h | 101 ++++
include/yubikey/yubikey_util.h | 68 +++
lib/Kconfig | 4 +
lib/Makefile | 1 +
lib/yubikey/Makefile | 4 +
lib/yubikey/yubikey_db.c | 649 +++++++++++++++++++++++
lib/yubikey/yubikey_util.c | 1077 ++++++++++++++++++++++++++++++++++++++
15 files changed, 2521 insertions(+), 2 deletions(-)
create mode 100644 commands/ykpasswd.c
create mode 100644 common/yubikey.c
create mode 100644 include/yubikey.h
create mode 100644 include/yubikey/yubikey_common.h
create mode 100644 include/yubikey/yubikey_db.h
create mode 100644 include/yubikey/yubikey_util.h
create mode 100644 lib/yubikey/Makefile
create mode 100644 lib/yubikey/yubikey_db.c
create mode 100644 lib/yubikey/yubikey_util.c
diff --git a/commands/Kconfig b/commands/Kconfig
index 2e296a3..223661d 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -1375,6 +1375,14 @@ config CMD_MENUTREE
Options:
-m DIR directory where the menu starts (Default: /env/menu)
+config CMD_YKPASSWD
+ tristate
+ select YUBIKEY
+ depends on CMD_LOGIN
+ prompt "ykpasswd"
+ help
+ Set Yubikey Database
+
config CMD_PASSWD
tristate
depends on CMD_LOGIN
diff --git a/commands/Makefile b/commands/Makefile
index 42bc1d8..cf5c57e 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -126,3 +126,4 @@ obj-$(CONFIG_CMD_NAND_BITFLIP) += nand-bitflip.o
obj-$(CONFIG_CMD_SEED) += seed.o
obj-$(CONFIG_CMD_CCRYPT) += ccrypt.o
obj-$(CONFIG_CMD_KEYSTOREINIT) += keystore_init.o
+obj-$(CONFIG_CMD_YKPASSWD) += ykpasswd.o
diff --git a/commands/ykpasswd.c b/commands/ykpasswd.c
new file mode 100644
index 0000000..450484c
--- /dev/null
+++ b/commands/ykpasswd.c
@@ -0,0 +1,236 @@
+/*
+ * ykpasswd.c - Initializes the yubikey database
+ *
+ * Copyright (c) 2017 Gerd Pauli <gp at high-consulting.de>, HighConsulting GmbH & Co. KG
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <common.h>
+#include <command.h>
+#include <fs.h>
+#include <libfile.h>
+#include <malloc.h>
+#include <linux/stat.h>
+#include <linux/ctype.h>
+#include <environment.h>
+#include <globalvar.h>
+#include <password.h>
+#include <envfs.h>
+#include <yubikey/yubikey_common.h>
+#include <yubikey/yubikey_db.h>
+#include <yubikey/yubikey_util.h>
+
+/*
+ * #define DBG(x) do { printf x; } while (0)
+ * #define DEBUG
+ */
+
+#define DBG(x)
+#undef DEBUG
+
+
+static int do_ykpasswd(int argc, char *argv[])
+{
+ int ret = 1;
+ ykdb_entry entry;
+ ykdb_h *handle = NULL;
+ yk_ticket tkt;
+ const char user_text[] = "barebox";
+ char otp[201];
+ char key_text[33];
+ char *public_uid_text = NULL;
+ char *private_uid_text = NULL;
+ uint8_t ticket_enc_key[256];
+ uint8_t ticket_enc_hash[32];
+ uint8_t public_uid_bin[PUBLIC_UID_BYTE_SIZE];
+ uint8_t public_uid_bin_size = 0;
+ uint8_t private_uid_bin[PRIVATE_UID_BYTE_SIZE];
+ uint8_t private_uid_bin_size = 0;
+ int i;
+
+ memset(&entry,0,sizeof(ykdb_entry));
+ memset(&tkt,0,sizeof(yk_ticket));
+ memset(key_text,0,33);
+ memset(otp,0,201);
+ memset(ticket_enc_key,0,sizeof(ticket_enc_key));
+ memset(ticket_enc_hash,0,sizeof(ticket_enc_hash));
+ memset(public_uid_bin,0,sizeof(public_uid_bin));
+ memset(private_uid_bin,0,sizeof(private_uid_bin));
+
+ /* set default values for the entry */
+ entry.ticket.last_use = 0x0000;
+ entry.ticket.last_timestamp_lo = 0x0000;
+ entry.ticket.last_timestamp_hi = 0x00;
+ entry.ticket.last_session = 0x00;
+
+ /* set additional default values for the entry after parsing */
+ getSHA256(user_text, strlen(user_text), (uint8_t *)&entry.user_hash);
+
+ handle = ykdbDatabaseOpen(YKDB_FILE);
+ if (handle == NULL) {
+ handle = ykdbDatabaseCreate(YKDB_FILE);
+ if (handle == NULL) {
+ printf("Unable to access the database: %s [%d]\n",YKDB_FILE,ykdb_errno);
+ goto OUT;
+ }
+ }
+
+ /* can't add when one already exists */
+ if ( ykdbEntrySeekOnUserHash(handle, (uint8_t *)&entry.user_hash) == YKDB_SUCCESS ) {
+ printf("Entry already exist.\n");
+ } else {
+
+ DBG(("Adding Yubikey entry for %s\n", user_text));
+ puts("AES key [exactly 32 hex chars]: ");
+ password(key_text, 33, STAR, 0);
+
+ if (strlen(key_text) == 32) {
+ if ( !checkHexString(key_text) )
+ hexDecode((uint8_t *)&entry.ticket.key, key_text, KEY_BYTE_SIZE);
+ else {
+ printf("Invalid key specified!\n");
+ goto OUT;
+ }
+ } else {
+ printf("Invalid key specified!\n");
+ goto OUT;
+ }
+
+#ifdef DEBUG
+ printf("AES Key: ");
+ for ( i=0; i<KEY_BYTE_SIZE ; i++ )
+ printf("%02x ", entry.ticket.key[i]);
+ printf("\n");
+#endif
+ /* check for an OTP first which will provide public UID and private UID */
+ /* with a valid key */
+
+ puts("OTP [max 200 char]: ");
+ password(otp, 201, CLEAR, 0);
+
+ if (*otp != '\0') {
+ /* decode the OTP */
+ if ( parseOTP(&tkt, public_uid_bin, &public_uid_bin_size, otp, entry.ticket.key ) != 0 ) {
+ printf("Invalid OTP specified!\n");
+ goto OUT;
+ }
+
+ /* print public UID */
+ if (public_uid_bin_size > 0) {
+#ifdef DEBUG
+ printf("Using public UID: ");
+ for ( i=0; i<public_uid_bin_size; i++ )
+ printf("%02x ", public_uid_bin[i]);
+ printf("\n");
+#endif
+ } else {
+ printf("Invalid OTP specified!\n");
+ goto OUT;
+ }
+
+ /* save in entry */
+ getSHA256(public_uid_bin, public_uid_bin_size, (uint8_t *)&entry.public_uid_hash);
+
+ /* extract the private UID */
+ memcpy(private_uid_bin, tkt.private_uid, PRIVATE_UID_BYTE_SIZE);
+ /* print private UID */
+ private_uid_bin_size = PRIVATE_UID_BYTE_SIZE;
+#ifdef DEBUG
+ printf("Using private UID: ");
+ for ( i=0; i<PRIVATE_UID_BYTE_SIZE; i++ )
+ printf("%02x ", tkt.private_uid[i]);
+ printf("\n");
+#endif
+ /* save in entry */
+ getSHA256(private_uid_bin, PRIVATE_UID_BYTE_SIZE, (uint8_t *)&entry.ticket.private_uid_hash);
+
+ /* extract counter information, because we can */
+ entry.ticket.last_use = tkt.use_counter;
+ entry.ticket.last_timestamp_lo = tkt.timestamp_lo;
+ entry.ticket.last_timestamp_hi = tkt.timestamp_hi;
+ entry.ticket.last_session = tkt.session_counter;
+ } else {
+ printf("NO OTP specifies\n");
+ goto OUT;
+ }
+
+#ifdef DEBUG
+ ykdbPrintEntry(&entry);
+#endif
+
+ /* encrypt entry as required */
+ safeSnprintf(ticket_enc_key, 256, "TICKET_ENC_KEY_BEGIN");
+
+ /* add hex string format of public uid */
+ safeSnprintfAppend((char *)ticket_enc_key, 256, "|", public_uid_bin);
+ for(i=0; i<public_uid_bin_size; i++)
+ safeSnprintfAppend((char *)ticket_enc_key, 256, "%02x", public_uid_bin[i]);
+
+ safeSnprintfAppend(ticket_enc_key, 256, "|TICKET_ENC_KEY_END");
+
+#ifdef DEBUG
+ printf("Using entry encryption key: %s\n", ticket_enc_key);
+#endif
+
+ getSHA256(ticket_enc_key, strlen(ticket_enc_key), ticket_enc_hash);
+ aesEncryptCBC((uint8_t *)&entry.ticket, sizeof(ykdb_entry_ticket), ticket_enc_key, ticket_enc_key+16);
+
+ if ( ykdbEntryAdd(handle, &entry) != YKDB_SUCCESS ) {
+ printf("Unable to write to the database: %s [%d]\n", YKDB_FILE, ykdb_errno);
+ goto OUT;
+ }
+#ifdef DEBUG
+ ykdbPrintEntry(&entry);
+#endif
+ }
+
+ /* close the db */
+
+ DBG(("Completed successfully.\n"));
+
+ OUT:
+
+ if (handle) ykdbDatabaseClose(handle);
+ if (public_uid_text) free(public_uid_text);
+ if (private_uid_text) free(private_uid_text);
+
+ if ( ret == 0 ) {
+ DBG(("Saving Environment.\n"));
+ if ( envfs_save(NULL,NULL,0) ) {
+ printf("cannot save environment.\n");
+ ret = 1;
+ }
+#ifdef DEBUG
+ } else {
+ ykdbPrintEntry(&entry);
+#endif
+ }
+
+ return ret;
+}
+
+BAREBOX_CMD_HELP_START(ykpasswd)
+BAREBOX_CMD_HELP_TEXT("Interactively asks for the Yubikey Credentials to initialize")
+BAREBOX_CMD_HELP_TEXT("the yubikey Database stored in " YKDB_FILE ". Zis enables use of")
+BAREBOX_CMD_HELP_TEXT("yubikey OTP token as passwd")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(ykpasswd)
+ .cmd = do_ykpasswd,
+ BAREBOX_CMD_DESC("Edit the Yubikey DB")
+ BAREBOX_CMD_GROUP(CMD_GRP_CONSOLE)
+ BAREBOX_CMD_HELP(cmd_ykpasswd_help)
+BAREBOX_CMD_END
diff --git a/common/Makefile b/common/Makefile
index 8cd0ab3..88f29ac 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -62,6 +62,7 @@ obj-$(CONFIG_UBIFORMAT) += ubiformat.o
obj-$(CONFIG_BAREBOX_UPDATE_IMX_NAND_FCB) += imx-bbu-nand-fcb.o
obj-$(CONFIG_CONSOLE_RATP) += ratp.o
obj-$(CONFIG_BOOT) += boot.o
+obj-$(CONFIG_YUBIKEY) += yubikey.o
quiet_cmd_pwd_h = PWDH $@
ifdef CONFIG_PASSWORD
diff --git a/common/password.c b/common/password.c
index 74d328f..147607c 100644
--- a/common/password.c
+++ b/common/password.c
@@ -32,6 +32,7 @@
#include <globalvar.h>
#include <generated/passwd.h>
#include <crypto/pbkdf2.h>
+#include <yubikey.h>
#if defined(CONFIG_PASSWD_SUM_MD5)
#define PASSWD_SUM "md5"
@@ -131,6 +132,23 @@ static int is_passwd_env_enable(void)
return 1;
}
+static int is_passwd_yubikey_enable(void)
+{
+ int fd;
+ if (IS_ENABLED(CONFIG_YUBIKEY)) {
+ fd = open(PASSWD_FILE, O_RDONLY);
+
+ if (fd < 0)
+ return 0;
+
+ close(fd);
+
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
int passwd_env_disable(void)
{
return unlink(PASSWD_FILE);
@@ -282,6 +300,13 @@ static int check_passwd(unsigned char *passwd, size_t length)
int ret = 0;
int hash_len;
+ if (IS_ENABLED(CONFIG_YUBIKEY)) {
+ if ( yubikey_authenticate(passwd,length) ) {
+ ret = 1;
+ goto out;
+ }
+ }
+
if (IS_ENABLED(CONFIG_PASSWD_CRYPTO_PBKDF2)) {
hash_len = PBKDF2_LENGTH;
} else {
@@ -336,7 +361,7 @@ static int check_passwd(unsigned char *passwd, size_t length)
err:
free(passwd1_sum);
digest_free(d);
-
+out:
return ret;
}
@@ -415,7 +440,7 @@ void login(void)
unsigned char passwd[PASSWD_MAX_LENGTH];
int ret;
- if (!is_passwd_default_enable() && !is_passwd_env_enable())
+ if (!is_passwd_default_enable() && !is_passwd_env_enable() && !is_passwd_yubikey_enable())
return;
if (logged_in)
diff --git a/common/yubikey.c b/common/yubikey.c
new file mode 100644
index 0000000..ad70b07
--- /dev/null
+++ b/common/yubikey.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2017 Gerd Pauli <gp at high-consulting.de>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <common.h>
+#include <password.h>
+#include <errno.h>
+#include <readkey.h>
+#include <fs.h>
+#include <fcntl.h>
+#include <malloc.h>
+#include <xfuncs.h>
+#include <command.h>
+#include <magicvar.h>
+#include <clock.h>
+#include <init.h>
+#include <stdlib.h>
+#include <globalvar.h>
+#include <yubikey/yubikey_common.h>
+#include <yubikey/yubikey_db.h>
+#include <yubikey/yubikey_util.h>
+#include <envfs.h>
+
+#if defined(CONFIG_DEBUG_RSGW)
+#define DBG(x) do { printf x; } while (0)
+#else
+#define DBG(x)
+#endif
+
+#define MAXPASS 200
+
+int yubikey_authenticate ( unsigned char *otp, size_t length )
+{
+ int retval=0;
+
+ yk_ticket tkt;
+ ykdb_entry entry;
+ ykdb_h *handle;
+
+ uint8_t tkt_private_uid_hash[32];
+
+ uint8_t ticket_enc_key[256];
+ uint8_t ticket_enc_hash[32];
+
+ uint8_t public_uid_bin[PUBLIC_UID_BYTE_SIZE];
+ uint8_t public_uid_bin_size = 0;
+
+ uint32_t crc;
+ int delta_use;
+ int delta_session;
+ char user[]="barebox";
+ int i;
+
+ DBG(("got OTP: %s\n", otp));
+ if ( length > MAXPASS ) {
+ DBG(("OTP too long\n"));
+ goto OUT;
+ }
+
+ DBG(("User: %s\n",user));
+
+ /* set additional default values for the entry after parsing */
+ getSHA256((uint8_t *)user, strlen(user), (uint8_t *)&entry.user_hash);
+
+ /* perform initial parse to grab public UID */
+ parseOTP(&tkt, public_uid_bin, &public_uid_bin_size, (uint8_t *)otp, NULL);
+
+ DBG (("Parsing OTP\n"));
+ /* OTP needs the public UID for lookup */
+ if (public_uid_bin_size <= 0) {
+ DBG (("public_uid has no length, OTP is invalid\n"));
+ goto OUT;
+ }
+
+ /* set additional default values for the entry after parsing */
+ getSHA256(public_uid_bin, public_uid_bin_size, (uint8_t *)&entry.public_uid_hash);
+
+ /* open the db or create if empty */
+ handle = ykdbDatabaseOpen(YKDB_FILE);
+ if (handle == NULL) {
+ DBG (("couldn't access database: %s\n", YKDB_FILE));
+ goto OUT;
+ }
+
+ /* seek to public UID if it exists */
+ i = ykdbEntrySeekOnUserHash(handle, (uint8_t *)&entry.user_hash);
+ if ( i != YKDB_SUCCESS ) {
+ ykdbDatabaseClose(handle);
+ DBG (("no entry for user: %s [%d]\n", user,i));
+ goto OUT;
+ }
+ /* grab the entry */
+ i = ykdbEntryGet(handle, &entry);
+ if ( i != YKDB_SUCCESS ) {
+ ykdbDatabaseClose(handle);
+ DBG (("error getting entry [%d]\n",i));
+ }
+
+ /* start building decryption entry as required */
+ safeSnprintf((char *)ticket_enc_key, 256, "TICKET_ENC_KEY_BEGIN");
+
+ /* add hex string format of public uid */
+ safeSnprintfAppend((char *)ticket_enc_key, 256, "|", public_uid_bin);
+ for(i=0; i<public_uid_bin_size; i++)
+ safeSnprintfAppend((char *)ticket_enc_key, 256, "%02x", public_uid_bin[i]);
+
+ /* close off decryption key text and generate encryption hash */
+ safeSnprintfAppend((char *)ticket_enc_key, 256, "|TICKET_ENC_KEY_END");
+ getSHA256(ticket_enc_key, strlen((char *)ticket_enc_key), ticket_enc_hash);
+
+ /* decrypt if flags indicate so */
+ aesDecryptCBC((uint8_t *)&entry.ticket, sizeof(ykdb_entry_ticket), ticket_enc_key, ticket_enc_key+16);
+
+ /* perform real parse to grab real ticket, using the now unecrypted key */
+ parseOTP(&tkt, public_uid_bin, &public_uid_bin_size, (uint8_t *)otp, (uint8_t *)&entry.ticket.key);
+
+ DBG(("Ticket Enc: %s\n",ticket_enc_key));
+
+ crc = getCRC((uint8_t *)&tkt, sizeof(yk_ticket));
+ ENDIAN_SWAP_16(crc);
+
+ /* no use continuing if the decoded OTP failed */
+ if ( crc != CRC_OK_RESIDUE ) {
+ ykdbDatabaseClose(handle);
+ DBG (("crc invalid: 0x%04x", crc));
+ goto OUT;
+ }
+
+ getSHA256(tkt.private_uid, PRIVATE_UID_BYTE_SIZE, (uint8_t *)&tkt_private_uid_hash);
+
+ if ( memcmp(&tkt_private_uid_hash, &entry.ticket.private_uid_hash, 32) ) {
+ ykdbDatabaseClose(handle);
+ DBG (("private uid mismatch"));
+ goto OUT;
+ }
+
+ /* check counter deltas */
+ delta_use = tkt.use_counter - entry.ticket.last_use;
+ delta_session = tkt.session_counter - entry.ticket.last_session;
+
+ if ( delta_use < 0 ) {
+ ykdbDatabaseClose(handle);
+ DBG (("OTP is INVALID. Possible replay!!!"));
+ goto OUT;
+ }
+
+ if ( delta_use == 0 && delta_session <= 0 ) {
+ ykdbDatabaseClose(handle);
+ DBG (("OTP is INVALID. Possible replay!!!"));
+ goto OUT;
+ }
+
+ /* update the database entry with the latest counters */
+ entry.ticket.last_use = tkt.use_counter;
+ entry.ticket.last_timestamp_lo = tkt.timestamp_lo;
+ entry.ticket.last_timestamp_hi = tkt.timestamp_hi;
+ entry.ticket.last_session = tkt.session_counter;
+
+ /* re-encrypt and write to database */
+ aesEncryptCBC((uint8_t *)&entry.ticket, sizeof(ykdb_entry_ticket), ticket_enc_key, ticket_enc_key+16);
+
+ /* re-encrypt and write to database */
+ if ( ykdbEntryWrite(handle, &entry) != YKDB_SUCCESS ) {
+ ykdbDatabaseClose(handle);
+ DBG(("cannot write new database entry\n"));
+ goto OUT;
+ }
+
+ if ( envfs_save(NULL, NULL, 0) != 0 ) {
+ DBG(("cannot save environment\n"));
+ goto OUT;
+ }
+
+ /* auth ok */
+ retval=1;
+
+ OUT:
+ return retval;
+}
+
+EXPORT_SYMBOL(yubikey_authenticate);
+
diff --git a/include/yubikey.h b/include/yubikey.h
new file mode 100644
index 0000000..1b5b534
--- /dev/null
+++ b/include/yubikey.h
@@ -0,0 +1,9 @@
+/* * Copyright (c) 2017 Gerd Pauli HighConsulting GmbH & Co. KG
+ */
+
+#ifndef _YUBIKEY_H_
+#define _YUBIKEY_H_
+
+int yubikey_authenticate ( unsigned char *otp, size_t length );
+
+#endif
diff --git a/include/yubikey/yubikey_common.h b/include/yubikey/yubikey_common.h
new file mode 100644
index 0000000..4df743f
--- /dev/null
+++ b/include/yubikey/yubikey_common.h
@@ -0,0 +1,140 @@
+/*
+* YubiKey PAM Common API
+*
+* Copyright (C) 2008 Ian Firns <firnsy at securixlive.com>
+* Copyright (C) 2008 SecurixLive <dev at securixlive.com>
+* Copyright (C) 2017 HighConsulting <gp at high-consulting.de>
+*
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+* http://www.gnu.org/copyleft/gpl.html
+*/
+
+/*
+** Original Code adapted from YubiCo **
+*/
+
+/*****************************************************************************************
+**
+** 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 __YK_COMMON_H__
+#define __YK_COMMON_H__
+
+#define YKDB_FILE "/env/etc/yubikey"
+
+/* slot entries */
+#define SLOT_CONFIG 1
+#define SLOT_NAV 2
+#define SLOT_DATA_SIZE 64
+
+#include <types.h>
+#include <linux/ctype.h>
+
+// 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 PUBLIC_UID_BYTE_SIZE 16 /* max byte size of fixed public UID field */
+#define KEY_BYTE_SIZE 16 /* byte size of AES key */
+#define PRIVATE_UID_BYTE_SIZE 6 /* byte size of private UID field */
+#define ACCESS_CODE_BYTE_SIZE 6 /* max byte size of access code to re-program device */
+
+/* ticket structure */
+typedef struct _yk_ticket {
+ uint8_t private_uid[PRIVATE_UID_BYTE_SIZE];
+ uint16_t use_counter;
+ uint16_t timestamp_lo;
+ uint8_t timestamp_hi;
+ uint8_t session_counter;
+ uint16_t random;
+ uint16_t crc;
+} yk_ticket;
+
+typedef struct _yk_usb_config {
+ uint8_t public_uid[PUBLIC_UID_BYTE_SIZE];
+ uint8_t private_uid[PRIVATE_UID_BYTE_SIZE];
+ uint8_t key[KEY_BYTE_SIZE];
+ // Access code to re-program device
+ uint8_t accCode[ACCESS_CODE_BYTE_SIZE];
+ // Number of bytes in fixed field (0 if not used)
+ uint8_t public_uid_size; /* fixedSize */
+ // Program sequence number (ignored at programming - updated by firmware)
+ uint8_t pgmSeq;
+ // Ticket configuration flags
+ uint8_t tktFlags;
+ // General configuration flags
+ uint8_t cfgFlags;
+ // Counter offset value (ignored at programming - updated by firmware)
+ uint16_t ctrOffs;
+ // CRC16 value of all fields
+ uint16_t crc;
+} 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 _yk_usb_nav {
+ uint8_t scancode[MAX_URL]; // Scancode (lower 7 bits)
+ uint8_t scanmod[MAX_URL >> 2]; // Modifier fields (packed 2 bits each)
+ uint8_t flags; // NAVFLAG_xxx flags
+ uint8_t filler; // Filler byte
+ uint16_t 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 _yk_usb_status {
+ uint8_t versionMajor; // Firmware version information
+ uint8_t versionMinor;
+ uint8_t versionBuild;
+ uint8_t pgmSeq; // Programming sequence number. 0 if no valid configuration
+ uint16_t touchLevel; // Level from touch detector
+} STATUS;
+
+#endif /* __YK_COMMON_H__ */
+
diff --git a/include/yubikey/yubikey_db.h b/include/yubikey/yubikey_db.h
new file mode 100644
index 0000000..7042000
--- /dev/null
+++ b/include/yubikey/yubikey_db.h
@@ -0,0 +1,101 @@
+/*
+ * YubiKey DB API
+ *
+ * Copyright (C) 2008 SecurixLive dev at securixlive.com
+ * Copyright (C) 2008 Ian Firns firnsy at securixlive.com
+ * Copyright (C) 2017 Gerd Pauli gp at high-consulting.de
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef __YK_DB_H__
+#define __YK_DB_H__
+
+#define YKDB_MAGIC "YKDB"
+#define YKDB_MAGIC_SIZE 4
+#define YKDB_VERSION 42
+#define YKDB_KEY_BYTE_SIZE 16
+
+#define YKDB_SUCCESS 0
+#define YKDB_ERR_ARGS 1
+#define YKDB_ERR_IO 2
+#define YKDB_ERR_SEEK 3
+#define YKDB_ERR_LOCK 4
+#define YKDB_ERR_DB_INV 5
+#define YKDB_ERR_DB_EMPTY 6
+
+extern int ykdb_errno;
+
+/* data types */
+struct _ykdb_header {
+ uint8_t magic[YKDB_MAGIC_SIZE];
+ uint32_t version;
+ uint32_t entry_count;
+ uint8_t reserved[4];
+};
+typedef struct _ykdb_header ykdb_header;
+
+struct _ykdb_entry_ticket {
+ uint8_t key[YKDB_KEY_BYTE_SIZE];
+ uint8_t private_uid_hash[32];
+ uint16_t last_use;
+ uint8_t last_timestamp_hi;
+ uint16_t last_timestamp_lo;
+ uint8_t last_session;
+ uint8_t reserved[40];
+};
+typedef struct _ykdb_entry_ticket ykdb_entry_ticket;
+
+struct _ykdb_entry {
+ uint8_t user_hash[32];
+ uint8_t public_uid_hash[32];
+ uint32_t flags;
+ ykdb_entry_ticket ticket;
+};
+typedef struct _ykdb_entry ykdb_entry;
+
+struct _ykdb_handle {
+ int file_descriptor;
+ uint32_t cur_entry;
+ ykdb_header header;
+};
+typedef struct _ykdb_handle ykdb_h;
+
+
+/* database API */
+ykdb_h *ykdbDatabaseOpen(const char *);
+ykdb_h *ykdbDatabaseCreate(const char *);
+int ykdbDatabaseClose(ykdb_h *);
+
+uint32_t ykdbDatabaseEntryCountGet(ykdb_h *);
+
+int ykdbEntryNext(ykdb_h *);
+int ykdbEntryPrev(ykdb_h *);
+int ykdbEntryGet(ykdb_h *, ykdb_entry *);
+int ykdbEntrySeekOnIndex(ykdb_h *, uint32_t);
+int ykdbEntrySeekOnUserHash(ykdb_h *, uint8_t *);
+int ykdbEntrySeekOnPublicHash(ykdb_h *, uint8_t *);
+int ykdbEntryGetIndex(ykdb_h *, uint32_t *);
+int ykdbEntryAdd(ykdb_h *, ykdb_entry *);
+int ykdbEntryAdd2(ykdb_h *, uint8_t *, uint8_t *, uint8_t, ykdb_entry_ticket *);
+int ykdbEntryWrite(ykdb_h *, ykdb_entry *);
+int ykdHeaderWrite(ykdb_h *);
+int ykdbEntryDelete(ykdb_h *);
+int ykdbEntrySeekEmpty(ykdb_h *);
+
+void ykdbPrintEntry(ykdb_entry *entry);
+
+#endif /* __YK_DB_H__ */
diff --git a/include/yubikey/yubikey_util.h b/include/yubikey/yubikey_util.h
new file mode 100644
index 0000000..3d6ab5c
--- /dev/null
+++ b/include/yubikey/yubikey_util.h
@@ -0,0 +1,68 @@
+/*
+* YubiKey PAM Utils Module
+*
+* Copyright (C) 2008 SecurixLive dev at securixlive.com
+* Copyright (C) 2008 Ian Firns firnsy at securixlive.com
+* Copyright (C) 2017 Gerd Pauli gp at high-consulting.de
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+* http://www.gnu.org/copyleft/gpl.html
+*/
+
+#ifndef __YK_UTIL_H__
+#define __YK_UTIL_H__
+
+#include <yubikey/yubikey_common.h>
+
+#ifdef __BIG_ENDIAN__
+#define ENDIAN_SWAP_16(x) x = ((x) >> 8) | ((x) << 8)
+#else
+#define ENDIAN_SWAP_16(x)
+#endif
+
+#define SHA256_DIGEST_SIZE (8*sizeof(uint32_t))
+#define MODHEX_MAP "cbdefghijklnrtuv"
+#define HEX_MAP "0123456789abcdef"
+#define CRC_OK_RESIDUE 0xf0b8
+
+/* public API */
+int safeSnprintf(char *buf, size_t buf_size, const char *format, ...);
+int safeSnprintfAppend(char *buf, size_t buf_size, const char *format, ...);
+int safeStrnlen(const char *buf, int buf_size);
+
+int checkHexString(const uint8_t *);
+int checkModHexString(const uint8_t *);
+int checkOTPCompliance(const uint8_t *, uint32_t);
+
+/* cipher/ routines */
+void aesEncryptBlock(uint8_t *, const uint8_t *);
+void aesDecryptBlock(uint8_t *, const uint8_t *);
+void aesEncryptCBC(uint8_t *, uint32_t, const uint8_t *, const uint8_t *);
+void aesDecryptCBC(uint8_t *, uint32_t, const uint8_t *, const uint8_t *);
+void getSHA256(const uint8_t *, uint32_t, uint8_t *);
+uint16_t getCRC(const uint8_t *, uint32_t);
+
+/* yubikey routines */
+uint32_t hexDecode(uint8_t *, const uint8_t *, uint32_t);
+uint32_t modHexDecode(uint8_t *, const uint8_t *, uint32_t);
+uint32_t modHexEncode(uint8_t *, const uint8_t *, uint32_t);
+int parseOTP(yk_ticket *, uint8_t *, uint8_t *, const uint8_t *, const uint8_t *);
+
+void printTicket(yk_ticket *);
+
+
+
+
+#endif
diff --git a/lib/Kconfig b/lib/Kconfig
index c0694ca..137d6f5 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -85,6 +85,10 @@ config STMP_DEVICE
config CCRYPTLIB
bool
+config YUBIKEY
+ bool "Hadle yubikey OTP. Enables Keystore"
+ select CRYPTO_KEYSTORE
+
config RATP
select CRC16
bool
diff --git a/lib/Makefile b/lib/Makefile
index d32e102..edef2d5 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -60,3 +60,4 @@ obj-$(CONFIG_RATP) += ratp.o
obj-y += list_sort.o
obj-y += int_sqrt.o
obj-$(CONFIG_CCRYPTLIB) += ccryptlib/
+obj-$(CONFIG_YUBIKEY) += yubikey/
diff --git a/lib/yubikey/Makefile b/lib/yubikey/Makefile
new file mode 100644
index 0000000..8958463
--- /dev/null
+++ b/lib/yubikey/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_YUBIKEY) += yubikey_db.o
+obj-$(CONFIG_YUBIKEY) += yubikey_util.o
+
+
diff --git a/lib/yubikey/yubikey_db.c b/lib/yubikey/yubikey_db.c
new file mode 100644
index 0000000..bcb2961
--- /dev/null
+++ b/lib/yubikey/yubikey_db.c
@@ -0,0 +1,649 @@
+/*
+ * YubiKey DB API
+ *
+ * Copyright (C) 2008 SecurixLive dev at securixlive.com
+ * Copyright (C) 2008 Ian Firns firnsy at securixlive.com
+ * Copyright (C) 2017 Gerd Pauli gp at high-consulting.de
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <common.h>
+#include <xfuncs.h>
+#include <malloc.h>
+#include <errno.h>
+#include <libfile.h>
+#include <fs.h>
+#include <fcntl.h>
+#include <progress.h>
+#include <linux/stat.h>
+
+#include <yubikey/yubikey_db.h>
+
+#if defined(CONFIG_DEBUG_RSGW)
+#define DBG(x) do { printf x; } while (0)
+#else
+#define DBG(x)
+#endif
+
+#define YKDB_ERROR(code) ykdb_errno=code
+#define YKDB_ERROR_RET(code) ykdb_errno=code; return code;
+
+int ykdb_errno = 0;
+
+/* private DB functions */
+
+int ykdbHeaderWrite(ykdb_h *handle) {
+ off_t old_pos;
+
+ /* check arguments sanity */
+ if (handle == NULL) {
+ YKDB_ERROR_RET(YKDB_ERR_ARGS);
+ }
+
+ old_pos = lseek(handle->file_descriptor, 0, SEEK_CUR);
+
+ /* seek to database header (ie. start of file) */
+ if ( lseek(handle->file_descriptor, 0, SEEK_SET) == -1 ) {
+ YKDB_ERROR_RET(YKDB_ERR_IO);
+ }
+
+ /* write header to disk */
+ if ( write(handle->file_descriptor, &handle->header, sizeof(ykdb_header)) != sizeof(ykdb_header) ) {
+ YKDB_ERROR_RET(YKDB_ERR_IO);
+ }
+
+ /* return to old position */
+ if ( lseek(handle->file_descriptor, old_pos, SEEK_SET) == -1 ) {
+ YKDB_ERROR_RET(YKDB_ERR_IO);
+ }
+
+ DBG(("ykdbHeaderWrite aktpos: %lu\n",old_pos));
+
+ return YKDB_SUCCESS;
+}
+
+
+/* public API implementation */
+ykdb_h *ykdbDatabaseOpen(const char *path) {
+ ykdb_h *handle;
+#if defined(CONFIG_DEBUG_RSGW)
+ uint8_t *buf;
+ int i;
+#endif
+ /* check argument sanity */
+ if (path == NULL) {
+ YKDB_ERROR(YKDB_ERR_ARGS);
+ return NULL;
+ }
+
+ /* allocate the db handle */
+ handle = (ykdb_h *)malloc(sizeof(ykdb_h));
+ memset(handle,0,sizeof(ykdb_h));
+
+ if (handle == NULL) {
+ return NULL;
+ }
+
+ /* open the db */
+ handle->file_descriptor = open(path, O_RDWR);
+ if (handle->file_descriptor == -1) {
+ free(handle);
+
+ YKDB_ERROR(YKDB_ERR_IO);
+ return NULL;
+ }
+
+ /* read header */
+ if ( read(handle->file_descriptor, &handle->header, sizeof(ykdb_header)) != sizeof (ykdb_header) ) {
+ close(handle->file_descriptor);
+ free(handle);
+
+ YKDB_ERROR(YKDB_ERR_IO);
+ return NULL;
+ }
+
+ /* check magic and version compatibility */
+ if ( memcmp(&handle->header.magic, YKDB_MAGIC, YKDB_MAGIC_SIZE) != 0 ||
+ handle->header.version != YKDB_VERSION ) {
+ close(handle->file_descriptor);
+ free(handle);
+
+ YKDB_ERROR(YKDB_ERR_DB_INV);
+ return NULL;
+ }
+
+ DBG(("ykdbDatabaseOpen:\n"));
+ DBG((" header length + aktpos: %lu\n",sizeof (ykdb_header)));
+ DBG((" ykdb fd: %d\n",handle->file_descriptor));
+ DBG((" ykdb cur_entry: %u\n",handle->cur_entry));
+ DBG((" handle %p cur_entry %p\n",handle,&handle->cur_entry));
+#if defined(CONFIG_DEBUG_RSGW)
+ buf=(uint8_t *)handle;
+ for(i=0; i< sizeof(ykdb_h); i++) {
+ printf("%02x ", buf[i]);
+ }
+ printf("\n\n");
+
+#endif
+
+ return handle;
+}
+
+ykdb_h *ykdbDatabaseCreate(const char *path) {
+ struct _ykdb_handle *handle;
+
+ /* check argument sanity */
+ if (path == NULL) {
+ YKDB_ERROR(YKDB_ERR_ARGS);
+ return NULL;
+ }
+
+ /* allocate the db handle */
+ handle = (struct _ykdb_handle *)malloc(sizeof(struct _ykdb_handle));
+ if (handle == NULL) {
+ return NULL;
+ }
+ memset(handle,0,sizeof(struct _ykdb_handle));
+
+ /* create the database file */
+ handle->file_descriptor = open(path, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ if (handle->file_descriptor == -1) {
+ free(handle);
+ YKDB_ERROR(YKDB_ERR_IO);
+ return NULL;
+ }
+
+ handle->cur_entry = 0;
+
+ /* build the header */
+ memcpy(handle->header.magic, YKDB_MAGIC, YKDB_MAGIC_SIZE);
+ handle->header.version = YKDB_VERSION;
+ handle->header.entry_count = 0;
+
+ /* write the header to disk */
+ ykdbHeaderWrite(handle);
+ DBG(("ykdbDatabaseCreate\n"));
+
+ return handle;
+}
+
+int ykdbDatabaseClose(ykdb_h *handle) {
+ int ret;
+
+ /* check arguments sanity */
+ if (!handle) {
+ YKDB_ERROR_RET(YKDB_ERR_ARGS);
+ }
+
+ /* write header to disk */
+ if ( ( ret=ykdbHeaderWrite(handle) ) != 0 ) {
+ return ret;
+ }
+
+ /* close the file descriptor*/
+ if ( close(handle->file_descriptor) != 0 ) {
+ YKDB_ERROR_RET(YKDB_ERR_IO);
+ }
+
+ /* free handle */
+ free(handle);
+
+ return YKDB_SUCCESS;
+}
+
+int ykdbEntryNext(ykdb_h *handle)
+{
+#if defined(CONFIG_DEBUG_RSGW)
+ off_t akt_pos;
+#endif
+
+ /* check arguments sanity */
+ if (handle == NULL)
+ {
+ YKDB_ERROR_RET(YKDB_ERR_ARGS);
+ }
+
+ /* check if databse is empty */
+ if (handle->header.entry_count == 0)
+ {
+ YKDB_ERROR_RET(YKDB_ERR_DB_EMPTY);
+ }
+
+ /* check were not already at the end */
+ if (handle->cur_entry == (handle->header.entry_count-1))
+ {
+ YKDB_ERROR_RET(YKDB_ERR_SEEK);
+ }
+
+ /* seek to next entry */
+ if ( lseek(handle->file_descriptor, sizeof(ykdb_entry), SEEK_CUR) == -1)
+ {
+ YKDB_ERROR_RET(YKDB_ERR_IO);
+ }
+
+#if defined(CONFIG_DEBUG_RSGW)
+ akt_pos = lseek(handle->file_descriptor, 0, SEEK_CUR);
+ DBG(("ykdbEntryNext: aktpos: %lu\n",akt_pos));
+#endif
+
+ /* update handle */
+ handle->cur_entry++;
+
+ return YKDB_SUCCESS;
+}
+
+int ykdbEntryGet(ykdb_h *handle, ykdb_entry *entry)
+{
+ int ret;
+ off_t old_pos;
+
+#if defined(CONFIG_DEBUG_RSGW)
+ off_t akt_pos;
+#endif
+ /* check arguments sanity */
+ if (handle == NULL || entry == NULL)
+ {
+ YKDB_ERROR_RET(YKDB_ERR_ARGS);
+ }
+
+ /* check if databse is empty */
+ if (handle->header.entry_count == 0)
+ {
+ YKDB_ERROR_RET(YKDB_ERR_DB_EMPTY);
+ }
+
+ old_pos = lseek(handle->file_descriptor, 0, SEEK_CUR);
+
+#if defined(CONFIG_DEBUG_RSGW)
+ DBG(("ykdbEntryGet: aktpos: %lu\n",old_pos));
+ DBG(("ykdbEntryGet: trying to read %lu bytes\n",sizeof(ykdb_entry)));
+#endif
+
+ /* read entry from disk */
+ if ( (ret=read(handle->file_descriptor, entry, sizeof(ykdb_entry))) != sizeof(ykdb_entry))
+ {
+ YKDB_ERROR_RET(YKDB_ERR_IO);
+ }
+
+ DBG(("ykdbEntryGet: got %d bytes\n",ret));
+
+ /* rewind file position since a get does not increment */
+ if ( lseek(handle->file_descriptor, old_pos, SEEK_SET) == -1 )
+ {
+ YKDB_ERROR_RET(YKDB_ERR_IO);
+ }
+
+#if defined(CONFIG_DEBUG_RSGW)
+ akt_pos = lseek(handle->file_descriptor, 0, SEEK_CUR);
+ DBG(("ykdbEntryGet: aktpos: %lu\n",akt_pos));
+#endif
+
+ return YKDB_SUCCESS;
+}
+
+int ykdbEntrySeekOnIndex(ykdb_h *handle, uint32_t idx)
+{
+ uint32_t file_seek_position;
+
+#if defined(CONFIG_DEBUG_RSGW)
+ off_t akt_pos;
+#endif
+ /* check arguments sanity */
+ if (handle == NULL || idx < 0)
+ {
+ YKDB_ERROR_RET(YKDB_ERR_ARGS);
+ }
+
+ /* check if databse is empty */
+ if (handle->header.entry_count == 0)
+ {
+ YKDB_ERROR_RET(YKDB_ERR_DB_EMPTY);
+ }
+
+ /* calculate seek position and go there */
+ file_seek_position = sizeof(ykdb_header) + (idx * sizeof(ykdb_entry));
+
+ if ( lseek(handle->file_descriptor, file_seek_position, SEEK_SET) == -1)
+ {
+ YKDB_ERROR_RET(YKDB_ERR_IO);
+ }
+
+#if defined(CONFIG_DEBUG_RSGW)
+ akt_pos = lseek(handle->file_descriptor, 0, SEEK_CUR);
+ DBG(("ykdbEntrySeekOnIndex: aktpos: %lu\n",akt_pos));
+#endif
+
+ /* update handle */
+ handle->cur_entry = idx;
+ DBG(("ykdbEntrySeekOnIndex: current_entry %d\n",idx));
+
+ return YKDB_SUCCESS;
+}
+
+int ykdbEntryGetIndex(ykdb_h *handle, uint32_t *idx)
+{
+ /* check arguments sanity */
+ if (handle == NULL || idx == NULL)
+ {
+ YKDB_ERROR_RET(YKDB_ERR_ARGS);
+ }
+
+ /* check if databse is empty */
+ if (handle->header.entry_count == 0)
+ {
+ YKDB_ERROR_RET(YKDB_ERR_DB_EMPTY);
+ }
+
+ *idx = handle->cur_entry;
+
+ return YKDB_SUCCESS;
+}
+
+int ykdbEntryDelete(ykdb_h *handle)
+{
+ uint32_t file_seek_position;
+ ykdb_entry empty_entry;
+
+ /* check arguments sanity */
+ if (handle == NULL)
+ {
+ YKDB_ERROR_RET(YKDB_ERR_ARGS);
+ }
+
+ /* set all values to ff */
+ memset(&empty_entry, 0xff, sizeof(ykdb_entry));
+
+ /* write empty entry to disk */
+ if ( write(handle->file_descriptor, &empty_entry, sizeof(ykdb_entry)) != sizeof(ykdb_entry) )
+ {
+ YKDB_ERROR_RET(YKDB_ERR_IO);
+ }
+
+ /* update the header */
+ handle->header.entry_count--;
+
+ /* reset seek pointer to end of header */
+ file_seek_position = sizeof(ykdb_header);
+ handle->cur_entry = -1;
+
+ if ( lseek(handle->file_descriptor, file_seek_position, SEEK_SET) == -1)
+ {
+ YKDB_ERROR_RET(YKDB_ERR_IO);
+ }
+
+ /* reset back to zero */
+ return YKDB_SUCCESS;
+}
+
+int ykdbEntryAdd(ykdb_h *handle, ykdb_entry *entry)
+{
+#if defined(CONFIG_DEBUG_RSGW)
+ off_t akt_pos;
+#endif
+ /* check arguments sanity */
+ if (handle == NULL || entry == NULL)
+ {
+ YKDB_ERROR_RET(YKDB_ERR_ARGS);
+ }
+
+ /* search for a previously deleted entry (ie. empty entry) */
+ if ( ykdbEntrySeekEmpty(handle) != YKDB_SUCCESS )
+ {
+ /* add to end of file */
+ if ( lseek(handle->file_descriptor, 0, SEEK_END) == -1 )
+ {
+ YKDB_ERROR_RET(YKDB_ERR_IO);
+ }
+ }
+
+ /* write entry to disk */
+ if ( write(handle->file_descriptor, entry, sizeof(ykdb_entry)) != sizeof(ykdb_entry) )
+ {
+ YKDB_ERROR_RET(YKDB_ERR_IO);
+ }
+
+ /* update the header */
+ handle->header.entry_count++;
+
+#if defined(CONFIG_DEBUG_RSGW)
+ akt_pos = lseek(handle->file_descriptor, 0, SEEK_CUR);
+ DBG(("ykdbEntryAdd: aktpos: %lu\n",akt_pos));
+#endif
+
+ return ykdbEntrySeekOnIndex(handle, handle->cur_entry);
+}
+
+int ykdbEntryWrite(ykdb_h *handle, ykdb_entry *entry)
+{
+#if defined(CONFIG_DEBUG_RSGW)
+ off_t akt_pos;
+#endif
+ /* check arguments sanity */
+ if (handle == NULL || entry == NULL)
+ {
+ YKDB_ERROR_RET(YKDB_ERR_ARGS);
+ }
+
+ /* check if databse is empty */
+ if (handle->header.entry_count == 0)
+ {
+ YKDB_ERROR_RET(YKDB_ERR_DB_EMPTY);
+ }
+
+ /* write entry to disk */
+ if ( write(handle->file_descriptor, entry, sizeof(ykdb_entry)) != sizeof(ykdb_entry) )
+ {
+ YKDB_ERROR_RET(YKDB_ERR_IO);
+ }
+
+#if defined(CONFIG_DEBUG_RSGW)
+ akt_pos = lseek(handle->file_descriptor, 0, SEEK_CUR);
+ DBG(("ykdbEntryWrite: aktpos: %lu\n",akt_pos));
+#endif
+
+return YKDB_SUCCESS;
+
+}
+
+int ykdbEntrySeekEmpty(ykdb_h *handle)
+{
+ int i;
+ ykdb_entry entry;
+#if defined(CONFIG_DEBUG_RSGW)
+ off_t akt_pos;
+#endif
+
+ /* check argument sanity */
+ if (handle == NULL)
+ {
+ YKDB_ERROR_RET(YKDB_ERR_ARGS);
+ }
+
+ /* check if databse is empty */
+ if (handle->header.entry_count == 0)
+ {
+ YKDB_ERROR_RET(YKDB_ERR_DB_EMPTY);
+ }
+
+ /* start at beginning of database */
+ if ( ykdbEntrySeekOnIndex(handle, 0) != YKDB_SUCCESS )
+ {
+ YKDB_ERROR_RET(YKDB_ERR_IO);
+ }
+
+ /* loop looking for public hash match */
+ for (i=0; i<handle->header.entry_count; i++, ykdbEntryNext(handle) )
+ {
+ ykdbEntryGet(handle, &entry);
+ if ( entry.flags == 0xffffffff )
+ return YKDB_SUCCESS;
+ }
+
+#if defined(CONFIG_DEBUG_RSGW)
+ akt_pos = lseek(handle->file_descriptor, 0, SEEK_CUR);
+ DBG(("ykdbEntrySeekEmpty: aktpos: %lu\n",akt_pos));
+#endif
+
+ return YKDB_ERR_SEEK;
+}
+
+/* Extended functions */
+int ykdbEntrySeekOnUserHash(ykdb_h *handle, uint8_t *user_hash)
+{
+ int i;
+ off_t old_pos;
+ ykdb_entry entry;
+
+ DBG(("ykdbEntrySeekOnUserHash entry_count: %d\n",handle->header.entry_count));
+
+ /* check argument sanity */
+ if (handle == NULL || user_hash == NULL)
+ {
+ YKDB_ERROR_RET(YKDB_ERR_ARGS);
+ }
+
+ /* check if databse is empty */
+ if (handle->header.entry_count == 0)
+ {
+ YKDB_ERROR_RET(YKDB_ERR_DB_EMPTY);
+ }
+
+ /* save old position in case of fail */
+ old_pos = lseek(handle->file_descriptor, 0, SEEK_CUR);
+
+ /* start at beginning of database */
+ if ( ykdbEntrySeekOnIndex(handle, 0) != YKDB_SUCCESS )
+ {
+ YKDB_ERROR_RET(YKDB_ERR_IO);
+ }
+
+ /* loop looking for public hash match */
+ for (i=0; i<handle->header.entry_count; i++, ykdbEntryNext(handle) )
+ {
+ DBG(("ykdbEntrySeekOnUserHash testing: %d\n",i));
+
+ ykdbEntryGet(handle, &entry);
+
+ if ( memcmp(entry.user_hash, user_hash, 32) == 0 )
+ {
+ DBG(("ykdbEntrySeekOnUserHash found: %d\n",i));
+ return YKDB_SUCCESS;
+ }
+ }
+
+ /* since the record was not found, return to old position */
+ if ( lseek(handle->file_descriptor, old_pos, SEEK_SET) == -1 )
+ {
+ YKDB_ERROR_RET(YKDB_ERR_IO);
+ }
+
+ DBG(("ykdbEntrySeekOnUserHash aktpos: %lu\n",old_pos));
+
+ return YKDB_ERR_SEEK;
+}
+
+int ykdbEntrySeekOnPublicHash(ykdb_h *handle, uint8_t *public_uid_hash)
+{
+ int i;
+ off_t old_pos;
+ ykdb_entry entry;
+
+ /* check argument sanity */
+ if (handle == NULL || public_uid_hash == NULL)
+ {
+ YKDB_ERROR_RET(YKDB_ERR_ARGS);
+ }
+
+ /* check if databse is empty */
+ if (handle->header.entry_count == 0)
+ {
+ YKDB_ERROR_RET(YKDB_ERR_DB_EMPTY);
+ }
+
+ /* save old position in case of fail */
+ old_pos = lseek(handle->file_descriptor, 0, SEEK_CUR);
+
+ /* start at beginning of database */
+ if ( ykdbEntrySeekOnIndex(handle, 0) != YKDB_SUCCESS )
+ {
+ YKDB_ERROR_RET(YKDB_ERR_IO);
+ }
+
+ /* loop looking for public hash match */
+ for (i=0; i<handle->header.entry_count; i++, ykdbEntryNext(handle) )
+ {
+ ykdbEntryGet(handle, &entry);
+
+ if ( memcmp(entry.public_uid_hash, public_uid_hash, 32) == 0 )
+ {
+ return YKDB_SUCCESS;
+ }
+ }
+
+ /* since the record was not found, return to old position */
+ if ( lseek(handle->file_descriptor, old_pos, SEEK_SET) == -1 )
+ {
+ YKDB_ERROR_RET(YKDB_ERR_IO);
+ }
+
+ DBG(("ykdbEntrySeekOnPublicHash aktpos: %lu\n",old_pos));
+
+ return YKDB_ERR_SEEK;
+}
+
+uint32_t ykdbDatabaseEntryCountGet(ykdb_h *handle)
+{
+ /* check argument sanity */
+ if (handle == NULL)
+ {
+ return -1;
+ }
+
+ return handle->header.entry_count;
+}
+
+void ykdbPrintEntry(ykdb_entry *entry)
+{
+ int i;
+
+ printf("ykdb_entry {\n");
+ printf(" user_hash = ");
+ for (i=0; i<32; i++)
+ printf("%02x ", entry->user_hash[i]);
+ printf("\n");
+ printf(" public_uid_hash = ");
+ for (i=0; i<32; i++)
+ printf("%02x ", entry->public_uid_hash[i]);
+ printf("\n");
+ printf(" ticket {\n");
+ printf(" key = ");
+ for (i=0; i<16; i++)
+ printf("%02x ", entry->ticket.key[i]);
+ printf("\n");
+ printf(" private_uid_hash = ");
+ for (i=0; i<32; i++)
+ printf("%02x ", entry->ticket.private_uid_hash[i]);
+ printf("\n");
+ printf(" last_use = %04x\n", entry->ticket.last_use);
+ printf(" last_timestamp_lo = %04x\n", entry->ticket.last_timestamp_lo);
+ printf(" last_timestamp_hi = %02x\n", entry->ticket.last_timestamp_hi);
+ printf(" last_session = %02x\n", entry->ticket.last_session);
+ printf(" }\n");
+ printf("}\n");
+}
+
+
diff --git a/lib/yubikey/yubikey_util.c b/lib/yubikey/yubikey_util.c
new file mode 100644
index 0000000..a648cd2
--- /dev/null
+++ b/lib/yubikey/yubikey_util.c
@@ -0,0 +1,1077 @@
+/*
+ * YubiKey PAM Utils Module
+ *
+ * Copyright (C) 2008 SecurixLive dev at securixlive.com
+ * Copyright (C) 2008 Ian Firns firnsy at securixlive.com
+ * Copyright (C) 2017 Gerd Pauli gp at high-consulting.de
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*
+ * Original Code adapted from YubiCo **
+ * and
+
+ * A contribution to the open-source movement.
+ * Jean-Luc Cooke <jlcooke at certainkey.com>
+ * CertainKey Inc.
+ * Ottawa Ontario Canada
+ *
+ * Created: July 20th, 2001
+ *
+ */
+
+/* reference: http://csrc.nist.gov/encryption/shs/dfips-180-2.pdf */
+
+#include <common.h>
+#include <xfuncs.h>
+#include <malloc.h>
+#include <errno.h>
+#include <libfile.h>
+#include <fs.h>
+#include <fcntl.h>
+#include <progress.h>
+#include <linux/stat.h>
+
+#include <yubikey/yubikey_common.h>
+#include <yubikey/yubikey_util.h>
+
+
+/* start SHA256 requisites - lookup table, defines, etc */
+typedef struct _sha256_context {
+ uint32_t state[8];
+ uint8_t buf[128];
+ uint32_t count[2];
+} sha256_context;
+
+#define ROR32(a,b) (( ((a) >> ((b) & 31)) | ((a) << (32-((b) & 31))) ))
+
+#define Ch(x,y,z) ((x & y) ^ (~x & z))
+#define Maj(x,y,z) ((x & y) ^ (x & z) ^ (y & z))
+
+#define e0(x) (ROR32(x,2) ^ ROR32(x,13) ^ ROR32(x,22))
+#define e1(x) (ROR32(x,6) ^ ROR32(x,11) ^ ROR32(x,25))
+#define s0(x) (ROR32(x,7) ^ ROR32(x,18) ^ (x >> 3))
+#define s1(x) (ROR32(x,17) ^ ROR32(x,19) ^ (x >> 10))
+
+#define H0 0x6a09e667
+#define H1 0xbb67ae85
+#define H2 0x3c6ef372
+#define H3 0xa54ff53a
+#define H4 0x510e527f
+#define H5 0x9b05688c
+#define H6 0x1f83d9ab
+#define H7 0x5be0cd19
+
+const uint32_t sha256_K[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+#define LOAD_OP(I) \
+ { \
+ t1 = input[(4*I) ] & 0xff; t1<<=8; \
+ t1 |= input[(4*I)+1] & 0xff; t1<<=8; \
+ t1 |= input[(4*I)+2] & 0xff; t1<<=8; \
+ t1 |= input[(4*I)+3] & 0xff; \
+ W[I] = t1; \
+ }
+
+#define BLEND_OP(I) \
+ W[I] = s1(W[I-2]) + W[I-7] + s0(W[I-15]) + W[I-16];
+
+const uint8_t sha256_padding[128] = {
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* end SHA256 requisites */
+
+/* start AES requisites - lookup table, defines, etc */
+#define AES_ROUNDS 10
+#define AES_BLOCK_SIZE 16
+
+static const unsigned char rcon[] = {
+ 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36
+};
+
+static const unsigned char sbox[] = {
+ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
+ 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
+ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
+ 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
+ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
+ 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
+ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
+ 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
+ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
+ 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
+ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
+ 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
+ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
+ 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
+ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
+ 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
+ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
+ 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
+ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
+ 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
+ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
+ 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
+ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
+ 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
+ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
+ 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
+ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
+ 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
+ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
+ 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
+ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
+ 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
+};
+
+static const unsigned char inv_sbox[] = {
+ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38,
+ 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
+ 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
+ 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
+ 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d,
+ 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
+ 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2,
+ 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
+ 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
+ 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
+ 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
+ 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
+ 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
+ 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
+ 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
+ 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
+ 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea,
+ 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
+ 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,
+ 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
+ 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
+ 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
+ 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20,
+ 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
+ 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31,
+ 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
+ 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
+ 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
+ 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0,
+ 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
+ 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26,
+ 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
+};
+
+uint8_t xtime(unsigned char b) {
+ return (b & 0x80) ? ((b << 1) ^ 0x1b) : (b << 1);
+}
+
+/* end AES requisites */
+
+
+/*
+** checkHexString
+**
+** Description:
+** Identifies whether a string is a valid hex string.
+**
+** Arguments:
+** const uint8_t *src source containing hex characters
+**
+** Return
+** 0 if source is a hex string, non-zero otherwise.
+*/
+int checkHexString(const uint8_t *src)
+{
+ char trans[] = HEX_MAP;
+ uint32_t src_size = strlen(src);
+ uint32_t i;
+
+ for(i=0; i<src_size; i++, src++) {
+ if ( strchr(trans, tolower(*src)) == NULL )
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+** checkModHexString
+**
+** Description:
+** Identifies whether a string is a valid modhex string.
+**
+** Arguments:
+** const uint8_t *src source containing modhex characters
+**
+** Return
+** 0 if source is a modhex string, non-zero otherwise.
+*/
+int checkModHexString(const uint8_t *src) {
+ char trans[] = MODHEX_MAP;
+ uint32_t src_size = strlen(src);
+ uint32_t i;
+
+ for(i=0; i<src_size; i++, src++) {
+ if ( strchr(trans, tolower(*src)) == NULL )
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/*
+** checkOTPCompliance
+**
+** Description:
+** Identifies whether the string is compliant with the length and character
+** set.
+**
+** Arguments:
+** const uint8_t *otp source containing modhex characters
+** uint8_t min_pub_uid_len minimum length of the public_uid (fixed portion)
+**
+** Return
+** 0 if source is compliant, non-zero otherwise.
+*/
+int checkOTPCompliance(const uint8_t *otp, uint32_t min_pub_uid_len)
+{
+ uint32_t otp_size;
+
+ /* check if OTP exists */
+ if ( otp == NULL )
+ return -1;
+
+ otp_size = strlen(otp);
+
+ /* check length */
+ if ( otp_size < (min_pub_uid_len + 32) )
+ return -2;
+
+ /* check modhex character set */
+ if ( checkModHexString(otp) )
+ return -3;
+
+ return 0;
+}
+
+/*
+** hexDecode
+**
+** Description:
+** Decodeds a hex string into binary. Due to a hex character only
+** representing 4 bits of information, the source should be twice as long as
+** the desired output size.
+**
+** Arguments:
+** uint8_t *dst destination of decoded binary information
+** const uint8_t *src source containing hex characters
+** uint32_t dst_size number of bytes to read into destination buffer
+**
+** Return
+** Number of modhex characters processed
+*/
+uint32_t hexDecode(uint8_t *dst, const uint8_t *src, uint32_t dst_size)
+{
+ static const char trans[] = HEX_MAP;
+ uint8_t b;
+ uint32_t i, processed = 0;
+ uint32_t src_size = strlen(src);
+ const char *p1;
+
+ /* truncate source if destination is too short */
+ if ((dst_size << 1) < src_size)
+ src_size = dst_size << 1;
+
+ for (i = 0; i < src_size; i++, src++) {
+ /* translate the modhex character, set to 0 if not found */
+ if ((p1 = strchr(trans, tolower(*src))))
+ b = (uint8_t) (p1 - trans);
+ else
+ b = 0;
+
+ if (i % 2) {
+ *dst = (*dst << 4) | b;
+ dst++;
+ processed++;
+ } else
+ *dst = b;
+ }
+
+ return processed;
+}
+
+EXPORT_SYMBOL(hexDecode);
+
+/*
+** modHexDecode
+**
+** Description:
+** Decodeds a modhex string into binary. Due to a modhex character only
+** representing 4 bits of information, the source should be twice as long as
+** the desired output size.
+**
+** Arguments:
+** uint8_t *dst destination of decoded binary information
+** const uint8_t *src source containing modhex characters
+** uint32_t dst_size number of bytes to read into destination buffer
+**
+** Return
+** Number of modhex characters processed
+*/
+uint32_t modHexDecode(uint8_t *dst, const uint8_t *src, uint32_t dst_size)
+{
+ static const char trans[] = MODHEX_MAP;
+ uint8_t b;
+ uint32_t i, processed = 0;
+ uint32_t src_size = strlen(src);
+ const char *p1;
+
+ /* truncate source if destination is too short */
+ if ((dst_size << 1) < src_size)
+ src_size = dst_size << 1;
+
+ for (i = 0; i < src_size; i++, src++) {
+ /* translate the modhex character, set to 0 if not found */
+ if ((p1 = strchr(trans, tolower(*src))))
+ b = (uint8_t) (p1 - trans);
+ else
+ b = 0;
+
+ if (i % 2) {
+ *dst = (*dst << 4) | b;
+ dst++;
+ processed++;
+ } else
+ *dst = b;
+ }
+
+ return processed;
+}
+
+EXPORT_SYMBOL(modHexDecode);
+
+/*
+** aesEncryptCBC
+**
+** Description:
+** CBC Encryption of any data size.
+**
+** Arguments:
+** uint8_t * data data to encrypt
+** uint32_t data_zise length of data to encrypt
+** const uint8_t *key encryption key
+** const uint8_t *iv IV for first block
+*/
+void aesEncryptCBC(uint8_t *data, uint32_t data_size, const uint8_t *key, const uint8_t *iv) {
+ const uint8_t *ivec = iv;
+ uint32_t i;
+
+
+ while (data_size >= AES_BLOCK_SIZE) {
+ for(i=0; i<AES_BLOCK_SIZE; ++i)
+ data[i] ^= ivec[i];
+
+ aesEncryptBlock(data, key);
+ ivec = data;
+ data_size -= AES_BLOCK_SIZE;
+ data += AES_BLOCK_SIZE;
+ }
+
+ if (data_size) {
+ for(i=0; i<data_size; ++i)
+ data[i] ^= ivec[i];
+
+ for(i=data_size; i<AES_BLOCK_SIZE; ++i)
+ data[i] = ivec[i];
+
+ aesEncryptBlock(data, key);
+ }
+}
+
+/*
+** aesDecryptCBC
+**
+** Description:
+** CBC Decryption of any data size.
+**
+** Arguments:
+** uint8_t * data data to decrypt
+** uint32_t data_zise length of data to decrypt
+** const uint8_t *key decryption key
+** const uint8_t *iv IV for first block
+*/
+void aesDecryptCBC(uint8_t *data, uint32_t data_size, const uint8_t *key, const uint8_t *iv) {
+ uint8_t ivec[AES_BLOCK_SIZE];
+ uint8_t iv_next[AES_BLOCK_SIZE];
+ uint32_t i;
+
+ memcpy(ivec, iv, AES_BLOCK_SIZE);
+ while (data_size >= AES_BLOCK_SIZE) {
+ memcpy(iv_next, data, AES_BLOCK_SIZE);
+ aesDecryptBlock(data, key);
+
+ for(i=0; i<AES_BLOCK_SIZE; ++i)
+ data[i] ^= ivec[i];
+
+ memcpy(ivec, iv_next, AES_BLOCK_SIZE);
+ data_size -= AES_BLOCK_SIZE;
+ data += AES_BLOCK_SIZE;
+ }
+
+ if (data_size) {
+ memcpy(iv_next, data, AES_BLOCK_SIZE);
+ aesDecryptBlock(data,key);
+
+ for(i=0; i<data_size; ++i)
+ data[i] ^= ivec[i];
+
+ for(i=data_size; i<AES_BLOCK_SIZE; ++i)
+ data[i] = iv_next[i];
+ }
+}
+
+/*
+** aesEncryptBlock
+**
+** Description:
+** Encrypts a single 128bit block with AES.
+**
+** Arguments:
+** unsigned char *block block buffer that contains the data to be
+** encrypted (ie. plain in, cipher out);
+** const unsigned char *key 128-bit key to use for encryption
+*/
+void aesEncryptBlock(uint8_t *block, const uint8_t *key)
+{
+ uint8_t i, j, k, tmp, round_key[0x10];
+
+ memcpy(round_key, key, sizeof(round_key));
+
+ for (i = 0; i < 16; i++)
+ block[i] ^= key[i];
+
+ for (i = 0; i < AES_ROUNDS; i++)
+ {
+ // byte_sub_shift_row(block);
+ block[0] = sbox[block[0]];
+ block[4] = sbox[block[4]];
+ block[8] = sbox[block[8]];
+ block[12] = sbox[block[12]];
+
+ tmp = block[1];
+ block[1] = sbox[block[5]];
+ block[5] = sbox[block[9]];
+ block[9] = sbox[block[13]];
+ block[13] = sbox[tmp];
+
+ tmp = block[2];
+ block[2] = sbox[block[10]];
+ block[10] = sbox[tmp];
+ tmp = block[6];
+ block[6] = sbox[block[14]];
+ block[14] = sbox[tmp];
+
+ tmp = block[15];
+ block[15] = sbox[block[11]];
+ block[11] = sbox[block[7]];
+ block[7] = sbox[block[3]];
+ block[3] = sbox[tmp];
+
+ if (i != (AES_ROUNDS - 1))
+ {
+ // mix_column(block);
+ for (k = 0; k < 16; k += 4)
+ {
+ j = block[k] ^ block[k + 1];
+ tmp = j ^ block[k + 2] ^ block[k + 3];
+
+ j = xtime(j);
+
+ block[k] ^= (j ^ tmp);
+
+ j = block[k + 1] ^ block[k + 2];
+ j = xtime(j);
+
+ block[k + 1] ^= (j ^ tmp);
+
+ j = block[k + 2] ^ block[k + 3];
+ j = xtime(j);
+
+ block[k + 2] ^= (j ^ tmp);
+ block[k + 3] = block[k] ^ block[k + 1] ^ block[k + 2] ^ tmp;
+ }
+ }
+
+ round_key[0] ^= rcon[i];
+
+ round_key[0] ^= sbox[round_key[13]];
+ round_key[1] ^= sbox[round_key[14]];
+ round_key[2] ^= sbox[round_key[15]];
+ round_key[3] ^= sbox[round_key[12]];
+
+ for (k = 4; k < 16; k++)
+ round_key[k] ^= round_key[k - 4];
+
+ // add_round_key(block, round_key);
+ for (j = 0; j < 16; j++)
+ block[j] ^= round_key[j];
+ }
+}
+
+/*
+** aesDecryptBlock
+**
+** Description:
+** Decrypts a single 128bit block with AES.
+**
+** Arguments:
+** unsigned char *block block buffer that contains the data to be
+** decrypted (ie. cipher in, plain out);
+** const unsigned char *key 128-bit key to use for decryption
+*/
+void aesDecryptBlock(uint8_t *block, const uint8_t *key)
+{
+ uint8_t i, j, round_key[0x10];
+ uint8_t a02x, a13x;
+ uint8_t a02xx, a13xx;
+ uint8_t k1, k2;
+
+ memcpy(round_key, key, sizeof(round_key));
+ for (i = 0; i < AES_ROUNDS; i++)
+ {
+ round_key[0] ^= rcon[i];
+
+ round_key[0] ^= sbox[round_key[13]];
+ round_key[1] ^= sbox[round_key[14]];
+ round_key[2] ^= sbox[round_key[15]];
+ round_key[3] ^= sbox[round_key[12]];
+
+ for (j = 4; j < 16; j++)
+ round_key[j] ^= round_key[j - 4];
+ }
+
+ for (i = 0; i < 0x10; i++)
+ block[i] ^= round_key[i];
+
+ for (i = 1; i <= AES_ROUNDS; i++)
+ {
+ // inv_byte_sub_shift_row();
+ block[0] = inv_sbox[block[0]];
+ block[4] = inv_sbox[block[4]];
+ block[8] = inv_sbox[block[8]];
+ block[12] = inv_sbox[block[12]];
+
+ j = block[13];
+ block[13] = inv_sbox[block[9]];
+ block[9] = inv_sbox[block[5]];
+ block[5] = inv_sbox[block[1]];
+ block[1] = inv_sbox[j];
+
+ j = block[2];
+ block[2] = inv_sbox[block[10]];
+ block[10] = inv_sbox[j];
+ j = block[6];
+ block[6] = inv_sbox[block[14]];
+ block[14] = inv_sbox[j];
+
+ j = block[3];
+ block[3] = inv_sbox[block[7]];
+ block[7] = inv_sbox[block[11]];
+ block[11] = inv_sbox[block[15]];
+ block[15] = inv_sbox[j];
+
+ // get_inv_round_key(i);
+ for (j = 15; j > 3; j--)
+ round_key[j] ^= round_key[j - 4];
+
+ round_key[0] ^= (rcon[AES_ROUNDS - i] ^ sbox[round_key[13]]);
+
+ round_key[1] ^= sbox[round_key[14]];
+ round_key[2] ^= sbox[round_key[15]];
+ round_key[3] ^= sbox[round_key[12]];
+
+ for (j = 0; j < 16; j++)
+ block[j] ^= round_key[j];
+
+ if (i != AES_ROUNDS)
+ {
+ // inv_mix_column();
+ for (j = 0; j < 16; j += 4)
+ {
+ k1 = block[j] ^ block[j + 2];
+ a02x = xtime(k1);
+ k2 = block[j + 1] ^ block[j + 3];
+ a13x = xtime(k2);
+
+ k1 ^= (k2 ^ xtime(block[j + 1] ^ block[j + 2]));
+ k2 = k1;
+
+ a02xx = xtime(a02x);
+ a13xx = xtime(a13x);
+
+ k1 ^= (xtime(a02xx ^ a13xx) ^ a02xx);
+ k2 ^= (xtime(a02xx ^ a13xx) ^ a13xx);
+
+ block[j] ^= (k1 ^ a02x);
+ block[j + 1] ^= k2;
+ block[j + 2] ^= (k1 ^ a13x);
+ block[j + 3] ^= (k2 ^ a02x ^ a13x);
+ }
+ }
+ }
+}
+
+/*************************************************************************
+ ** function getCRC **
+ ** Calculate ISO13239 checksum of buffer **
+ ** **
+ ** unsigned short getCRC(const unsigned char *buf, int bcnt) **
+ ** **
+ ** Where: **
+ ** "buf" is pointer to buffer **
+ ** "bcnt" is size of the buffer **
+ ** **
+ ** Returns: ISO13239 checksum **
+ ** **
+ *************************************************************************/
+
+/*
+** getCRC
+**
+** Description:
+** Calculates the ISO 13239 16 bit checksum of data.
+**
+** Arguments:
+** const uint8_t *data pointer to data buffer
+** uint32_t size size of the data bufffer to calculate over
+**
+** Returns:
+** 16 bit ISO 13239 checksum.
+*/
+uint16_t getCRC(const uint8_t *data, uint32_t size)
+{
+ uint16_t crc = 0xffff;
+ uint8_t i;
+
+ while (size--)
+ {
+ crc ^= *data++;
+
+ for (i = 0; i < 8; i++)
+ crc = (crc & 1) ? ((crc >> 1) ^ 0x8408) : (crc >> 1);
+ }
+
+ return crc;
+}
+
+/*
+** parseOTP
+**
+** Description:
+** Decodeds a Yubikey One Time Pad (OTP) in modhex format. It expects at
+** least 32 modhex characters (ie 128 bits) of information which is the token
+** in it's encrypted format. Additional prepended data is assumed to be the
+** public UID portion of the token.
+**
+** Arguments:
+** yk_ticket *tkt destination of parsed ticket information
+** uint8_t *public_uid destination of public UID if present
+** uint8_t *public_uid_size byte size of the public UID
+** const uint8_t *otp source OTP in modhex format (>=32 chars)
+** const uint8_t *otp AES decryptino key in hex format (16 bytes)
+**
+** Returns:
+** Return 0 on success, non zero otherwise.
+*/
+int parseOTP(yk_ticket *tkt, uint8_t *public_uid, uint8_t *public_uid_size, const uint8_t *otp, const uint8_t *key)
+{
+ uint8_t otp_bin[PUBLIC_UID_BYTE_SIZE + sizeof(yk_ticket)];
+ uint32_t otp_bin_size;
+ uint16_t crc;
+
+ /* convert from either modhex or hex */
+ if ( !checkHexString(otp) )
+ {
+ if ((otp_bin_size = hexDecode(otp_bin, otp, sizeof(otp_bin))) < sizeof(yk_ticket))
+ return 1;
+ }
+ else if ( !checkModHexString(otp) )
+ {
+ if ((otp_bin_size = modHexDecode(otp_bin, otp, sizeof(otp_bin))) < sizeof(yk_ticket))
+ return 1;
+ }
+ else
+ {
+ return 1;
+ }
+
+ /* must be at least the size of a yk_ticket structure */
+ if (otp_bin_size < sizeof(yk_ticket))
+ return 1;
+
+ /* ticket is located in the last 16 bytes */
+ memcpy(tkt, otp_bin + otp_bin_size - sizeof(yk_ticket), sizeof(yk_ticket));
+
+ /* grab the public uid (if present) */
+ *public_uid_size = (uint8_t) (otp_bin_size - sizeof(yk_ticket));
+
+ /* limit public uid to maximum allowable by a Yubikey */
+ if ( *public_uid_size > PUBLIC_UID_BYTE_SIZE )
+ *public_uid_size = PUBLIC_UID_BYTE_SIZE;
+
+ /* store the public uid if exists */
+ if (*public_uid_size > 0)
+ memcpy(public_uid, otp_bin, *public_uid_size);
+ else
+ *public_uid_size = 0;
+
+ /* decrypt the single block (ie. 128bit) ticket */
+ if (key == NULL)
+ return 1;
+
+ aesDecryptBlock((uint8_t *) tkt, key);
+
+ /* calculate CRC of the ticket */
+ crc = getCRC((uint8_t *) tkt, sizeof(yk_ticket));
+
+ /* ticket is generated in little endian */
+ ENDIAN_SWAP_16(crc);
+
+ if (crc != CRC_OK_RESIDUE)
+ return 1;
+
+ // Shape up little-endian fields (if applicable)
+ ENDIAN_SWAP_16(tkt->random);
+ ENDIAN_SWAP_16(tkt->timestamp_lo);
+ ENDIAN_SWAP_16(tkt->use_counter);
+
+ return 0;
+}
+
+/*
+** sha256_xform
+**
+** Description:
+** Perform a 256bit transform on the input block by placing 8 bit words into
+** 32 bit words.
+**
+** Arguments:
+** uint32_t *state destination for 32 bit words after transform
+** const uint8_t *input source of 8 bit words
+*/
+void sha256_xform(uint32_t *state, const uint8_t *input)
+{
+ uint32_t a, b, c, d, e, f, g, h, t1, t2;
+ uint32_t W[64];
+
+ int i;
+
+ /* load the input */
+ for (i=0; i<16; i++)
+ LOAD_OP(i);
+
+ /* now blend */
+ for (i=16; i<64; i++)
+ BLEND_OP(i);
+
+ /* load the state into our registers */
+ a=state[0]; b=state[1]; c=state[2]; d=state[3];
+ e=state[4]; f=state[5]; g=state[6]; h=state[7];
+
+ /* now blend */
+ for (i=0; i<64; i++)
+ {
+ t1 = h + e1(e) + Ch(e,f,g) + sha256_K[i] + W[i];
+ t2 = e0(a) + Maj(a,b,c);
+ h = g; g = f; f = e; e = d + t1;
+ d = c; c = b; b = a; a = t1 + t2;
+ }
+
+ state[0]+=a; state[1]+=b; state[2]+=c; state[3]+=d;
+ state[4]+=e; state[5]+=f; state[6]+=g; state[7]+=h;
+}
+
+/*
+** sha256_init
+**
+** Description:
+** Initialises the context prior to generating a SHA256 hash.
+**
+** Arguments:
+** sha256_context *C context structure used during hash generation
+*/
+void sha256_init(sha256_context *C)
+{
+ C->state[0] = H0;
+ C->state[1] = H1;
+ C->state[2] = H2;
+ C->state[3] = H3;
+ C->state[4] = H4;
+ C->state[5] = H5;
+ C->state[6] = H6;
+ C->state[7] = H7;
+ C->count[0] = C->count[1] = 0;
+
+ memset(C->buf, 0, 128);
+}
+
+/*
+** sha256_update
+**
+** Description:
+** Updates the context with the stream/block data being passed.
+**
+** Arguments:
+** sha256_context *C context structure used during hash generation
+** const uint8_t *data input block/stream data to hash
+** uint32_t size size of data to process
+*/
+void sha256_update(sha256_context *C, const uint8_t *data, uint32_t size)
+{
+ uint32_t i, index, chunk_size;
+
+ /* calculate number of bytes mod 128 */
+ index = (uint32_t)((C->count[0] >> 3) & 0x3f);
+
+ /* update number of bits */
+ if ((C->count[0] += (size << 3)) < (size << 3)) {
+ C->count[1]++;
+ C->count[1] += (size >> 29);
+ }
+
+ chunk_size = 64 - index;
+
+ /* transform in chunks as required */
+ if (size >= chunk_size)
+ {
+ memcpy((uint8_t *)&C->buf[index], data, chunk_size);
+ sha256_xform(C->state, C->buf);
+
+ for (i=chunk_size; i+63<size; i+=64)
+ sha256_xform(C->state, &data[i]);
+
+ index = 0;
+ }
+ else
+ {
+ i = 0;
+ }
+
+ /* buffer remaining input */
+ memcpy((uint8_t *)&C->buf[index], (uint8_t *)&data[i], size-i);
+}
+
+/*
+** sha256_final
+**
+** Description:
+** Finalies the context and produces the final SHA256 digest.
+**
+** Arguments:
+** uint8_t *digest pointer to final hash digest
+** sha256_context *C context structure used during hash generation
+*/
+void sha256_final(uint8_t *digest, sha256_context *C)
+{
+ uint8_t bits[8];
+ uint32_t index, pad_size, t;
+ uint32_t i, j;
+
+ /* save number of bits */
+ t = C->count[0];
+ bits[7] = t; t>>=8;
+ bits[6] = t; t>>=8;
+ bits[5] = t; t>>=8;
+ bits[4] = t; t>>=8;
+ t = C->count[1];
+ bits[3] = t; t>>=8;
+ bits[2] = t; t>>=8;
+ bits[1] = t; t>>=8;
+ bits[0] = t; t>>=8;
+
+ /* pad out to 56 mod 64. */
+ index = (C->count[0] >> 3) & 0x3f;
+ pad_size = (index < 56) ? (56 - index) : ((64+56) - index);
+ sha256_update(C, (uint8_t *)sha256_padding, pad_size);
+
+ /* append length (before padding) */
+ sha256_update(C, bits, 8);
+
+ /* store state in digest */
+ for (i=j=0; i<8; i++, j+=4)
+ {
+ t = C->state[i];
+ digest[j+3] = t; t>>=8;
+ digest[j+2] = t; t>>=8;
+ digest[j+1] = t; t>>=8;
+ digest[j ] = t;
+ }
+
+ /* zeroize sensitive information. */
+ memset(C, 0, sizeof(sha256_context));
+}
+
+/*
+** getSHA256
+**
+** Description:
+** Produces a SHA256 hash based on teh input data. Wraps the pervious *init,
+** *update and *final functions.
+**
+** Arguments:
+** const uint8_t *data input block/stream data to hash
+** uint32_t size size of data to process
+** uint8_t *digest pointer to final hash digest
+*/
+void getSHA256(const uint8_t *data, uint32_t size, uint8_t *digest)
+{
+ sha256_context context;
+
+ if (size <= 0)
+ return;
+
+ sha256_init(&context);
+ sha256_update(&context, data, size);
+ sha256_final(digest, &context);
+}
+
+void printTicket(yk_ticket *tkt)
+{
+ int i;
+
+ printf("ticket {\n");
+ printf(" private uid = ");
+ for (i = 0; i<PRIVATE_UID_BYTE_SIZE; i++)
+ printf("%02x ", tkt->private_uid[i]);
+ printf("[%u]\n", PRIVATE_UID_BYTE_SIZE);
+ printf(" counter = 0x%04x (%u)\n", tkt->use_counter, tkt->use_counter);
+ printf(" timestamp (low) = 0x%04x (%u)\n", tkt->timestamp_lo, tkt->timestamp_lo);
+ printf(" timestamp (high) = 0x%02x (%u)\n", tkt->timestamp_hi, tkt->timestamp_hi);
+ printf(" session use = 0x%02x (%u)\n", tkt->session_counter, tkt->session_counter);
+ printf(" pseudo-random = 0x%04x (%u)\n", tkt->random, tkt->random);
+ printf(" crc = 0x%04x (%u)\n", tkt->crc, tkt->crc);
+ printf("}\n");
+}
+
+/* Guaranteed to be '\0' terminated even if truncation occurs.
+ */
+int safeSnprintf(char *buf, size_t buf_size, const char *format, ...)
+{
+ va_list ap;
+ int ret;
+
+ if (buf == NULL || buf_size <= 0 || format == NULL)
+ return -1;
+
+ /* zero first byte in case an error occurs with
+ * vsnprintf, so buffer is null terminated with
+ * zero length */
+ buf[0] = '\0';
+ buf[buf_size - 1] = '\0';
+
+ va_start(ap, format);
+ ret = vsnprintf(buf, buf_size, format, ap);
+ va_end(ap);
+
+ if (ret < 0)
+ return -1;
+
+ if (buf[buf_size - 1] != '\0' || (size_t)ret >= buf_size)
+ {
+ /* result was truncated */
+ buf[buf_size - 1] = '\0';
+ return -2;
+ }
+
+ return 0;
+}
+
+/* Appends to a given string
+** Guaranteed to be '\0' terminated even if truncation occurs.
+*/
+int safeSnprintfAppend(char *buf, size_t buf_size, const char *format, ...)
+{
+ int str_len;
+ int ret;
+ va_list ap;
+
+ if (buf == NULL || buf_size <= 0 || format == NULL)
+ return -1;
+
+ str_len = safeStrnlen(buf, buf_size);
+
+ /* since we've already checked buf and buf_size an error
+ * indicates no null termination, so just start at
+ * beginning of buffer */
+ if (str_len == -1)
+ {
+ buf[0] = '\0';
+ str_len = 0;
+ }
+
+ buf[buf_size - 1] = '\0';
+ va_start(ap, format);
+ ret = vsnprintf(buf + str_len, buf_size - (size_t)str_len, format, ap);
+ va_end(ap);
+
+ if (ret < 0)
+ return -1;
+
+ if (buf[buf_size - 1] != '\0' || (size_t)ret >= buf_size)
+ {
+ /* truncation occured */
+ buf[buf_size - 1] = '\0';
+ return -2;
+ }
+
+ return 0;
+}
+
+int safeStrnlen(const char *buf, int buf_size)
+{
+ int i = 0;
+
+ if (buf == NULL || buf_size <= 0)
+ return -1;
+
+ for (i = 0; i < buf_size; i++)
+ {
+ if (buf[i] == '\0')
+ break;
+ }
+
+ if (i == buf_size)
+ return -1;
+
+ return i;
+}
+
--
1.9.1
More information about the barebox
mailing list