Ejemplo n.º 1
0
def parseSafeNetXML(xml):
    """
    This function parses XML data of a Aladdin/SafeNet XML
    file for eToken PASS

    It returns a dictionary of
        serial : { hmac_key , counter, type }
    """

    TOKENS = {}
    elem_tokencontainer = etree.fromstring(xml)

    if getTagName(elem_tokencontainer) != "Tokens":
        raise ImportException("No toplevel element Tokens")

    for elem_token in list(elem_tokencontainer):
        SERIAL = None
        COUNTER = None
        HMAC = None
        DESCRIPTION = None
        if getTagName(elem_token) == "Token":
            SERIAL = elem_token.get("serial")
            log.debug("Found token with serial %r", SERIAL)
            for elem_tdata in list(elem_token):
                tag = getTagName(elem_tdata)
                if "ProductName" == tag:
                    DESCRIPTION = elem_tdata.text
                    log.debug(
                        "The Token with the serial %s has the productname %s",
                        SERIAL,
                        DESCRIPTION,
                    )
                if "Applications" == tag:
                    for elem_apps in elem_tdata:
                        if getTagName(elem_apps) == "Application":
                            for elem_app in elem_apps:
                                tag = getTagName(elem_app)
                                if "Seed" == tag:
                                    HMAC = elem_app.text
                                if "MovingFactor" == tag:
                                    COUNTER = elem_app.text
            if not SERIAL:
                log.error("Found token without a serial")
            else:
                if HMAC:
                    hashlib = "sha1"
                    if len(HMAC) == 64:
                        hashlib = "sha256"

                    TOKENS[SERIAL] = {
                        "hmac_key": HMAC,
                        "counter": COUNTER,
                        "type": "HMAC",
                        "hashlib": hashlib,
                    }
                else:
                    log.error("Found token %s without a element 'Seed'",
                              SERIAL)

    return TOKENS
Ejemplo n.º 2
0
def getMacMethod(elem):
    meth = elem.get("Algorithm")
    m = re.search("\#(.*)$", meth)
    if m:
        meth = m.group(1)
    if "hmac-sha1" != meth:
        log.error("The method %s is not supported" % meth)
        raise ImportException("The method %s is not supported" % meth)
    return meth
Ejemplo n.º 3
0
def getEncMethod(elem):
    algo = elem.get("Algorithm")
    m = re.search("\#(.*)$", algo)
    if m:
        algo = m.group(1)
    if "aes128-cbc" != algo:
        log.error("The algorithm %s is not supported" % algo)
        raise ImportException("The algorithm %s is not supported" % algo)
    return algo
Ejemplo n.º 4
0
def parse_dat_data(data, d_string=None):
    '''
    this function is called from the web-ui and parses an eToken data file

    :param data: data from the web-ui file upload

    :return: It returns a dictionary of serial : { /admin/init parameters }
    '''

    from linotp.lib.ImportOTP  import ImportException

    #the result set
    tokens = {}


    try:
        startdate = parse_datetime(d_string)

        ## collect all token info in an array
        lines = []

        for line in data.splitlines():
            line = line.strip()

            ## if line is empty, we take all already defined lines and
            ## create an token out of it
            if len(line) == 0:
                token = create_token(lines, startdate)
                ## if we get a token, we can preserve this for
                ## later, to store them
                if token is not None:
                    LOG.info("Token parsed: %r" % token)
                    serial = token.serial
                    tokens[serial] = token.get_initparams()
                del lines[:]
            else:
                lines.append(line)

        ## if finally there are lines left, try to create an additional token
        if len(lines) != 0:
            token = create_token(lines, startdate)
            if token is not None:
                LOG.info("Token parsed: %r" % token)
                serial = token.serial
                tokens[serial] = token.get_initparams()
            del lines[:]

    except Exception as err:
        raise ImportException(err)

    return tokens
