Пример #1
0
 def parse_signature(cls, signature, signature_elm):
     """Read signature information from the <Signature> element."""
     if signature_elm is None:
         return
     cm_elm = find(signature_elm, 'SignedInfo/CanonicalizationMethod')
     if cm_elm is not None:
         signature.canonicalization_method = cm_elm.attrib.get('Algorithm')
     sm_elm = find(signature_elm, 'SignedInfo/SignatureMethod')
     if sm_elm is not None:
         signature.algorithm = sm_elm.attrib.get('Algorithm')
     dm_elm = find(signature_elm, 'SignedInfo/Reference/DigestMethod')
     if dm_elm is not None:
         signature.digest_algorithm = dm_elm.attrib.get('Algorithm')
     issuer = find(signature_elm, 'KeyInfo/X509Data/X509IssuerSerial')
     if issuer is not None:
         signature.issuer = findtext(issuer, 'X509IssuerName')
         signature.serial = findtext(issuer, 'X509SerialNumber')
     certificate = findbin(
         signature_elm, 'KeyInfo/X509Data/X509Certificate')
     if certificate:
         certificate = base64.b64encode(certificate)
         signature.certificate = b'\n'.join(
             [b'-----BEGIN CERTIFICATE-----'] +
             [certificate[i:i + 64]
              for i in range(0, len(certificate), 64)] +
             [b'-----END CERTIFICATE-----'])
Пример #2
0
 def parse_document(cls, pskc, container):
     """Read information from the provided <KeyContainer> tree."""
     remove_namespaces(container)
     if container.tag not in ('KeyContainer', 'SecretContainer'):
         raise ParseError('Missing KeyContainer')
     # the version of the PSKC schema
     pskc.version = container.get('Version') or container.get('version')
     if (container.tag == 'KeyContainer' and
             pskc.version and
             pskc.version not in ('1', '1.0')):
         raise ParseError('Unsupported version %r' % pskc.version)
     # unique identifier for the container
     pskc.id = (
         container.get('Id') or container.get('ID') or container.get('id'))
     # handle EncryptionKey entries
     cls.parse_encryption(pskc.encryption, find(
         container, 'EncryptionKey', 'EncryptionMethod'))
     # handle MACMethod entries
     cls.parse_mac_method(pskc.mac, find(
         container, 'MACMethod', 'DigestMethod'))
     # fall back to MACAlgorithm
     mac_algorithm = findtext(container, 'MACAlgorithm')
     if mac_algorithm:
         pskc.mac.algorithm = mac_algorithm
     # handle KeyPackage entries
     for key_package in findall(container, 'KeyPackage', 'Device'):
         cls.parse_key_package(pskc.add_device(), key_package)
     # handle Signature entries
     cls.parse_signature(pskc.signature, find(container, 'Signature'))
