]> err.no Git - yubikey-personalization/commitdiff
Add challenge-response tool (ykchalresp).
authorFredrik Thulin <fredrik@yubico.com>
Sat, 5 Feb 2011 18:42:53 +0000 (19:42 +0100)
committerFredrik Thulin <fredrik@yubico.com>
Sat, 5 Feb 2011 18:42:53 +0000 (19:42 +0100)
This required implementing multi-feature-report reads in ykcore.c.

It is kind of wrong to have these functions in libykpers, but that's
where they are at the moment. We might split some part of libykpers
out into a new library before releasing anything official. This is
to be considered work in progress.

Makefile.am
NEWS
configure.ac
libykpers-1.map
ykchalresp.c [new file with mode: 0644]
ykcore/ykcore.c
ykcore/ykcore.h

index 7e0848bc5f5f5e995f16e1d9df2ada104384f214..748c453f04625e6a97b7a380d975f185ae4befb5 100644 (file)
@@ -61,11 +61,14 @@ endif
 
 # The command line tools.
 
-bin_PROGRAMS = ykpersonalize
+bin_PROGRAMS = ykpersonalize ykchalresp
 
 ykpersonalize_SOURCES = ykpersonalize.c
 ykpersonalize_LDADD = ./libykpers-1.la ./ykcore/libykcore.la
 
+ykchalresp_SOURCES = ykchalresp.c
+ykchalresp_LDADD = ./libykpers-1.la ./ykcore/libykcore.la
+
 dist_man1_MANS = ykpersonalize.1
 
 # Dist docs from wiki.
diff --git a/NEWS b/NEWS
index 9fe14b82169635e0932db39e5bcc0be142687e1c..0925fafd67be54fa484bb2c8037597d34888cc7f 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,7 @@ Yubikey-personalize NEWS -- History of user-visible changes.     -*- outline -*-
 ** When generating keys from passphrase, generate 160 bit keys for modes
 that support it (OATH-HOTP and HMAC challenge response).
 
+** An additional program to use challenge-response was added.
 * Version 1.4.1 (released 2011-01-19)
 ** Restore OATH-HOTP symbols for LIBYKPERS_1.0.
 Accidentally moved to LIBYKPERS_1.4 in 1.4.0.
index a231f577089d8cd3eed1ca369d39ea88bfb102b9..5a5c4e26f13a6692ec0286a39e8ed9b8830772d5 100644 (file)
@@ -36,9 +36,9 @@ AC_CONFIG_MACRO_DIR([m4])
 # Interfaces changed/added/removed:   CURRENT++       REVISION=0
 # Interfaces added:                             AGE++
 # Interfaces removed:                           AGE=0
-AC_SUBST(LT_CURRENT, 4)
-AC_SUBST(LT_REVISION, 1)
-AC_SUBST(LT_AGE, 3)
+AC_SUBST(LT_CURRENT, 5)
+AC_SUBST(LT_REVISION, 0)
+AC_SUBST(LT_AGE, 4)
 
 AM_INIT_AUTOMAKE([-Wall -Werror])
 AC_PROG_CC
index 6a03c8acebd0ed198c72a5f5620fed27a681e1f4..f18d42ed91e56ffdfba5d6bddea57818c0ab3b39 100644 (file)
@@ -109,3 +109,11 @@ LIBYKPERS_1.4 {
     ykp_set_extflag_SERIAL_API_VISIBLE;
 # Variables:
 } LIBYKPERS_1.0;
