diff --git a/gnutls.c b/gnutls.c index 32159fd5..e97789c4 100644 --- a/gnutls.c +++ b/gnutls.c @@ -2597,10 +2597,26 @@ int hotp_hmac(struct openconnect_info *vpninfo, const void *challenge) int ret; int hpos; unsigned char hash[64]; /* Enough for a SHA256 */ + gnutls_mac_algorithm_t alg; + + switch(vpninfo->oath_hmac_alg) { + case OATH_ALG_HMAC_SHA1: + alg = GNUTLS_MAC_SHA1; + break; + case OATH_ALG_HMAC_SHA256: + alg = GNUTLS_MAC_SHA256; + break; + case OATH_ALG_HMAC_SHA512: + alg = GNUTLS_MAC_SHA512; + break; + default: + vpn_progress(vpninfo, PRG_ERR, + _("Unsupported OATH HMAC algorithm\n")); + return -EINVAL; + } hpos = 19; - ret = gnutls_hmac_fast(GNUTLS_MAC_SHA1, - vpninfo->oath_secret, + ret = gnutls_hmac_fast(alg, vpninfo->oath_secret, vpninfo->oath_secret_len, challenge, 8, hash); if (ret) { diff --git a/oath.c b/oath.c index 39f16ad7..7015ba4a 100644 --- a/oath.c +++ b/oath.c @@ -226,7 +226,22 @@ int set_totp_mode(struct openconnect_info *vpninfo, const char *token_str) ret = pskc_decode(vpninfo, token_str, toklen, OC_TOKEN_MODE_TOTP); if (ret) return -EINVAL; - } else if (strncasecmp(token_str, "base32:", strlen("base32:")) == 0) { + vpninfo->token_mode = OC_TOKEN_MODE_TOTP; + return 0; + } + if (!strncasecmp(token_str, "sha1:", 5)) { + token_str += 5; + vpninfo->oath_hmac_alg = OATH_ALG_HMAC_SHA1; + } else if (!strncasecmp(token_str, "sha256:", 7)) { + token_str += 7; + vpninfo->oath_hmac_alg = OATH_ALG_HMAC_SHA256; + } else if (!strncasecmp(token_str, "sha512:", 7)) { + token_str += 7; + vpninfo->oath_hmac_alg = OATH_ALG_HMAC_SHA256; + } else + vpninfo->oath_hmac_alg = OATH_ALG_HMAC_SHA1; + + if (strncasecmp(token_str, "base32:", strlen("base32:")) == 0) { ret = decode_base32(vpninfo, token_str + strlen("base32:"), toklen - strlen("base32:")); if (ret) @@ -263,6 +278,19 @@ int set_hotp_mode(struct openconnect_info *vpninfo, const char *token_str) vpninfo->token_mode = OC_TOKEN_MODE_HOTP; return 0; } + + if (!strncasecmp(token_str, "sha1:", 5)) { + token_str += 5; + vpninfo->oath_hmac_alg = OATH_ALG_HMAC_SHA1; + } else if (!strncasecmp(token_str, "sha256:", 7)) { + token_str += 7; + vpninfo->oath_hmac_alg = OATH_ALG_HMAC_SHA256; + } else if (!strncasecmp(token_str, "sha512:", 7)) { + token_str += 7; + vpninfo->oath_hmac_alg = OATH_ALG_HMAC_SHA256; + } else + vpninfo->oath_hmac_alg = OATH_ALG_HMAC_SHA1; + p = strrchr(token_str, ','); if (p) { long counter; diff --git a/openconnect-internal.h b/openconnect-internal.h index 558bc432..9603290f 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -406,6 +406,11 @@ struct openconnect_info { #endif char *oath_secret; size_t oath_secret_len; + enum { + OATH_ALG_HMAC_SHA1 = 0, + OATH_ALG_HMAC_SHA256, + OATH_ALG_HMAC_SHA512, + } oath_hmac_alg; enum { HOTP_SECRET_BASE32 = 1, HOTP_SECRET_RAW, diff --git a/openssl.c b/openssl.c index 47a4005c..0434e783 100644 --- a/openssl.c +++ b/openssl.c @@ -1672,8 +1672,24 @@ int hotp_hmac(struct openconnect_info *vpninfo, const void *challenge) { unsigned char hash[64]; /* Enough for a SHA256 */ unsigned int hashlen = sizeof(hash); + const EVP_MD *alg; - if (!HMAC(EVP_sha1(), vpninfo->oath_secret, vpninfo->oath_secret_len, + switch(vpninfo->oath_hmac_alg) { + case OATH_ALG_HMAC_SHA1: + alg = EVP_sha1(); + break; + case OATH_ALG_HMAC_SHA256: + alg = EVP_sha256(); + break; + case OATH_ALG_HMAC_SHA512: + alg = EVP_sha512(); + break; + default: + vpn_progress(vpninfo, PRG_ERR, + _("Unsupported OATH HMAC algorithm\n")); + return -EINVAL; + } + if (!HMAC(alg, vpninfo->oath_secret, vpninfo->oath_secret_len, challenge, 8, hash, &hashlen)) { vpninfo->progress(vpninfo, PRG_ERR, _("Failed to calculate OATH HMAC\n")); diff --git a/www/building.xml b/www/building.xml index a08b97fc..bcee63b9 100644 --- a/www/building.xml +++ b/www/building.xml @@ -33,7 +33,7 @@ And optionally also:
OpenConnect supports the use of HTTP and SOCKS proxies to connect to the diff --git a/www/changelog.xml b/www/changelog.xml index 909f6f8f..8ca51506 100644 --- a/www/changelog.xml +++ b/www/changelog.xml @@ -15,6 +15,8 @@
OATH HOTP/TOTP tokens are also supported in hardware by:
The default HMAC algorithm for TOTP tokens is SHA-1. SHA-256 and +SHA-512 are also supported; to use them prefix "sha256:" or +"sha512:" when explicitly providing a key on the command line. +Algorithms other than SHA-1 are not yet supported with PSKC files until +the relevant standards have been updated to indicate how they shall be +indicated in the PSKC file. See this erratum to RFC6238 for current status.
+ + +HOTP tokens are very similar to TOTP tokens except that they are event-based, and @@ -165,6 +178,8 @@ configuration. So if you configure a VPN connection with a HOTP token secret of and authenticate once, you should be able to go back into the configuration and see that the token secret has been updated to "0x1234,2".
+HOTP tokens also support SHA-256 and SHA-512 in precisely the same +fashion as TOTP tokens, as described above.
The ykneo-oath applet