]> err.no Git - yubikey-personalization/commitdiff
Add support for 40 bytes (160 bits) -a.
authorFredrik Thulin <fredrik@yubico.com>
Fri, 14 Jan 2011 16:22:46 +0000 (17:22 +0100)
committerFredrik Thulin <fredrik@yubico.com>
Fri, 14 Jan 2011 16:22:46 +0000 (17:22 +0100)
This is for OATH and HMAC challenge-response, introduced in Yubikey
2.1 and 2.2.

libykpers-1.map
tests/test_args_to_config.c
ykpers.c
ykpersonalize.1
ykpersonalize.c

index edd4d165a23f1b7bc3c75c1f42dcb2094f1af342..a3f17eb4d593f26b23e6cf048a3666b97496e889 100644 (file)
@@ -52,6 +52,7 @@ LIBYKPERS_1.0 {
     ykds_version_major;
     ykds_version_minor;
     ykp_AES_key_from_hex;
+    ykp_AES160_key_from_hex;
     ykp_AES_key_from_passphrase;
     ykp_config_num;
     ykp_configure_for;
index 43d85fed67cd5a2e57be9525777855f8c6906044..88a8c0846dd641d93201bc7c4ff617b0088414f4 100644 (file)
@@ -139,7 +139,7 @@ int _test_config_slot1()
        assert(cfg->yk_minor_version == 3);
 
        /* verify some specific flags */
-       ycfg = (struct config_st *) &cfg->ykcore_config;
+       ycfg = (struct config_st *) ykp_core_config(cfg);
        assert(ycfg->tktFlags == TKTFLAG_APPEND_CR);
 
        /* then check CRC against a known value to bulk check the rest */
@@ -147,7 +147,7 @@ int _test_config_slot1()
                                    offsetof(struct config_st, crc));
 
        if (ycfg->crc != 0xc046)
-               _yktest_hexdump ("NO-MATCH :\n", ycfg, 64, 8);
+               _yktest_hexdump ("NO-MATCH :\n", ycfg, sizeof(*ycfg), 8);
 
        assert(ycfg->crc == 0xc046);
 
@@ -176,7 +176,7 @@ int _test_config_static_slot2()
        assert(cfg->yk_minor_version == 0);
 
        /* verify some specific flags */
-       ycfg = (struct config_st *) &cfg->ykcore_config;
+       ycfg = (struct config_st *) ykp_core_config(cfg);
        assert(ycfg->tktFlags == TKTFLAG_APPEND_CR);
        assert(ycfg->cfgFlags == CFGFLAG_STATIC_TICKET | CFGFLAG_STRONG_PW1 | CFGFLAG_STRONG_PW2 | CFGFLAG_MAN_UPDATE);
 
@@ -185,7 +185,7 @@ int _test_config_static_slot2()
                                    offsetof(struct config_st, crc));
 
        if (ycfg->crc != 0xf5e9)
-               _yktest_hexdump ("NO-MATCH :\n", ycfg, 64, 8);
+               _yktest_hexdump ("NO-MATCH :\n", ycfg, sizeof(*ycfg), 8);
 
        assert(ycfg->crc == 0xf5e9);
 
@@ -284,6 +284,44 @@ int _test_non_config_args()
        free(st);
 }
 
+int _test_oath_hotp_nist_160_bits()
+{
+       YKP_CONFIG *cfg = ykp_create_config();
+       YK_STATUS *st = _test_init_st(2, 1, 0);
+       int rc = 0;
+       struct config_st *ycfg;
+
+       char *argv[] = {
+               "unittest", "-1", "-a303132333435363738393a3b3c3d3e3f40414243", "-ooath-hotp", "-o-append-cr",
+               NULL
+       };
+       int argc = sizeof argv/sizeof argv[0] - 1;
+
+       rc = _test_config(cfg, st, argc, argv);
+       assert(rc == 1);
+
+       /* verify required version for this config */
+       assert(cfg->yk_major_version == 2);
+       assert(cfg->yk_minor_version == 1);
+
+       /* verify some specific flags */
+       ycfg = (struct config_st *) ykp_core_config(cfg);
+       assert(ycfg->tktFlags == TKTFLAG_OATH_HOTP);
+       assert(ycfg->cfgFlags == 0);
+
+       /* then check CRC against a known value to bulk check the rest */
+       ycfg->crc = ~yubikey_crc16 ((unsigned char *) ycfg,
+                                   offsetof(struct config_st, crc));
+
+       if (ycfg->crc != 0xb96a)
+               _yktest_hexdump ("NO-MATCH :\n", ycfg, sizeof(*ycfg), 8);
+
+       assert(ycfg->crc == 0xb96a);
+
+       ykp_free_config(cfg);
+       free(st);
+}
+
 int main (int argc, char **argv)
 {
        _test_config_slot1();
@@ -291,6 +329,7 @@ int main (int argc, char **argv)
        _test_too_old_key();
        _test_too_new_key();
        _test_non_config_args();
+       _test_oath_hotp_nist_160_bits();
 
        return 0;
 }