Пример #3
0
    def parse(self, key_package):
        """Read key information from the provided <KeyPackage> tree."""
        from pskc.xml import find, findtext, findtime, getint, getbool

        if key_package is None:
            return

        key = find(key_package, "pskc:Key")
        if key is not None:
            self.id = key.get("Id")
            self.algorithm = key.get("Algorithm")

        data = find(key_package, "pskc:Key/pskc:Data")
        if data is not None:
            self._secret.parse(find(data, "pskc:Secret"))
            self._counter.parse(find(data, "pskc:Counter"))
            self._time_offset.parse(find(data, "pskc:Time"))
            self._time_interval.parse(find(data, "pskc:TimeInterval"))
            self._time_drift.parse(find(data, "pskc:TimeDrift"))

        self.issuer = findtext(key_package, "pskc:Key/pskc:Issuer")
        self.key_profile = findtext(key_package, "pskc:Key/pskc:KeyProfileId")
        self.key_reference = findtext(key_package, "pskc:Key/pskc:KeyReference")
        self.friendly_name = findtext(key_package, "pskc:Key/pskc:FriendlyName")
        # TODO: support multi-language values of <FriendlyName>
        self.key_userid = findtext(key_package, "pskc:Key/pskc:UserId")

        self.manufacturer = findtext(key_package, "pskc:DeviceInfo/pskc:Manufacturer")
        self.serial = findtext(key_package, "pskc:DeviceInfo/pskc:SerialNo")
        self.model = findtext(key_package, "pskc:DeviceInfo/pskc:Model")
        self.issue_no = findtext(key_package, "pskc:DeviceInfo/pskc:IssueNo")
        self.device_binding = findtext(key_package, "pskc:DeviceInfo/pskc:DeviceBinding")
        self.start_date = findtime(key_package, "pskc:DeviceInfo/pskc:StartDate")
        self.expiry_date = findtime(key_package, "pskc:DeviceInfo/pskc:ExpiryDate")
        self.device_userid = findtext(key_package, "pskc:DeviceInfo/pskc:UserId")

        self.crypto_module = findtext(key_package, "pskc:CryptoModuleInfo/pskc:Id")

        self.algorithm_suite = findtext(key_package, "pskc:Key/pskc:AlgorithmParameters/pskc:Suite")

        challenge_format = find(key_package, "pskc:Key/pskc:AlgorithmParameters/pskc:ChallengeFormat")
        if challenge_format is not None:
            self.challenge_encoding = challenge_format.get("Encoding")
            self.challenge_min_length = getint(challenge_format, "Min")
            self.challenge_max_length = getint(challenge_format, "Max")
            self.challenge_check = getbool(challenge_format, "CheckDigits")

        response_format = find(key_package, "pskc:Key/pskc:AlgorithmParameters/pskc:ResponseFormat")
        if response_format is not None:
            self.response_encoding = response_format.get("Encoding")
            self.response_length = getint(response_format, "Length")
            self.response_check = getbool(response_format, "CheckDigits")

        self.policy.parse(find(key_package, "pskc:Key/pskc:Policy"))
Пример #4
0
 def parse_encrypted_value(cls, encrypted_value):
     """Read encryption value from <EncryptedValue> element."""
     algorithm = None
     cipher_value = findbin(encrypted_value, 'CipherData/CipherValue')
     encryption_method = find(encrypted_value, 'EncryptionMethod')
     if encryption_method is not None:
         algorithm = encryption_method.attrib.get('Algorithm')
     encryption_scheme = find(
         encrypted_value, 'EncryptionMethod/EncryptionScheme')
     if encryption_scheme is not None:
         algorithm = encryption_scheme.attrib.get('Algorithm') or algorithm
     return (algorithm, cipher_value)
Пример #5
0
    def parse(self, element):
        """Read information from the provided element.

        The element is expected to contain <PlainValue>, <EncryptedValue>
        and/or ValueMAC elements that contain information on the actual
        value."""
        from pskc.xml import find, findtext

        if element is None:
            return
        value = findtext(element, "pskc:PlainValue")
        if value is not None:
            self.value = self.from_text(value)
        self.encrypted_value.parse(find(element, "pskc:EncryptedValue"))
        self.value_mac.parse(find(element, "pskc:ValueMAC"))
Пример #6
0
 def parse_encryption(cls, encryption, key_info):
     """Read encryption information from the <EncryptionKey> XML tree."""
     if key_info is None:
         return
     encryption.id = key_info.get('Id')
     encryption.algorithm = (
         key_info.get('Algorithm') or
         key_info.get('algorithm') or
         encryption.algorithm)
     for name in findall(key_info,
                         'KeyName', 'DerivedKey/MasterKeyName',
                         'DerivedKey/CarriedKeyName'):
         encryption.key_names.append(findtext(name, '.'))
     encryption.iv = findbin(key_info, 'IV') or encryption.iv
     cls.parse_key_derivation(encryption.derivation, find(
         key_info, 'DerivedKey/KeyDerivationMethod'))
     encryption.derivation.pbkdf2_salt = (
         findbin(key_info, 'PBESalt') or encryption.derivation.pbkdf2_salt)
     encryption.derivation.pbkdf2_iterations = (
         findint(key_info, 'PBEIterationCount') or
         encryption.derivation.pbkdf2_iterations)
     algorithm = (
         key_info.get('Algorithm') or key_info.get('algorithm') or '')
     if (algorithm.lower().startswith('pbe') and
             not encryption.derivation.algorithm):
         encryption.derivation.algorithm = 'pbkdf2'
         encryption.derivation.pbkdf2_key_length = (
             encryption.derivation.pbkdf2_key_length or
             encryption.algorithm_key_lengths[0])
