Skip to content

Commit

Permalink
Calculate TOTP/HOTP codes for ourselves
Browse files Browse the repository at this point in the history
No released version of liboath supports SHA256/SHA512. At this point it
looks like more trouble than it's worth, since generating an HMAC is
fairly trivial and we already do it in various places already.

This is simpler than adding conditional support for the new function
that liboath will eventually add to allow us to specify the HMAC
algorithm we want it to use.

(SHA1 only for now; we need to work out how the user asks for it too)

Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
  • Loading branch information
David Woodhouse authored and David Woodhouse committed Jan 30, 2015
1 parent 3e097d5 commit 554454b
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 27 deletions.
22 changes: 22 additions & 0 deletions gnutls.c
Expand Up @@ -2591,3 +2591,25 @@ int openconnect_yubikey_chalresp(struct openconnect_info *vpninfo,
return 0;
}
#endif

int hotp_hmac(struct openconnect_info *vpninfo, const void *challenge)
{
int ret;
int hpos;
unsigned char hash[64]; /* Enough for a SHA256 */

hpos = 19;
ret = gnutls_hmac_fast(GNUTLS_MAC_SHA1,
vpninfo->oath_secret,
vpninfo->oath_secret_len,
challenge, 8, hash);
if (ret) {
vpninfo->progress(vpninfo, PRG_ERR,
_("Failed to calculate OATH HMAC: %s\n"),
gnutls_strerror(ret));
return -EINVAL;
}

hpos = hash[hpos] & 15;
return load_be32(&hash[hpos]) & 0x7fffffff;
}
53 changes: 26 additions & 27 deletions oath.c
Expand Up @@ -239,7 +239,7 @@ int can_gen_totp_code(struct openconnect_info *vpninfo,
} else if (vpninfo->token_tries == 1) {
vpn_progress(vpninfo, PRG_DEBUG,
_("OK to generate NEXT tokencode\n"));
vpninfo->token_time += OATH_TOTP_DEFAULT_TIME_STEP_SIZE;
vpninfo->token_time += 30;
} else {
/* limit the number of retries, to avoid account lockouts */
vpn_progress(vpninfo, PRG_INFO,
Expand Down Expand Up @@ -272,30 +272,41 @@ int can_gen_hotp_code(struct openconnect_info *vpninfo,
return 0;
}

static int gen_hotp(struct openconnect_info *vpninfo, uint64_t data, char *output)
{
uint32_t data_be[2];
int digest;

data_be[0] = htonl(data >> 32);
data_be[1] = htonl(data);

digest = hotp_hmac(vpninfo, data_be);
if (digest < 0)
return digest;

digest %= 1000000;
snprintf(output, 7, "%d", digest);

return 0;
}

int do_gen_totp_code(struct openconnect_info *vpninfo,
struct oc_auth_form *form,
struct oc_form_opt *opt)
{
int oath_err;
char tokencode[7];
uint64_t challenge;

if (!vpninfo->token_time)
vpninfo->token_time = time(NULL);

vpn_progress(vpninfo, PRG_INFO, _("Generating OATH TOTP token code\n"));

oath_err = oath_totp_generate(vpninfo->oath_secret,
vpninfo->oath_secret_len,
vpninfo->token_time,
OATH_TOTP_DEFAULT_TIME_STEP_SIZE,
OATH_TOTP_DEFAULT_START_TIME,
6, tokencode);
if (oath_err != OATH_OK) {
vpn_progress(vpninfo, PRG_ERR,
_("Unable to generate OATH TOTP token code: %s\n"),
oath_strerror(oath_err));
/* XXX: Support non-standard start time and step size */
challenge = vpninfo->token_time / 30;

if (gen_hotp(vpninfo, challenge, tokencode))
return -EIO;
}

vpninfo->token_tries++;
opt->_value = strdup(tokencode);
Expand Down Expand Up @@ -392,7 +403,6 @@ int do_gen_hotp_code(struct openconnect_info *vpninfo,
struct oc_auth_form *form,
struct oc_form_opt *opt)
{
int oath_err;
char tokencode[7];
int ret;

Expand All @@ -405,20 +415,9 @@ int do_gen_hotp_code(struct openconnect_info *vpninfo,
if (ret)
return ret;
}

oath_err = oath_hotp_generate(vpninfo->oath_secret,
vpninfo->oath_secret_len,
vpninfo->token_time,
6, false, OATH_HOTP_DYNAMIC_TRUNCATION,
tokencode);
if (oath_err != OATH_OK) {
vpn_progress(vpninfo, PRG_ERR,
_("Unable to generate OATH HOTP token code: %s\n"),
oath_strerror(oath_err));
if (vpninfo->unlock_token)
vpninfo->unlock_token(vpninfo->tok_cbdata, NULL);
if (gen_hotp(vpninfo, vpninfo->token_time, tokencode))
return -EIO;
}

vpninfo->token_time++;
vpninfo->token_tries++;
opt->_value = strdup(tokencode);
Expand Down
1 change: 1 addition & 0 deletions openconnect-internal.h
Expand Up @@ -832,6 +832,7 @@ int openconnect_yubikey_chalresp(struct openconnect_info *vpninfo,
const void *challenge, int chall_len, void *result);
int openconnect_hash_yubikey_password(struct openconnect_info *vpninfo,
const char *password, const void *ident, int id_len);
int hotp_hmac(struct openconnect_info *vpninfo, const void *challenge);
#if defined(OPENCONNECT_OPENSSL)
#define openconnect_https_connected(_v) ((_v)->https_ssl)
#elif defined (OPENCONNECT_GNUTLS)
Expand Down
17 changes: 17 additions & 0 deletions openssl.c
Expand Up @@ -1667,3 +1667,20 @@ int openconnect_yubikey_chalresp(struct openconnect_info *vpninfo,
return 0;
}
#endif

int hotp_hmac(struct openconnect_info *vpninfo, const void *challenge)
{
unsigned char hash[64]; /* Enough for a SHA256 */
unsigned int hashlen = sizeof(hash);

if (!HMAC(EVP_sha1(), vpninfo->oath_secret, vpninfo->oath_secret_len,
challenge, 8, hash, &hashlen)) {
vpninfo->progress(vpninfo, PRG_ERR,
_("Failed to calculate OATH HMAC\n"));
openconnect_report_ssl_errors(vpninfo);
return -EINVAL;
}

hashlen = hash[hashlen - 1] & 15;
return load_be32(&hash[hashlen]) & 0x7fffffff;
}

0 comments on commit 554454b

Please sign in to comment.