index 132bda13b2f5116c7100ac73c02ea5a59a66f117..89e7f5c00e2aa63bff875867ebf853b2459eeebc 100644 (file)
--- a/ykpers.c
+++ b/ykpers.c
@@ -126,26 +126,62 @@ int ykp_configure_for(YKP_CONFIG *cfg, int confnum, YK_STATUS *st)
        return 0;
 }
 
+/* local helper function to check that a string contains only 0-9a-f */
+static bool is_valid_hexstr(const char *buf)
+{
+       int i;
+       for (i=0; i < strlen(buf); i++) {
+               char c = tolower(*(buf + i));
+               /* In ASCII, 0-9 == 48-57 and a-f == 97-102 */
+               if ( c<48 || (c>57 && c<97) || c>102 ) {
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+/* Decode 128 bit AES key into cfg->ykcore_config.key */
 int ykp_AES_key_from_hex(YKP_CONFIG *cfg, const char *hexkey) {
        char aesbin[256];
 
-/* Make sure that the hexkey is exactly 32 characters */
+       /* Make sure that the hexkey is exactly 32 characters */
        if (strlen(hexkey) != 32) {
                return 1;  /* Bad AES key */
        }
 
-/* Make sure that the hexkey is made up of only [0-9a-f] */
+       /* Make sure that the hexkey is made up of only [0-9a-f] */
+       if (! is_valid_hexstr(hexkey))
+               return 1;
+
+       yubikey_hex_decode(aesbin, hexkey, sizeof(aesbin));
+       memcpy(cfg->ykcore_config.key, aesbin, sizeof(cfg->ykcore_config.key));
+
+       return 0;
+}
+
+/* Decode 160 bits AES key, used with OATH and HMAC challenge-response.
+ *
+ * The first 128 bits of the AES go key into cfg->ykcore_config.key,
+ * and 32 bits into the first four bytes of cfg->ykcore_config.uid.
+*/
+int ykp_AES160_key_from_hex(YKP_CONFIG *cfg, const char *hexkey) {
+       char aesbin[256];
        int i;
-       for (i=0; i < strlen(hexkey); i++) {
-               char c = tolower(hexkey[i]);
-/* In ASCII, 0-9 == 48-57 and a-f == 97-102 */
-               if ( c<48 || (c>57 && c<97) || c>102 ) {
-                       return 1;
-               }
+
+       /* Make sure that the hexkey is exactly 40 characters */
+       if (strlen(hexkey) != 40) {
+               return 1;  /* Bad AES key */
        }
 
+       /* Make sure that the hexkey is made up of only [0-9a-f] */
+       if (! is_valid_hexstr(hexkey))
+               return 1;
+
        yubikey_hex_decode(aesbin, hexkey, sizeof(aesbin));
-       memcpy(cfg->ykcore_config.key, aesbin, sizeof(cfg->ykcore_config.key));
+       i = sizeof(cfg->ykcore_config.key);
+       memcpy(cfg->ykcore_config.key, aesbin, i);
+       memcpy(cfg->ykcore_config.uid, aesbin + i, 20 - i);
 
        return 0;
 }
index d310d499b809dcca8c71af83b138f94a22a534e3..2db6dfa1dafd6bace8a0649eb008ae7d50f912b1 100644 (file)
@@ -65,7 +65,7 @@ read configuration from file.
 (if file is -, read from stdin)
 .TP
 \fB\-a\fIxxx\fR
-A 32 char hex value (not modhex) of a fixed AES key to use.
+A 32 char (40 for OATH-HOTP and HMAC challenge-response) hex value (not modhex) of a fixed AES key to use.
 .TP
 \fB\-c\fIxxx\fR
 A 12 char hex value (not modhex) to use as access code for
@@ -202,16 +202,21 @@ When set, the first two bytes of the fixed part is sent as modhex.
 [\-]\fBoath-fixed-modhex\fR
 When set, the fixed part is sent as modhex.
 .SH OATH-HOTP Mode
-When using OATH-HOTP mode, the key that is shared with the server
-consists of the AES key plus the first four bytes (eight hex
-characters) of the UID.  The token identifier is defined by the fixed
-prefix.
+When using OATH-HOTP mode, an AES key of 160 bits (20 bytes, 40 chars of hex)
+can be supplied with -a.
+.PP
+The token identifier can be set with the -ofixed= option.
+See section "5.3.4 - OATH-HOTP Token Identifier" of the
+.URL "http://static.yubico.com/var/uploads/pdfs/YubiKey_Manual_2010-09-16.pdf" "Yubikey manual"
+for details, but in short the token identifier is 2 bytes manufacturer prefix,
+2 character token type and then 8 bytes manufacturer unique ID.
+
 .SH BUGS
 Report ykpersonalize bugs in 
 .URL "http://code.google.com/p/yubikey-personalization/issues/list" "the issue tracker"
 .SH "SEE ALSO"
 The 
 .URL "http://code.google.com/p/yubikey-personalization/" "ykpersonalize home page"
-.br
+.PP
 Yubikeys can be obtained from
 .URL "http://www.yubico.com/products/yubikey/" "Yubico" "."
index 2cdd447b5cfa772dd6882c123afb05954f36ad26..489edef281feb676b06238f7be7e872d6e3d9a99 100644 (file)
@@ -38,6 +38,7 @@
 
 #include <ykpers.h>
 #include <yubikey.h> /* To get yubikey_modhex_encode and yubikey_hex_encode */
+#include <ykdef.h>
 
 const char *usage =
 "Usage: ykpersonalize [options]\n"
@@ -53,8 +54,9 @@ const char *usage =
 "          (if FILE is -, send to stdout)\n"
 "-iFILE    read configuration from FILE.\n"
 "          (if FILE is -, read from stdin)\n"
-"-aXXX..   A 32 char hex value (not modhex) of a fixed AES key to use\n"
-"-cXXX..   A 12 char hex value to use as access code for programming\n"
+"-aXXX..   The AES secret key as a 32 (or 40 for OATH-HOTP/HMAC CHAL-RESP)\n"
+"          char hex value (not modhex)\n"
+"-cXXX..   A 12 char hex value (not modhex) to use as access code for programming\n"
 "          (this does NOT SET the access code, that's done with -oaccess=)\n"
 "-oOPTION  change configuration option.  Possible OPTION arguments are:\n"
 "          salt=ssssssss       Salt to be used when deriving key from a\n"
@@ -359,7 +361,29 @@ int args_to_config(int argc, char **argv, YKP_CONFIG *cfg,
        }
 
        if (*aesviahash) {
-               if (ykp_AES_key_from_hex(cfg, aeshash)) {
+               int long_key_valid = false;
+               struct config_st *ycfg;
+               int res = 0;
+
+               ycfg = (struct config_st *) ykp_core_config(cfg);
+               
+               /* for OATH-HOTP, 160 bits key is also valid */
+               if ((ycfg->tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP)
+                       long_key_valid = true;
+
+               /* for HMAC (not Yubico) challenge-response, 160 bits key is also valid */
+               if ((ycfg->tktFlags & TKTFLAG_CHAL_RESP) == TKTFLAG_CHAL_RESP &&
+                   (ycfg->cfgFlags & CFGFLAG_CHAL_HMAC) == CFGFLAG_CHAL_HMAC) {
+                       long_key_valid = true;
+               }
+
+               if (long_key_valid && strlen(aeshash) == 40) {
+                       res = ykp_AES160_key_from_hex(cfg, aeshash);
+               } else {
+                       res = ykp_AES_key_from_hex(cfg, aeshash);
+               }
+                       
+               if (res) {
                        fprintf(stderr, "Bad AES key: %s\n", aeshash);
                        fflush(stderr);
                        return 0;