Пример #7
0
 def parse(self, mac_method):
     """Read MAC information from the <MACMethod> XML tree."""
     from pskc.xml import find, findtext
     if mac_method is None:
         return
     self.algorithm = mac_method.get('Algorithm')
     self._mac_key.parse(find(mac_method, 'pskc:MACKey'))
     mac_key_reference = findtext(mac_method, 'pskc:MACKeyReference')
Пример #8
0
 def parse_key_derivation(cls, derivation, key_derivation):
     """Read derivation parameters from a <KeyDerivationMethod> element."""
     if key_derivation is None:
         return
     derivation.algorithm = key_derivation.get('Algorithm')
     # PBKDF2 properties
     pbkdf2 = find(key_derivation, 'PBKDF2-params')
     if pbkdf2 is not None:
         # get used salt
         derivation.pbkdf2_salt = findbin(pbkdf2, 'Salt/Specified')
         # required number of iterations
         derivation.pbkdf2_iterations = findint(pbkdf2, 'IterationCount')
         # key length
         derivation.pbkdf2_key_length = findint(pbkdf2, 'KeyLength')
         # pseudorandom function used
         prf = find(pbkdf2, 'PRF')
         if prf is not None:
             derivation.pbkdf2_prf = prf.get('Algorithm')
Пример #9
0
 def parse(self, encrypted_value):
     """Read encrypted data from the <EncryptedValue> XML tree."""
     from pskc.xml import find, findbin
     if encrypted_value is None:
         return
     encryption_method = find(encrypted_value, 'xenc:EncryptionMethod')
     if encryption_method is not None:
         self.algorithm = encryption_method.attrib.get('Algorithm')
     self.cipher_value = findbin(
         encrypted_value, 'xenc:CipherData/xenc:CipherValue')
Пример #10
0
 def parse_mac_method(cls, mac, mac_method):
     """Read MAC information from the <MACMethod> XML tree."""
     if mac_method is None:
         return
     mac.algorithm = (
         mac_method.get('Algorithm') or
         mac_method.get('algorithm'))
     mac_key = find(mac_method, 'MACKey')
     if mac_key is not None:
         algorithm, cipher_value = cls.parse_encrypted_value(mac_key)
         mac.key = EncryptedValue(cipher_value, None, algorithm)
Пример #11
0
 def parse(self, container):
     """Read information from the provided <KeyContainer> tree."""
     from pskc.exceptions import ParseError
     from pskc.key import Key
     from pskc.xml import find, findall
     if not container.tag.endswith('KeyContainer'):
         raise ParseError('Missing KeyContainer')
     # the version of the PSKC schema
     self.version = container.get('Version')
     if self.version != '1.0':
         raise ParseError('Unsupported version %r' % self.version)
     # unique identifier for the container
     self.id = container.get('Id')
     # handle EncryptionKey entries
     self.encryption.parse(find(container, 'pskc:EncryptionKey'))
     # handle MACMethod entries
     self.mac.parse(find(container, 'pskc:MACMethod'))
     # handle KeyPackage entries
     for key_package in findall(container, 'pskc:KeyPackage'):
         self.keys.append(Key(self, key_package))
Пример #12
0
 def parse(self, key_info):
     """Read encryption information from the <EncryptionKey> XML tree."""
     from pskc.xml import find, findall, findtext
     if key_info is None:
         return
     self.id = key_info.get('Id')
     for name in findall(key_info, 'ds:KeyName'):
         self.key_names.append(findtext(name, '.'))
     for name in findall(
             key_info, 'xenc11:DerivedKey/xenc11:MasterKeyName'):
         self.key_names.append(findtext(name, '.'))
     self.derivation.parse(find(
         key_info, 'xenc11:DerivedKey/xenc11:KeyDerivationMethod'))