Ejemplo n.º 5
0
def parsePSKCdata(xml , preshared_key_hex=None, password=None,
                    do_checkserial=True,
                    do_feitian=False):
    '''
    This function parses XML data of a PSKC file, (RFC6030)
    It can read
    * AES-128-CBC encrypted (preshared_key_bin) data
    * password based encrypted data
    * plain text data

    It returns a dictionary of
        serial : { hmac_key , counter, .... }
    '''
    TAG_NAME_KEYPACKAGE = "KeyPackage"
    TAG_TOKEN_ID = "Id"
    # Feitian Fix
    if do_feitian:
        TAG_NAME_KEYPACKAGE = "Device"
        TAG_TOKEN_ID = "KeyId"
        do_checkserial = False


    TOKENS = {}
    elem_keycontainer = etree.fromstring(xml)
    ENCRYPTION_KEY_hex = preshared_key_hex

    if getTagName(elem_keycontainer).lower() != "keycontainer":
        raise ImportException("No toplevel element KeyContainer")

    tag = elem_keycontainer.tag
    match = re.match("^({.*?})Key[Cc]ontainer$", tag)
    namespace = ""
    if match:
        namespace = match.group(1)
        log.debug("Found namespace %s" % namespace)

    PSKC_VERSION = elem_keycontainer.get("Version")
    KEYNAME = None
    MACKEY_bin = None
    ENC_ALGO = None
    ENC_MODE = None

    PBE_DERIVE_ALGO = None
    PBE_SALT = None
    PBE_ITERATION_COUNT = None
    PBE_KEY_LENGTH = None

    # check for any encryption method 6.1, 6.2
    ### Do the Encryption Key
    elem_encKey = elem_keycontainer.find(namespace + "EncryptionKey")

    if elem_encKey:

        # Check for AES-128-CBC, preshared key (chapter 6.1)
        enckeyTag = getTagName(list(elem_encKey)[0])
        # This will hold the name of the preshared key
        if "KeyName" == enckeyTag:
            ENC_MODE = "AES128"
            KEYNAME = list(elem_encKey)[0].text
            log.debug("The keyname of preshared encryption is <<%s>>" % KEYNAME)
        # check for PasswordBasedEncyprion (chapter 6.2)
        elif "DerivedKey" == enckeyTag:
            ENC_MODE = "PBE"
            log.debug("We found PBE.")
            # Now we check for KeyDerivationMethod
            elem_keyderivation = list(list(elem_encKey)[0])
            for e in elem_keyderivation:
                if "KeyDerivationMethod" == getTagName(e):

                    deriv_algo = e.get("Algorithm")
                    m = re.search("\#(.*)$", deriv_algo)
                    PBE_DERIVE_ALGO = m.group(1)
                    log.debug("Algorithm of the PBE: %s" % PBE_DERIVE_ALGO)
                    if "pbkdf2" == PBE_DERIVE_ALGO:
                        for p in list(e):
                            if "PBKDF2-params" == getTagName(p):
                                for sp in list(p):
                                    spTag = getTagName(sp)
                                    if "Salt" == spTag:
                                        for salt in list(sp):
                                            if "Specified" == getTagName(salt):
                                                PBE_SALT = salt.text
                                            else:
                                                log.warning("Unknown element in element Salt: %s" % getTagName(salt))
                                    elif "IterationCount" == spTag:
                                        PBE_ITERATION_COUNT = sp.text
                                    elif "KeyLength" == spTag:
                                        PBE_KEY_LENGTH = sp.text
                    else:
                        # probably pbkdf1
                        log.error("We do not support key derivation method %s" % deriv_algo)
                        raise ImportException("We do not support key derivation method %s" % deriv_algo)
                log.debug("found the salt <<%s>>" % PBE_SALT)

            if password and len(password) > 5 and len(password) <= 64:
                log.debug("calculation encryption key from password [%s], salt: [%s] and length: [%s], count: [%s]" %
                    (password, PBE_SALT, PBE_KEY_LENGTH, PBE_ITERATION_COUNT))
                ENCRYPTION_KEY_bin = pbkdf2.pbkdf2(password.encode('ascii'), base64.b64decode(PBE_SALT),
                    int(PBE_KEY_LENGTH), int(PBE_ITERATION_COUNT))
                ENCRYPTION_KEY_hex = binascii.hexlify(ENCRYPTION_KEY_bin)
                log.debug("calculated encryption key: %s" % ENCRYPTION_KEY_hex)
            else:
                log.error("You must provide a password that is longer than 5 characters and up to 64 characters long.")
                raise ImportException("You must provide a password that is longer than 5 characters and up to 64 characters long.")


        ### Do the MAC Key
        # This will hold the MAC key
        macmethod = elem_keycontainer.find(namespace + "MACMethod")
        MAC_Method = getMacMethod(macmethod)
        elem_mackey = macmethod.find(namespace + "MACKey")


        # Find the MAC: ENC_ALGO and MAC_bin
        for e in list(elem_mackey):
            tag = getTagName(e)
            if "CipherData" == tag:
                for c in list(e):
                    cipher_tag = getTagName(c)
                    if "CipherValue" == cipher_tag:
                        cipherValue = c.text.strip()
                        log.debug("Found this MAC Key cipherValue: <<%s>>" % cipherValue)
                        MACKEY_bin = aes_decrypt(cipherValue, ENCRYPTION_KEY_hex)
                    else:
                        log.error("Found unsupported child in CipherData: %s" % cipher_tag)
                        raise ImportException("Found unsupported child in CipherData: %s" % cipher_tag)
            elif "EncryptionMethod" == tag:
                ENC_ALGO = getEncMethod(e)
            else:
                log.warning("Found unknown tag: %s" % tag)


    ## End of Encryption Key
    # There is a keypackage per key
    # Now we get the list of keypackages

    elem_KeyPackageList = elem_keycontainer.findall(namespace + TAG_NAME_KEYPACKAGE)
    if 0 == len(elem_KeyPackageList):
        raise ImportException("No element %s contained!" % TAG_NAME_KEYPACKAGE)

    # Now parsing all the keys
    for elem_package in elem_KeyPackageList:

        ### Do the keys

        elem_key = elem_package.find(namespace + "Key")

        serial = elem_key.get(TAG_TOKEN_ID)
        log.info("Processing token with serial (Key Id=%s)" % serial)

        elem_deviceInfo = elem_package.find(namespace + "DeviceInfo")
        if elem_deviceInfo:
            # Try to find the real serial number
            elem_serial = elem_deviceInfo.find(namespace + "SerialNo")
            serial = elem_serial.text
            log.info("Processing token with the real SerialNo %s" % serial)

        algorithm = elem_key.get("Algorithm")
        if algorithm:
            # <Key Id="12345678" Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:hotp">
            algorithm = algorithm.split(":")[-1]
        else:
            # Some draft say, this would be KeyAlgorithm
            algorithm = elem_key.get("KeyAlgorithm")
            # <Key KeyAlgorithm="http://www.ietf.org/keyprov/pskc#totp"
            algorithm = algorithm.split("#")[-1]

        TOKEN_TYPE = None

        if algorithm:
            if 'hotp' == algorithm.lower():
                TOKEN_TYPE = "hmac"
            elif 'totp' == algorithm.lower():
                TOKEN_TYPE = "totp"
            elif 'ocra' == algorithm.lower():
                TOKEN_TYPE = "ocra"

        if do_checkserial and not checkSerial(serial):
            log.warning("serial %s is not a valid OATH serial" % serial)
        else:
            # Now we do the Parameters, which can hold
            # the number of the digits :
            # <pskc:AlgorithmParameters>
            #   <Suite>OCRA-1:HOTP-SHA1-6:C-QN08-PSHA1</Suite>
            #   <pskc:ResponseFormat Length="6" Encoding="DECIMAL"/>
            # </pskc:AlgorithmParameters>
            KD_otplen = 6
            KD_hashlib = None
            KD_Suite = None
            elem_algoParam = elem_key.find(namespace + "AlgorithmParameters")
            for e in list(elem_algoParam):
                eTag = getTagName(e)
                log.debug("Evaluating element <<%s>>" % eTag)
                if "ResponseFormat" == eTag:
                    KD_otplen = int (e.get("Length"))
                    log.debug("Found length = %s" % e.get("Length"))
                elif "Suite" == eTag:
                    if TOKEN_TYPE == "ocra":
                        KD_Suite = e.text
                        log.debug("Found OCRA Suite = %s" % KD_Suite)
                    else:
                        # This can be HMAC-SHA256
                        KD_hashlib = e.text
                        if KD_hashlib.lower() == "hmac-sha256":
                            KD_hashlib = "sha256"
                        if KD_hashlib.lower() == "hmac-sha1":
                            KD_hashlib = "sha1"
                        log.debug("Found hashlib = %s" % KD_hashlib)


            # Now we do all the Key Data: <pskc:Data>
            elem_keydata = elem_key.find(namespace + "Data")
            # Parse through the data of the Key
            KD_hmac_key_b64 = None
            KD_cipher_b64 = None
            KD_mac_b64 = None
            KD_algo = None
            KD_counter = None
            KD_TimeInterval = None
            KD_TimeOffset = None
            for e in list(elem_keydata):
                eTag = getTagName(e)
                log.debug("Evaluating element <<%s>>" % eTag)
                if "Secret" == eTag:
                    for se in list(e):
                        seTag = getTagName(se)
                        if "EncryptedValue" == seTag:
                            for ev in list(se):
                                evTag = getTagName(ev)
                                if "EncryptionMethod" == evTag:
                                    KD_algo = getEncMethod(ev)
                                elif "CipherData" == evTag:
                                    for ciph in list(ev):
                                        ciphTag = getTagName(ciph)
                                        if "CipherValue" == ciphTag:
                                            KD_cipher_b64 = ciph.text.strip()


                        elif "PlainValue" == seTag:
                            KD_hmac_key_b64 = se.text.strip()
                        elif "ValueMAC" == seTag:
                            KD_mac_b64 = se.text.strip()

                elif "Counter" == eTag:
                    for se in list(e):
                        seTag = getTagName(se)
                        if "PlainValue" == seTag:
                            KD_counter = se.text
                        else:
                            log.warning("We do only support PlainValue counters")
                elif "TimeInterval" == eTag:
                    for se in list(e):
                        seTag = getTagName(se)
                        if "PlainValue" == seTag:
                            KD_TimeInterval = se.text
                            log.debug("Found TimeInterval = %s" % KD_TimeInterval)
                        else:
                            log.warning("We do only support PlainValue for TimeInterval")
                elif "Time" == eTag:
                    for se in list(e):
                        seTag = getTagName(se)
                        if "PlainValue" == seTag:
                            KD_Time = se.text
                            log.debug("Found Time offset = %s" % KD_Time)
                        else:
                            log.warning("We do only support PlainValue for Time")

                else:
                    log.warning("Unparsed Tag in Key: %s" % eTag)

            if KD_algo and KD_hmac_key_b64:
                log.warning("The key %s contained a secret with PlainValue and EncryptedValue!" % serial)
            else:
                if "aes128-cbc" == ENC_ALGO:
                    #
                    #   Verifiy the MAC Value
                    #
                    if "hmac-sha1" == MAC_Method:

                        MAC_digest_bin = hmac.new(MACKEY_bin, base64.b64decode(KD_cipher_b64), sha).digest()
                        MAC_digest_b64 = base64.b64encode(MAC_digest_bin)
                        log.debug("AES128-CBC secret cipher: %s" % KD_cipher_b64)
                        log.debug("calculated MAC value    : %s" % MAC_digest_b64)
                        log.debug("read MAC value          : %s" % KD_mac_b64)

                        # decrypt key
                        HMAC_KEY_bin = aes_decrypt(KD_cipher_b64, ENCRYPTION_KEY_hex, serial)

                        if MAC_digest_b64 == KD_mac_b64:
                            TOKENS[serial] = { 'hmac_key' : binascii.hexlify(HMAC_KEY_bin),
                                        'counter' : KD_counter, 'type' : TOKEN_TYPE,
                                        'timeStep' : KD_TimeInterval, 'otplen' : KD_otplen,
                                        'hashlib' : KD_hashlib,
                                        'ocrasuite' : KD_Suite }
                        else:
                            log.error("The MAC value for %s does not fit. The HMAC secrets could be compromised!" % serial)
                            raise ImportException("The MAC value for %s does not fit. The HMAC secrets could be compromised!" % serial)
                            #TOKENS[serial] = { 'hmac_key' : binascii.hexlify(HMAC_KEY_bin),
                            #            'counter' : KD_counter, 'type' : TOKEN_TYPE,
                            #            'timeStep' : KD_TimeInterval, 'otplen' : KD_otplen,
                            #            'hashlib' : KD_hashlib }
                    else:
                        log.warning("At the moment we only support hmac-sha1. We found %s" % MAC_Method)

                elif KD_hmac_key_b64:
                    TOKENS[serial] = { 'hmac_key' : binascii.hexlify(base64.b64decode(KD_hmac_key_b64)),
                                        'counter' : KD_counter, 'type' : TOKEN_TYPE,
                                        'timeStep' : KD_TimeInterval, 'otplen' : KD_otplen,
                                        'hashlib' : KD_hashlib,
                                        'ocrasuite' : KD_Suite  }
                else:
                    log.warning("neither a PlainValue nor an EncryptedValue was found for the secret of key %s" % serial)

    return TOKENS