From d636913e56da49650bd5cd5531b4db3419eef31d Mon Sep 17 00:00:00 2001 From: Fredrik Thulin Date: Mon, 31 Jan 2011 13:44:06 +0100 Subject: [PATCH] Make ykp_AES_key_from_passphrase() handle 160 bit keys too. --- tests/Makefile.am | 2 +- tests/test_key_generation.c | 96 +++++++++++++++++++++++++++++++++++++ ykpers.c | 65 ++++++++++++++++++++----- 3 files changed, 149 insertions(+), 14 deletions(-) create mode 100644 tests/test_key_generation.c diff --git a/tests/Makefile.am b/tests/Makefile.am index ece6e63..9f07a1f 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -33,7 +33,7 @@ LDADD = ../libykpers-1.la ykpersonalize_LDADD = ../libykpers-1.la ../ykcore/libykcore.la -ctests = selftest$(EXEEXT) test_args_to_config$(EXEEXT) +ctests = selftest$(EXEEXT) test_args_to_config$(EXEEXT) test_key_generation$(EXEEXT) check_PROGRAMS = $(ctests) TESTS = $(ctests) diff --git a/tests/test_key_generation.c b/tests/test_key_generation.c new file mode 100644 index 0000000..57c226d --- /dev/null +++ b/tests/test_key_generation.c @@ -0,0 +1,96 @@ +/* -*- mode:C; c-file-style: "bsd" -*- */ +/* + * Copyright (c) 2011, Yubico AB + * All rights reserved. + * + * 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 +#include +#include + +#include +#include + +void _test_128_bits_key(YKP_CONFIG *ykp, struct config_st *cfg) +{ + unsigned char empty[256]; + + memset (empty, 0, sizeof(empty)); + memset (cfg, 0, sizeof(cfg)); + cfg->tktFlags = TKTFLAG_APPEND_CR; + + ykp_AES_key_from_passphrase(ykp, "test", "ABCDEF"); + + /* make sure config.key now has non-zero bytes in it */ + assert(memcmp(cfg->key, empty, sizeof(cfg->key)) != 0); + /* make sure config.uid is still zero for 128 bits config */ + assert(memcmp(cfg->uid, empty, sizeof(cfg->uid)) == 0); +} + +void _test_160_bits_key(YKP_CONFIG *ykp, struct config_st *cfg) +{ + unsigned char empty[256]; + + memset (empty, 0, sizeof(empty)); + memset (cfg, 0, sizeof(cfg)); + cfg->tktFlags = TKTFLAG_APPEND_CR | TKTFLAG_OATH_HOTP; + + ykp_AES_key_from_passphrase(ykp, "test", "ABCDEF"); + + /* make sure config.key now has non-zero bytes in it */ + assert(memcmp(cfg->key, empty, sizeof(cfg->key)) != 0); + /* make sure config.uid is NOT zero for 160 bits config */ + assert(memcmp(cfg->uid, empty, sizeof(cfg->uid)) != 0); +} + +int main (void) +{ + YKP_CONFIG *ykp; + struct config_st *ycfg; + int rc; + + ykp = ykp_create_config (); + if (!ykp) + { + printf ("ykp_create_config returned NULL\n"); + return 1; + } + + ycfg = (struct config_st *) ykp_core_config(ykp); + + _test_128_bits_key(ykp, ycfg); + _test_160_bits_key(ykp, ycfg); + + rc = ykp_free_config(ykp); + if (!rc) + { + printf ("ykp_free_config => %d\n", rc); + return 1; + } + + return 0; +} diff --git a/ykpers.c b/ykpers.c index b5c88cd..5a1a718 100644 --- a/ykpers.c +++ b/ykpers.c @@ -1,6 +1,6 @@ /* -*- mode:C; c-file-style: "bsd" -*- */ /* - * Copyright (c) 2008, 2009, 2010, Yubico AB + * Copyright (c) 2008, 2009, 2010, 2011 Yubico AB * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,6 +38,7 @@ #include #include #include +#include #include @@ -126,6 +127,27 @@ int ykp_configure_for(YKP_CONFIG *cfg, int confnum, YK_STATUS *st) return 0; } +/* Return number of bytes of key data for this configuration. + * 20 bytes is 160 bits, 16 bytes is 128. + */ +int _get_supported_key_length(const YKP_CONFIG *cfg) +{ + bool key_bits_in_uid = false; + + /* OATH-HOTP and HMAC-SHA1 challenge response support 20 byte (160 bits) + * keys, holding the last four bytes in the uid field. + */ + if ((cfg->ykcore_config.tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP) + return 20; + + if ((cfg->ykcore_config.tktFlags & TKTFLAG_CHAL_RESP) == TKTFLAG_CHAL_RESP && + (cfg->ykcore_config.cfgFlags & CFGFLAG_CHAL_HMAC) == CFGFLAG_CHAL_HMAC) { + return 20; + } + + return 16; +} + /* 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]; @@ -171,6 +193,13 @@ int ykp_HMAC_key_from_hex(YKP_CONFIG *cfg, const char *hexkey) { return 0; } +/* Generate an AES (128 bits) or HMAC (despite the function name) (160 bits) + * key from user entered input. + * + * Use user provided salt, or use salt from an available random device. + * If no random device is available we fall back to using 2048 bits of + * system time data, together with the user input, as salt. + */ int ykp_AES_key_from_passphrase(YKP_CONFIG *cfg, const char *passphrase, const char *salt) { @@ -184,6 +213,11 @@ int ykp_AES_key_from_passphrase(YKP_CONFIG *cfg, const char *passphrase, char **random_place; uint8_t _salt[8]; size_t _salt_len = 0; + unsigned char buf[sizeof(cfg->ykcore_config.key) + 4]; + int rc; + int key_bytes = _get_supported_key_length(cfg); + + assert (key_bytes <= sizeof(buf)); if (salt) { _salt_len = strlen(salt); @@ -228,11 +262,22 @@ int ykp_AES_key_from_passphrase(YKP_CONFIG *cfg, const char *passphrase, _salt_len = sizeof(_salt); } - return yk_pbkdf2(passphrase, - _salt, _salt_len, - 1024, - cfg->ykcore_config.key, sizeof(cfg->ykcore_config.key), - &yk_hmac_sha1); + rc = yk_pbkdf2(passphrase, + _salt, _salt_len, + 1024, + buf, key_bytes, + &yk_hmac_sha1); + + if (rc) { + memcpy(cfg->ykcore_config.key, buf, sizeof(cfg->ykcore_config.key)); + + if (key_bytes == 20) { + memcpy(cfg->ykcore_config.uid, buf + sizeof(cfg->ykcore_config.key), 4); + } + } + + memset (buf, 0, sizeof(buf)); + return rc; } return 0; } @@ -466,13 +511,7 @@ int ykp_write_config(const YKP_CONFIG *cfg, /* for OATH-HOTP and HMAC-SHA1 challenge response, there is four bytes * additional key data in the uid field */ - if ((cfg->ykcore_config.tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP) - key_bits_in_uid = true; - - if ((cfg->ykcore_config.tktFlags & TKTFLAG_CHAL_RESP) == TKTFLAG_CHAL_RESP && - (cfg->ykcore_config.cfgFlags & CFGFLAG_CHAL_HMAC) == CFGFLAG_CHAL_HMAC) { - key_bits_in_uid = true; - } + key_bits_in_uid = (_get_supported_key_length(cfg) == 20); /* fixed: */ writer(str_fixed, strlen(str_fixed), userdata); -- 2.39.5