Пример #13
0
    def make_xml(self, key, tag):
        from pskc.xml import find, mk_elem

        # skip empty values
        value = self.get_value()
        if value is None:
            return
        # find the data tag and create our tag under it
        data = find(key, "pskc:Data")
        if data is None:
            data = mk_elem(key, "pskc:Data", empty=True)
        element = mk_elem(data, tag, empty=True)
        mk_elem(element, "pskc:PlainValue", self.to_text(self.value))
Пример #14
0
 def parse(self, key_deriviation):
     """Read derivation parameters from a <KeyDerivationMethod> element."""
     from pskc.xml import find, findint, findbin
     if key_deriviation is None:
         return
     self.algorithm = key_deriviation.get('Algorithm')
     # PBKDF2 properties
     pbkdf2 = find(
         key_deriviation, 'xenc11:PBKDF2-params', 'pkcs5:PBKDF2-params')
     if pbkdf2 is not None:
         # get used salt
         self.pbkdf2_salt = findbin(
             pbkdf2, 'Salt/Specified', 'xenc11:Salt/xenc11:Specified')
         # required number of iterations
         self.pbkdf2_iterations = findint(
             pbkdf2, 'IterationCount', 'xenc11:IterationCount')
         # key length
         self.pbkdf2_key_length = findint(
             pbkdf2, 'KeyLength', 'xenc11:KeyLength')
         # pseudorandom function used
         prf = find(pbkdf2, 'PRF', 'xenc11:PRF')
         if prf is not None:
             self.pbkdf2_prf = prf.get('Algorithm')
Пример #15
0
    def parse_data(cls, key, field, element):
        """Read information from the provided element.

        The element is expected to contain <PlainValue>, <EncryptedValue>
        and/or <ValueMAC> elements that contain information on the actual
        value.
        """
        if element is None:
            return
        pskc = key.device.pskc
        plain_value = None
        cipher_value = None
        algorithm = None
        # get the plain2value function and encryption storage
        if field == 'secret':
            plain2value = base64.b64decode
            encrypted_value_cls = EncryptedValue
        else:
            plain2value = plain2int
            encrypted_value_cls = EncryptedIntegerValue
        # read plaintext value from <PlainValue>
        plain_value = findtext(element, 'PlainValue')
        if plain_value is not None:
            plain_value = plain2value(plain_value)
        # read encrypted data from <EncryptedValue>
        encrypted_value = find(element, 'EncryptedValue')
        if encrypted_value is not None:
            algorithm, cipher_value = cls.parse_encrypted_value(
                encrypted_value)
            # store the found algorithm in the pskc.encryption property
            if not pskc.encryption.algorithm and algorithm:
                pskc.encryption.algorithm = algorithm
        # read MAC information from <ValueMAC>
        mac_value = findbin(element, 'ValueMAC', 'ValueDigest')
        # read legacy <Value> elements (can be plain or encrypted)
        value = findtext(element, 'Value')
        if value is not None:
            if pskc.encryption.algorithm and mac_value:
                cipher_value = findbin(element, 'Value')
            else:
                plain_value = plain2value(value)
        # store the found information
        if plain_value is not None:
            setattr(key, field, plain_value)
        elif cipher_value:
            setattr(key, field,
                    encrypted_value_cls(cipher_value, mac_value, algorithm))
