From 929d35ed08eebd8ca8bcd884ceb26b3faf23f73c Mon Sep 17 00:00:00 2001
From: David Woodhouse
Date: Fri, 30 Jan 2015 20:16:34 +0000
Subject: [PATCH] Support SHA256/SHA512 for OATH
Signed-off-by: David Woodhouse
---
gnutls.c | 20 ++++++++++++++++++--
oath.c | 30 +++++++++++++++++++++++++++++-
openconnect-internal.h | 5 +++++
openssl.c | 18 +++++++++++++++++-
www/building.xml | 2 +-
www/changelog.xml | 2 ++
www/features.xml | 2 +-
www/token.xml | 29 ++++++++++++++++++++++-------
8 files changed, 95 insertions(+), 13 deletions(-)
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:
libproxy
trousers (for TPM support if using GnuTLS)
libstoken (for SecurID software token support)
- liboath (for software HOTP/TOTP support)
+ libpskc (for RFC6030 PSKC file storage of HOTP/TOTP keys)
libpcsclite (for Yubikey hardware HOTP/HOTP support)
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 @@
- OpenConnect HEAD
+ - Add SHA256/SHA512 support for OATH.
+ - Remove liboath dependency.
- Preliminary support for Juniper SSL VPN.
diff --git a/www/features.xml b/www/features.xml
index 3ea03710..f03101ad 100644
--- a/www/features.xml
+++ b/www/features.xml
@@ -16,7 +16,7 @@
- Authentication via HTTP forms.
- Authentication using SSL certificates — from local file, Trusted Platform Module and PKCS#11 smartcards.
- Authentication using SecurID software tokens (when built with libstoken)
- - Authentication using OATH TOTP or HOTP software tokens (when built with liboath)
+ - Authentication using OATH TOTP or HOTP software tokens.
- Authentication using Yubikey OATH tokens (when built with libpcsclite)
- UserGroup support for selecting between multiple configurations on a single VPN server.
- Data transport over TCP (HTTPS) or UDP (DTLS).
diff --git a/www/token.xml b/www/token.xml
index 16b22ce5..c905fb7c 100644
--- a/www/token.xml
+++ b/www/token.xml
@@ -15,10 +15,8 @@ generating one-time passwords:
OATH HOTP/TOTP tokens are also supported in hardware by:
- ykneo-oath applet on
@@ -113,15 +111,21 @@ of a referenced file, or entered into the NetworkManager configuration dialog.
They may be specified in one of the following forms:
- - SecretSecret!
+ - SecretSecret!
+ - sha256:SecretSecret!
+ - sha512:SecretSecret!
For secrets which are actually UTF-8 strings instead of entirely randomly generated
data, they may be specified directly in this form.
- - 0x53656372657453656372657421
+ - 0x53656372657453656372657421
+ - sha256:0x53656372657453656372657421
+ - sha512:0x53656372657453656372657421
This is the hexadecimal form which (without the leading 0x) is
accepted by default by the
oathtool
program.
- - base32:KNSWG4TFORJWKY3SMV2CC===
+ - base32:KNSWG4TFORJWKY3SMV2CC===
+ - sha256:base32:KNSWG4TFORJWKY3SMV2CC===
+ - sha512:base32:KNSWG4TFORJWKY3SMV2CC===
This is the base32 form which is accepted by the
oathtool
program with its -b option..
@@ -130,6 +134,15 @@ They may be specified in one of the following forms:
These should be generally be imported from a file: '--token-secret @FILE.PSKC'
+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 (HMAC-Based One-Time Password)
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.
Yubikey HOTP/TOTP
The ykneo-oath applet