"""
helper functions to work with OATH TOTP (RFC6238) OTP's and YubiHSM
"""

# Copyright (c) 2016 Storedsafe AB
# Copyright (c) 2011 Yubico AB
# See the file COPYING for licence statement.

import string
import struct
import datetime
import time

__all__ = [
    # constants
    # functions
    'validate_oath_totp_with_aead',
    # classes
 ]

import pyhsm.exception
import pyhsm.aead_cmd

def search_for_oath_code(hsm, key_handle, nonce, aead, counter, user_code, interval=30):
    """
    Try to validate an OATH TOTP OTP generated by a token whose secret key is
    available to the YubiHSM through the AEAD.

    The parameter `aead' is either a string, or an instance of YHSM_GeneratedAEAD.

    Returns timecounter value on successful auth, and None otherwise.
    """
    key_handle = pyhsm.util.input_validate_key_handle(key_handle)
    nonce = pyhsm.util.input_validate_nonce(nonce, pad = False)
    aead = pyhsm.util.input_validate_aead(aead)
    """ counter will not be used since we validate against time instead of counter """
    counter = pyhsm.util.input_validate_int(counter, 'counter')
    user_code = pyhsm.util.input_validate_int(user_code, 'user_code')
    hsm.load_temp_key(nonce, key_handle, aead)

    timecounter = timecode(datetime.datetime.now(), interval)
    secret = struct.pack("> Q", timecounter)
    hmac_result = hsm.hmac_sha1(pyhsm.defines.YSM_TEMP_KEY_HANDLE, secret).get_hash()
    this_code = truncate(hmac_result)
    if this_code == user_code:
        return timecounter 
    return None

def timecode(time_now, interval):
    """ make integer and divide by time interval of valid OTP """
    i = time.mktime(time_now.timetuple())
    return int(i / interval)


def truncate(hmac_result, length=6):
    """ Perform the truncating. """
    assert(len(hmac_result) == 20)
    offset   =  ord(hmac_result[19]) & 0xf
    bin_code = (ord(hmac_result[offset]) & 0x7f) << 24 \
        | (ord(hmac_result[offset+1]) & 0xff) << 16 \
        | (ord(hmac_result[offset+2]) & 0xff) <<  8 \
        | (ord(hmac_result[offset+3]) & 0xff)
    return bin_code % (10 ** length)