Пример #16
0
 def serialise_data(cls, key, field, key_elm, tag):
     """Provide an XML structure for the key material."""
     value = getattr(key, '_%s' % field, None)
     pskc = key.device.pskc
     # skip empty values
     if value in (None, ''):
         return
     # get the value2text and encryption storage
     if field == 'secret':
         value2text = my_b64encode
         encrypted_value_cls = EncryptedValue
     else:
         value2text = str
         encrypted_value_cls = EncryptedIntegerValue
     # find the data tag and create our tag under it
     data = find(key_elm, 'pskc:Data')
     if data is None:
         data = mk_elem(key_elm, 'pskc:Data', empty=True)
     element = mk_elem(data, tag, empty=True)
     # see if we should encrypt the value
     if field in pskc.encryption.fields and not hasattr(value, 'get_value'):
         value = encrypted_value_cls.create(pskc, value)
     # write out value
     if not hasattr(value, 'get_value'):
         # unencrypted value
         mk_elem(element, 'pskc:PlainValue', value2text(value))
     else:
         # encrypted value
         algorithm = value.algorithm or pskc.encryption.algorithm
         cipher_value = value.cipher_value
         if pskc.encryption.iv:
             cipher_value = pskc.encryption.iv + cipher_value
         encrypted_value = mk_elem(element,
                                   'pskc:EncryptedValue',
                                   empty=True)
         mk_elem(encrypted_value,
                 'xenc:EncryptionMethod',
                 Algorithm=algorithm)
         cipher_data = mk_elem(encrypted_value,
                               'xenc:CipherData',
                               empty=True)
         mk_elem(cipher_data, 'xenc:CipherValue',
                 base64.b64encode(cipher_value).decode())
         if value.mac_value:
             mk_elem(element, 'pskc:ValueMAC',
                     base64.b64encode(value.mac_value).decode())
Пример #17
0
 def parse_key_package(cls, device, key_package):
     """Read key information from the provided <KeyPackage> tree."""
     # find basic device information
     info = find(key_package, 'DeviceInfo', 'DeviceId')
     if info is not None:
         device.manufacturer = findtext(info, 'Manufacturer')
         device.serial = findtext(info, 'SerialNo')
         device.model = findtext(info, 'Model')
         device.issue_no = findtext(info, 'IssueNo')
         device.device_binding = findtext(info, 'DeviceBinding')
         device.start_date = findtime(info, 'StartDate')
         device.expiry_date = findtime(info, 'ExpiryDate', 'Expiry')
         device.device_userid = findtext(info, 'UserId')
     # find crypto module info
     device.crypto_module = findtext(key_package, 'CryptoModuleInfo/Id')
     # find keys for device
     for key_elm in findall(key_package, 'Key', 'Secret'):
         cls.parse_key(device.add_key(), key_elm)
Пример #18
0
 def serialise_data(cls, key, field, key_elm, tag):
     """Provide an XML structure for the key material."""
     value = getattr(key, '_%s' % field, None)
     pskc = key.device.pskc
     # skip empty values
     if value in (None, ''):
         return
     # get the value2text and encryption storage
     if field == 'secret':
         value2text = my_b64encode
         encrypted_value_cls = EncryptedValue
     else:
         value2text = str
         encrypted_value_cls = EncryptedIntegerValue
     # find the data tag and create our tag under it
     data = find(key_elm, 'pskc:Data')
     if data is None:
         data = mk_elem(key_elm, 'pskc:Data', empty=True)
     element = mk_elem(data, tag, empty=True)
     # see if we should encrypt the value
     if field in pskc.encryption.fields and not hasattr(
             value, 'get_value'):
         value = encrypted_value_cls.create(pskc, value)
     # write out value
     if not hasattr(value, 'get_value'):
         # unencrypted value
         mk_elem(element, 'pskc:PlainValue', value2text(value))
     else:
         # encrypted value
         algorithm = value.algorithm or pskc.encryption.algorithm
         cipher_value = value.cipher_value
         if pskc.encryption.iv:
             cipher_value = pskc.encryption.iv + cipher_value
         encrypted_value = mk_elem(
             element, 'pskc:EncryptedValue', empty=True)
         mk_elem(encrypted_value, 'xenc:EncryptionMethod',
                 Algorithm=algorithm)
         cipher_data = mk_elem(
             encrypted_value, 'xenc:CipherData', empty=True)
         mk_elem(cipher_data, 'xenc:CipherValue',
                 base64.b64encode(cipher_value).decode())
         if value.mac_value:
             mk_elem(element, 'pskc:ValueMAC',
                     base64.b64encode(value.mac_value).decode())