+
+LIBYKPERS_1.5 {
+  global:
+# Functions:
+  yk_wait_for_key_status;
+  yk_read_response_from_key;
+# Variables:
+} LIBYKPERS_1.4;
diff --git a/ykchalresp.c b/ykchalresp.c
new file mode 100644 (file)
index 0000000..25772ad
--- /dev/null
@@ -0,0 +1,314 @@
+/* -*- mode:C; c-file-style: "bsd" -*- */
+/*
+ * Copyright (c) 2011 Yubico AB.
+ * All rights reserved.
+ *
+ * Author : Fredrik Thulin <fredrik@yubico.com>
+ *
+ * Some basic code copied from ykpersonalize.c.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include <ykpers.h>
+#include <yubikey.h>
+#include <ykdef.h>
+
+const char *usage =
+       "Usage: ykchalresp [options] challenge\n"
+       "\n"
+       "Options :\n"
+       "\n"
+       "\t-1        Send challenge to slot 1. This is the default.\n"
+       "\t-2        Send challenge to slot 2.\n"
+       "\t-H        Send a 64 byte HMAC challenge. This is the default.\n"
+       "\t-Y        Send a 6 byte Yubico challenge.\n"
+       "\t-N        Abort if Yubikey requires button press.\n"
+       "\t-x        Challenge is hex encoded.\n"
+       "\n"
+       "\t-v        verbose\n"
+       "\t-h        help (this text)\n"
+       "\n"
+       "\n"
+       ;
+const char *optstring = "12xvhHYN";
+
+static void report_yk_error()
+{
+       if (ykp_errno)
+               fprintf(stderr, "Yubikey personalization error: %s\n",
+                       ykp_strerror(ykp_errno));
+       if (yk_errno) {
+               if (yk_errno == YK_EUSBERR) {
+                       fprintf(stderr, "USB error: %s\n",
+                               yk_usb_strerror());
+               } else {
+                       fprintf(stderr, "Yubikey core error: %s\n",
+                               yk_strerror(yk_errno));
+               }
+       }
+}
+
+int parse_args(int argc, char **argv,
+              int *slot, bool *verbose,
+              unsigned char **challenge, unsigned int *challenge_len,
+              bool *hmac, bool *may_block,
+              int *exit_code)
+{
+       char c;
+       bool hex_encoded = false;
+
+       while((c = getopt(argc, argv, optstring)) != -1) {
+               switch (c) {
+               case '1':
+                       *slot = 1;
+                       break;
+               case '2':
+                       *slot = 2;
+                       break;
+               case 'H':
+                       *hmac = true;
+                       break;
+               case 'N':
+                       *may_block = false;
+                       break;
+               case 'Y':
+                       *hmac = false;
+                       break;
+               case 'x':
+                       hex_encoded = true;
+                       break;
+               case 'v':
+                       *verbose = true;
+                       break;
+               case 'h':
+               default:
+                       fputs(usage, stderr);
+                       *exit_code = 0;
+                       return 0;
+               }
+       }
+
+       if (optind >= argc) {
+               /* No challenge */
+               fputs(usage, stderr);
+               return 0;
+       }
+
+       if (hex_encoded) {
+               static unsigned char decoded[64];
+               int decoded_len;
+
+               int strl = strlen(argv[optind]);
+
+               if (strl >= sizeof(decoded) * 2) {
+                       fprintf(stderr, "Hex-encoded challenge too long (max %i chars)\n",
+                               sizeof(decoded) * 2);
+                       return 0;
+               }
+
+               if (strl % 2 != 0) {
+                       fprintf(stderr, "Odd number of characters in hex-encoded challenge\n");
+                       return 0;
+               }
+
+               memset(decoded, 0, sizeof(decoded));
+
+               if (yubikey_hex_p(argv[optind])) {
+                       yubikey_hex_decode((char *)decoded, argv[optind], strl);
+               } else {
+                       fprintf(stderr, "Bad hex-encoded string '%s'\n", argv[optind]);
+                       return 0;
+               }
+               *challenge = (unsigned char *) &decoded;
+               *challenge_len = strl / 2;
+       } else {
+               *challenge = argv[optind];
+               *challenge_len = strlen(*challenge);
+       }
+
+       return 1;
+}
+
+int check_firmware(YK_KEY *yk, bool verbose)
+{
+       YK_STATUS *st = ykds_alloc();
+               
+       if (!yk_get_status(yk, st)) {
+               free(st);
+               return 0;
+       }
+
+       if (verbose) {
+               printf("Firmware version %d.%d.%d\n",
+                      ykds_version_major(st),
+                      ykds_version_minor(st),
+                      ykds_version_build(st));
+               fflush(stdout);
+       }
+
+       if (ykds_version_major(st) < 2 ||
+           ykds_version_minor(st) < 2) {
+               fprintf(stderr, "Challenge-response not supported before YubiKey 2.2.\n");
+               free(st);
+               return 0;
+       }
+
+       free(st);
+       return 1;
+}
+
+int challenge_response(YK_KEY *yk, int slot,
+                      unsigned char *challenge, unsigned int len,
+                      bool hmac, bool may_block, bool verbose)
+{
+       unsigned char response[64];
+       unsigned char output_buf[sizeof(response) * 2];
+       int yk_cmd;
+       unsigned int flags = 0;
+       unsigned int response_len = 0;
+       unsigned int expect_bytes = 0;
+
+       memset(response, 0, sizeof(response));
+
+       if (may_block)
+               flags |= YK_FLAG_MAYBLOCK;
+
+       if (verbose) {
+               fprintf(stderr, "Sending %i bytes %s challenge to slot %i\n", len, (hmac == true)?"HMAC":"Yubico", slot);
+               //_yk_hexdump(challenge, len);
+       }
+
+       switch(slot) {
+       case 1:
+               yk_cmd = (hmac == true) ? SLOT_CHAL_HMAC1 : SLOT_CHAL_OTP1;
+               break;
+       case 2:
+               yk_cmd = (hmac == true) ? SLOT_CHAL_HMAC2 : SLOT_CHAL_OTP2;
+               break;
+       }
+
+       if (!yk_write_to_key(yk, yk_cmd, challenge, len))
+               return 0;
+
+       if (verbose) {
+               fprintf(stderr, "Reading response...\n");
+       }
+       
+       /* HMAC responses are 160 bits, Yubico 128 */
+       expect_bytes = (hmac == true) ? 20 : 16;
+
+       if (! yk_read_response_from_key(yk, slot, flags,
+                                       &response, sizeof(response),
+                                       expect_bytes,
+                                       &response_len))
+               return 0;
+
+       if (hmac && response_len > 20)
+               response_len = 20;
+       if (! hmac && response_len > 16)
+               response_len = 16;
+       
+       memset(output_buf, 0, sizeof(output_buf));
+       if (hmac) {
+               yubikey_hex_encode(output_buf, (char *)response, response_len);
+       } else {
+               yubikey_modhex_encode(output_buf, (char *)response, response_len);
+       }
+       printf("%s\n", output_buf);
+
+       return 1;
+}
+
+int main(int argc, char **argv)
+{
+       YK_KEY *yk = 0;
+       bool error = true;
+       int exit_code = 0;
+
+       /* Options */
+       bool verbose = false;
+       bool hex_encoded = false;
+       bool hmac = true;
+       bool may_block = true;
+       unsigned char *challenge;
+       unsigned int challenge_len;
+       int slot = 1;
+
+       ykp_errno = 0;
+       yk_errno = 0;
+
+       if (! parse_args(argc, argv,
+                        &slot, &verbose,
+                        &challenge, &challenge_len,
+                        &hmac, &may_block,
+                        &exit_code))
+               goto err;
+
+       if (!yk_init()) {
+               exit_code = 1;
+               goto err;
+       }
+
+       if (!(yk = yk_open_first_key())) {
+               exit_code = 1;
+               goto err;
+       }
+
+       if (! check_firmware(yk, verbose)) {
+               exit_code = 1;
+               goto err;
+       }
+
+       if (! challenge_response(yk, slot,
+                                challenge, challenge_len,
+                                hmac, may_block, verbose)) {
+               exit_code = 1;
+               goto err;
+       }
+
+       exit_code = 0;
+       error = false;
+
+err:
+       if (error || exit_code != 0) {
+               report_yk_error();
+       }
+
+       if (yk && !yk_close_key(yk)) {
+               report_yk_error();
+               exit_code = 2;
+       }
+
+       if (!yk_release()) {
+               report_yk_error();
+               exit_code = 2;
+       }
+
+       exit(exit_code);
+}
index c7065a1dc949a66c10e42678d5bb63a0f16e9a61..74470090444abd9e4f2b81fee716145493985114 100644 (file)
@@ -35,6 +35,7 @@
 /* To get modhex and crc16 */
 #include <yubikey.h>
 
