diff --git a/pkcs11/_utils.pyx b/pkcs11/_utils.pyx index 57c8f72..8b93ee4 100644 --- a/pkcs11/_utils.pyx +++ b/pkcs11/_utils.pyx @@ -28,11 +28,13 @@ cdef bytes _pack_attribute(key, value): cdef _unpack_attributes(key, value): - """Unpack a Attribute bytes array into a Python value.""" + """ + Unpack a Attribute bytes array into a Python value. + For more custom translations, expand ATTRIBUTE_TYPES in defaults.py. + """ try: _, unpack = ATTRIBUTE_TYPES[key] return unpack(bytes(value)) except KeyError: - raise NotImplementedError("Can't unpack this %s. " - "Expand ATTRIBUTE_TYPES!" % key) + return bytes(value) diff --git a/pkcs11/types.py b/pkcs11/types.py index 34f7678..0f2d92d 100644 --- a/pkcs11/types.py +++ b/pkcs11/types.py @@ -6,6 +6,7 @@ from threading import RLock from binascii import hexlify +from base64 import b64encode from cached_property import cached_property @@ -16,11 +17,13 @@ SlotFlag, TokenFlag, UserType, + CertificateType, ) from .mechanisms import KeyType, Mechanism from .exceptions import ( ArgumentsBad, AttributeTypeInvalid, + AttributeSensitive, NoSuchKey, MultipleObjectsReturned, SignatureInvalid, @@ -621,6 +624,61 @@ def destroy(self): The :class:`Object` is no longer valid. """ raise NotImplementedError() + + + def dict_for_print(self): + """ + This function returns dictionary with most of the object attribute types unchanged. + However, some of them can not be translated directly - in that case the dictionary value is a string explaining, what happened. + In the same fasion, bytes objects are base64 encoded for printing. + + Returns: + dict: A dictionary where keys are the pkcs11.constants.Attributes names and values are original value objects OR + strings with OR + base64 encoded bytes objects as strings. + + """ + ret = {} + for attr in list(Attribute): + try: + attr_value = self[attr] + except AttributeTypeInvalid: + # Attribute is missing, that's fine. + pass + except AttributeSensitive: + # Attribute is sensitive, but we might still want information about the attribute existence. + ret[attr.name] = "" + except ValueError as e: + # Some bytes-encoded datetimes can't be parsed by Python datetime.datetime(), it might happen with other types as well. + # It depends on what exactly is stored on the token. + # (bytes value should be included in the error message) + ret[attr.name] = f"" + except NotImplementedError as e: + # Should not happened, kept here for legacy reasons. Might be removed in the future. + # Just in case, we want to atleast know that it happened. + ret[attr.name] = ( + f"" + ) + else: # (all known exceptions are handled) + attr_type = type(attr_value) + + # If the result is member of the IntEnum, we want to know the name, not the number + if attr_type in ( + ObjectClass, + CertificateType, + KeyType, + ): + attr_value = attr_value.name + elif attr_type is bytes: + # Since all other value types are oriented mainly for printing + if attr_value == b"": + attr_value = "" + else: + attr_value = b64encode(attr_value).decode("utf-8") + + ret[attr.name] = attr_value + return ret class DomainParameters(Object):