Пример #19
0
    def parse_policy(cls, policy, policy_elm):
        """Read key policy information from the provided <Policy> tree."""
        if policy_elm is None:
            return

        policy.start_date = findtime(policy_elm, 'StartDate')
        policy.expiry_date = findtime(policy_elm, 'ExpiryDate')
        policy.number_of_transactions = findint(
            policy_elm, 'NumberOfTransactions')
        for key_usage in findall(policy_elm, 'KeyUsage'):
            policy.key_usage.append(findtext(key_usage, '.'))

        pin_policy_elm = find(policy_elm, 'PINPolicy')
        if pin_policy_elm is not None:
            policy.pin_key_id = pin_policy_elm.get('PINKeyId')
            policy.pin_usage = pin_policy_elm.get('PINUsageMode')
            policy.pin_max_failed_attempts = getint(
                pin_policy_elm, 'MaxFailedAttempts')
            policy.pin_min_length = getint(pin_policy_elm, 'MinLength')
            policy.pin_max_length = getint(pin_policy_elm, 'MaxLength')
            policy.pin_encoding = pin_policy_elm.get('PINEncoding')
            # check for child elements
            if list(pin_policy_elm):
                policy.unknown_policy_elements = True
            # check for unknown attributes
            known_attributes = set([
                'PINKeyId', 'PINUsageMode', 'MaxFailedAttempts', 'MinLength',
                'MaxLength', 'PINEncoding'])
            if set(pin_policy_elm.keys()) - known_attributes:
                policy.unknown_policy_elements = True

        # check for other child elements
        known_children = set([
            'StartDate', 'ExpiryDate', 'NumberOfTransactions', 'KeyUsage',
            'PINPolicy'])
        for child in policy_elm:
            if child.tag not in known_children:
                policy.unknown_policy_elements = True
Пример #20
0
    def parse(self, policy):
        """Read key policy information from the provided <Policy> tree."""
        from pskc.xml import (
            find, findall, findtext, findint, findtime, getint)
        if policy is None:
            return

        self.start_date = findtime(policy, 'pskc:StartDate')
        self.expiry_date = findtime(policy, 'pskc:ExpiryDate')
        self.number_of_transactions = findint(
            policy, 'pskc:NumberOfTransactions')
        for key_usage in findall(policy, 'pskc:KeyUsage'):
            self.key_usage.append(findtext(key_usage, '.'))

        pin_policy = find(policy, 'pskc:PINPolicy')
        if pin_policy is not None:
            self.pin_key_id = pin_policy.get('PINKeyId')
            self.pin_usage = pin_policy.get('PINUsageMode')
            self.pin_max_failed_attemtps = getint(
                pin_policy, 'MaxFailedAttempts')
            self.pin_min_length = getint(pin_policy, 'MinLength')
            self.pin_max_length = getint(pin_policy, 'MaxLength')
            self.pin_encoding = pin_policy.get('PINEncoding')
Пример #21
0
 def serialise_datatype(cls, dt, key_elm, tag, field):
     # skip empty values
     if dt.value in (None, '') and not dt.cipher_value:
         return
     # find the data tag and create our tag under it
     data = find(key_elm, 'pskc:Data')
     if data is None:
         data = mk_elem(key_elm, 'pskc:Data', empty=True)
     element = mk_elem(data, tag, empty=True)
     # see if we should encrypt
     if field in dt.pskc.encryption.fields and not dt.cipher_value:
         dt.cipher_value = dt.pskc.encryption.encrypt_value(
             dt._to_bin(dt.value))
         dt.algorithm = dt.pskc.encryption.algorithm
         dt.value = None
     # write out value
     if dt.cipher_value:
         encrypted_value = mk_elem(
             element, 'pskc:EncryptedValue', empty=True)
         mk_elem(
             encrypted_value, 'xenc:EncryptionMethod',
             Algorithm=dt.algorithm)
         cipher_data = mk_elem(
             encrypted_value, 'xenc:CipherData', empty=True)
         mk_elem(
             cipher_data, 'xenc:CipherValue',
             base64.b64encode(dt.cipher_value).decode())
         if dt.value_mac:
             mk_elem(element, 'pskc:ValueMAC', base64.b64encode(
                 dt.value_mac).decode())
         elif dt.pskc.mac.algorithm:
             mk_elem(element, 'pskc:ValueMAC', base64.b64encode(
                 dt.pskc.mac.generate_mac(dt.cipher_value)
             ).decode())
     else:
         mk_elem(element, 'pskc:PlainValue', dt._to_text(dt.value))