+#include <stdio.h>
 #ifndef _WIN32
 #include <unistd.h>
 #define Sleep(x) usleep((x)*1000)
@@ -202,7 +203,9 @@ static const char *errtext[] = {
        "unsupported firmware version",
        "out of memory",
        "no status structure given",
-       "not yet implemented"
+       "not yet implemented",
+       "checksum mismatch",
+       "operation would block"
 };
 const char *yk_strerror(int errnum)
 {
@@ -215,9 +218,15 @@ const char *yk_usb_strerror()
        return _ykusb_strerror();
 }
 
-/* Note: we currently have no idea whatsoever how to read things larger
-   than FEATURE_RPT_SIZE - 1.  We also have no idea what to do with the
-   slot parameter, it currently is there for future purposes only. */
+/* This function would've been better named 'yk_read_status_from_key'. Because
+ * it disregards the first byte in each feature report, it can't be used to read
+ * generic feature reports from the Yubikey, and this behaviour can't be changed
+ * without breaking compatibility with existing programs.
+ *
+ * See yk_read_response_from_key() for a generic purpose data reading function.
+ *
+ * The slot parameter is here for future purposes only.
+ */
 int yk_read_from_key(YK_KEY *yk, uint8_t slot,
                     void *buf, unsigned int bufsize, unsigned int *bufcount)
 {
@@ -235,12 +244,163 @@ int yk_read_from_key(YK_KEY *yk, uint8_t slot,
 
        /* This makes it apparent that there's some mysterious value in
           the first byte...  I wonder what...  /Richard Levitte */
-       memcpy(buf, data + 1, bufsize); 
+       memcpy(buf, data + 1, bufsize);
        *bufcount = bufsize;
 
        return 1;
 }
 
+/* Wait for the Yubikey to either set or clear (controlled by the boolean logic_and)
+ * the bits in mask.
+ *
+ * The slot parameter is here for future purposes only.
+ */
+int yk_wait_for_key_status(YK_KEY *yk, uint8_t slot, unsigned int flags,
+                          unsigned int max_time_ms,
+                          bool logic_and, unsigned char mask,
+                          unsigned char *last_data)
+{
+       unsigned char data[FEATURE_RPT_SIZE];
+       unsigned int bytes_read;
+
+       int sleepval = 10;
+       int slept_time = 0;
+       int blocking = 0;
+
+       while (slept_time < max_time_ms) {
+               /* Read a status report from the key */
+               memset(data, 0, sizeof(data));
+               if (!_ykusb_read(yk, REPORT_TYPE_FEATURE, slot, (char *) &data, FEATURE_RPT_SIZE))
+                       return 0;
+
+               if (last_data != NULL)
+                       memcpy(last_data, data, sizeof(data));
+
+               /* The status byte from the key is now in last byte of data */
+               if (logic_and) {
+                       /* Check if Yubikey has SET the bit(s) in mask */
+                       if ((data[FEATURE_RPT_SIZE - 1] & mask) == mask) {
+                               return 1;
+                       }
+               } else {
+                       /* Check if Yubikey has CLEARED the bit(s) in mask */
+                       if (! (data[FEATURE_RPT_SIZE - 1] & mask)) {
+                               return 1;
+                       }
+               }
+
+               /* Check if Yubikey says it will wait for user interaction */
+               if ((data[FEATURE_RPT_SIZE - 1] & RESP_TIMEOUT_WAIT_FLAG) == RESP_TIMEOUT_WAIT_FLAG) {
+                       if ((flags & YK_FLAG_MAYBLOCK) == YK_FLAG_MAYBLOCK) {
+                               if (! blocking) {
+                                       /* Extend timeout first time we see RESP_TIMEOUT_WAIT_FLAG. */
+                                       blocking = 1;
+                                       max_time_ms += 15000;
+                               }
+                       } else {
+                               /* Reset read mode of Yubikey before aborting. */
+                               yk_force_key_update(yk);
+                               yk_errno = YK_EWOULDBLOCK;
+                               return 0;
+                       }
+               } else {
+                       if (blocking) {
+                               /* YubiKey timed out waiting for user interaction */
+                               break;
+                       }
+               }
+
+               Sleep(sleepval);
+               slept_time += sleepval;
+               /* exponential backoff, up to 500 ms */
+               sleepval *= 2;
+               if (sleepval > 500)
+                       sleepval = 500;
+       }
+
+       yk_errno = YK_ETIMEOUT;
+       return 0;
+}
+
+/* Read one or more feature reports from a Yubikey and put them together.
+ *
+ * Bufsize must be able to hold at least 2 more bytes than you are expecting
+ * (the CRC), but since all read requests return 7 bytes of data bufsize needs
+ * to be up to 7 bytes more than you expect.
+ *
+ * If the key returns more data than bufsize, we fail and set yk_errno to
+ * YK_EWRONGSIZ. If that happens there will be partial data in buf.
+ *
+ * If we read a response from a Yubikey that is configured to block and wait for
+ * a button press (in challenge response), this function will abort unless
+ * flags contain YK_FLAG_MAYBLOCK, in which case it might take up to 15 seconds
+ * for this function to return.
+ *
+ * The slot parameter is here for future purposes only.
+ */
+int yk_read_response_from_key(YK_KEY *yk, uint8_t slot, unsigned int flags,
+                             void *buf, unsigned int bufsize, unsigned int expect_bytes,
+                             unsigned int *bytes_read)
+{
+       unsigned char data[FEATURE_RPT_SIZE];
+       memset(data, 0, sizeof(data));
+
+       memset(buf, 0, bufsize);
+       *bytes_read = 0;
+
+       /* Wait for the key to turn on RESP_PENDING_FLAG */
+       if (! yk_wait_for_key_status(yk, slot, flags, 1000, true, RESP_PENDING_FLAG, (char *) &data))
+               return 0;
+
+       /* The first part of the response was read by yk_wait_for_key_status(). We need
+        * to copy it to buf.
+        */
+       memcpy(buf + *bytes_read, data, sizeof(data) - 1);
+       *bytes_read += sizeof(data) - 1;
+
+       while (*bytes_read + FEATURE_RPT_SIZE <= bufsize) {
+               memset(data, 0, sizeof(data));
+
+               if (!_ykusb_read(yk, REPORT_TYPE_FEATURE, 0, (char *)data, FEATURE_RPT_SIZE))
+                       return 0;
+
+               if (data[FEATURE_RPT_SIZE - 1] & RESP_PENDING_FLAG) {
+                       /* The lower five bits of the status byte has the response sequence
+                        * number. If that gets reset to zero we are done.
+                        */
+                       if ((data[FEATURE_RPT_SIZE - 1] & 31) == 0) {
+                               if (expect_bytes > 0) {
+                                       /* Size of response is known. Verify CRC. */
+                                       int crc = yubikey_crc16(buf, expect_bytes + 2);
+                                       if (crc != YK_CRC_OK_RESIDUAL) {
+                                               yk_errno = YK_ECHECKSUM;
+                                               return 0;
+                                       }
+                               }
+
+                               /* Reset read mode of Yubikey before returning. */
+                               yk_force_key_update(yk);
+
+                               return 1;
+                       }
+
+                       memcpy(buf + *bytes_read, data, sizeof(data) - 1);
+                       *bytes_read += sizeof(data) - 1;
+               } else {
+                       /* Reset read mode of Yubikey before returning. */
+                       yk_force_key_update(yk);
+
+                       return 0;
+               }
+       }
+
+       /* We're out of buffer space, abort reading */
+       yk_force_key_update(yk);
+
+       yk_errno = YK_EWRONGSIZ;
+       return 0;
+}
+
 int yk_write_to_key(YK_KEY *yk, uint8_t slot, const void *buf, int bufcount)
 {
        YK_FRAME frame;
@@ -273,6 +433,12 @@ int yk_write_to_key(YK_KEY *yk, uint8_t slot, const void *buf, int bufcount)
        ptr = (unsigned char *) &frame;
        end = (unsigned char *) &frame + sizeof(frame);
 
+       /* Initial check that the YubiKey is in a state where it will accept
+        * a write.
+        *
+       if (! yk_wait_for_key_status(yk, slot, 0, 1000, false, SLOT_WRITE_FLAG, NULL))
+               return 0;
+       */
        for (seq = 0; ptr < end; seq++) {
                int all_zeros = 1;
                /* Ignore parts that are all zeroes except first and last
@@ -287,29 +453,17 @@ int yk_write_to_key(YK_KEY *yk, uint8_t slot, const void *buf, int bufcount)
                /* sequence number goes into lower bits of last byte */
                repbuf[i] = seq | SLOT_WRITE_FLAG;
 
-               if (!_ykusb_write(yk, REPORT_TYPE_FEATURE, 0,
-                                 (char *)repbuf, FEATURE_RPT_SIZE))
-                       return 0;
-
                /* When the Yubikey clears the SLOT_WRITE_FLAG, the
-                  next part can be sent */
-
-               for (i = 0; i < 50; i++) {
-                       memset(repbuf, 0, sizeof(repbuf));
-                       if (!_ykusb_read(yk, REPORT_TYPE_FEATURE, 0,
-                                        (char *)repbuf, FEATURE_RPT_SIZE))
-                               return 0;
-                       if (! (repbuf[FEATURE_RPT_SIZE - 1] & SLOT_WRITE_FLAG))
-                               break;
-                       Sleep(10);
-               }
-
-               /* If timeout, something has gone wrong */
+                * next part can be sent. Yubikey low-level interface
+                * section 2.4 (Report arbitration polling) specifies
+                * a 600 ms timeout for this operation.
+                */
+               if (! yk_wait_for_key_status(yk, slot, 0, 600, false, SLOT_WRITE_FLAG, NULL))
+                       return 0;
 
-               if (i >= 50) {
-                       yk_errno = YK_ETIMEOUT;
+               if (!_ykusb_write(yk, REPORT_TYPE_FEATURE, 0,
+                                 (char *)repbuf, FEATURE_RPT_SIZE))
                        return 0;
-               }
        }
 
        return 1;
@@ -346,3 +500,15 @@ uint16_t yk_endian_swap_16(uint16_t x)
        return x;
 }
 
+/* Private little hexdump function for debugging */
+void _yk_hexdump(void *buffer, int size)
+{
+       unsigned char *p = buffer;
+       int i;
+       for (i = 0; i < size; i++) {
+               fprintf(stderr, "\\x%02x", *p);
+               p++;
+      }
+      fprintf(stderr, "\n");
+      fflush(stderr);
+}
index 270dded1d15ef935501b05e522cce74ced49b847..305a9ecfb5788a800b3241012fb71858e07d2999 100644 (file)
@@ -117,7 +117,16 @@ const char *yk_usb_strerror();
 #define YK_ENOKEY      0x05
 #define YK_EFIRMWARE   0x06
 #define YK_ENOMEM      0x07
-#define YK_ENOSTATUS   0x07
-#define YK_ENOTYETIMPL 0x08
+#define YK_ENOSTATUS   0x08
+#define YK_ENOTYETIMPL 0x09
+#define YK_ECHECKSUM   0x0a    /* checksum validation failed */
+#define YK_EWOULDBLOCK 0x0b    /* operation would block */
+
+/* Flags for response reading. Use high numbers to not exclude the possibility
+ * to combine these with for example SLOT commands from ykdef.h in the future.
+ */
+#define YK_FLAG_MAYBLOCK       0x01 << 16
+
+#define YK_CRC_OK_RESIDUAL     0xf0b8
 
 #endif /* __YKCORE_H_INCLUDED__ */