Пример #22
0
 def parse_key(cls, key, key_elm):
     """Read key information from the provided <KeyPackage> tree."""
     # get key basic information
     key.id = (
         key_elm.get('Id') or key_elm.get('KeyId') or
         key_elm.get('SecretId'))
     key.algorithm = (
         key_elm.get('Algorithm') or key_elm.get('KeyAlgorithm') or
         key_elm.get('SecretAlgorithm'))
     # parse data section with possibly encrypted data
     data = find(key_elm, 'Data')
     if data is not None:
         cls.parse_data(key, 'secret', find(data, 'Secret'))
         cls.parse_data(key, 'counter', find(data, 'Counter'))
         cls.parse_data(key, 'time_offset', find(data, 'Time'))
         cls.parse_data(key, 'time_interval', find(data, 'TimeInterval'))
         cls.parse_data(key, 'time_drift', find(data, 'TimeDrift'))
     # parse legacy data elements with name attribute
     for data in findall(key_elm, 'Data'):
         name = data.get('Name')
         if name:
             cls.parse_data(key, dict(
                 secret='secret',
                 counter='counter',
                 time='time_offset',
                 time_interval='time_interval',
             ).get(name.lower()), data)
     # parse more basic key properties
     key.issuer = findtext(key_elm, 'Issuer')
     key.key_profile = findtext(key_elm, 'KeyProfileId')
     key.key_reference = findtext(key_elm, 'KeyReference')
     key.friendly_name = findtext(key_elm, 'FriendlyName')
     # TODO: support multi-language values of <FriendlyName>
     key.key_userid = findtext(key_elm, 'UserId')
     key.algorithm_suite = findtext(
         key_elm, 'AlgorithmParameters/Suite')
     # parse challenge format
     challenge_format = find(
         key_elm,
         'AlgorithmParameters/ChallengeFormat', 'Usage/ChallengeFormat')
     if challenge_format is not None:
         key.challenge_encoding = (
             challenge_format.get('Encoding') or
             challenge_format.get('Format') or
             challenge_format.get('format'))
         key.challenge_min_length = (
             getint(challenge_format, 'Min') or
             getint(challenge_format, 'min'))
         key.challenge_max_length = (
             getint(challenge_format, 'Max') or
             getint(challenge_format, 'max'))
         key.challenge_check = getbool(
             challenge_format, 'CheckDigits', getbool(
                 challenge_format, 'CheckDigit'))
     # parse response format
     response_format = find(
         key_elm,
         'AlgorithmParameters/ResponseFormat', 'Usage/ResponseFormat')
     if response_format is not None:
         key.response_encoding = (
             response_format.get('Encoding') or
             response_format.get('Format') or
             response_format.get('format'))
         key.response_length = (
             getint(response_format, 'Length') or
             getint(response_format, 'length'))
         key.response_check = getbool(
             response_format, 'CheckDigits', getbool(
                 response_format, 'CheckDigit'))
     # parse key policy information
     cls.parse_policy(key.policy, find(key_elm, 'Policy'))
     # parse key usage information
     usage = find(key_elm, 'Usage')
     if usage is not None:
         for att in ('OTP', 'CR', 'Integrity', 'Encrypt', 'Unlock'):
             if getbool(usage, att):
                 key.policy.key_usage.append(att)
     key.policy.start_date = (
         findtime(key_elm, 'StartDate') or key.policy.start_date)
     key.policy.expiry_date = (
         findtime(key_elm, 'ExpiryDate') or key.policy.expiry_date)