diff --git a/dev-requirements.in b/dev-requirements.in index 2eb7c06..d6f81ec 100644 --- a/dev-requirements.in +++ b/dev-requirements.in @@ -10,3 +10,5 @@ sphinx sphinx-rtd-theme flake8 + +cardet diff --git a/pkcs11/_pkcs11.pyx b/pkcs11/_pkcs11.pyx index c0e5f44..d59c0c9 100644 --- a/pkcs11/_pkcs11.pyx +++ b/pkcs11/_pkcs11.pyx @@ -678,6 +678,87 @@ class Session(types.Session): &length)) return bytes(digest[:length]) + + def set_pin(self, old_pin, new_pin): + cdef CK_ULONG old_pin_length + cdef CK_ULONG new_pin_length + cdef CK_OBJECT_HANDLE handle = self._handle + cdef CK_UTF8CHAR *old_pin_data + cdef CK_UTF8CHAR *new_pin_data + + if old_pin is None or new_pin is None: + raise ArgumentsBad("Set `user_pin`") + + pin_old = old_pin.encode('utf-8') + pin_new = new_pin.encode('utf-8') + + if pin_old and pin_new: + old_pin_data = pin_old + new_pin_data = pin_new + old_pin_length = len(pin_old) + new_pin_length = len(pin_new) + + with nogil: + assertRV(_funclist.C_SetPIN(handle, old_pin_data, old_pin_length, new_pin_data, new_pin_length)) + return True + + return False + + def init_pin(self, user_pin): + cdef CK_OBJECT_HANDLE handle = self._handle + cdef CK_UTF8CHAR *pin_data + cdef CK_ULONG pin_length + + if user_pin is None: + raise ArgumentsBad("Set `user_pin`") + + pin = user_pin.encode('utf-8') + + if pin: + pin_data = pin + pin_length = len(pin) + + with nogil: + assertRV(_funclist.C_InitPIN(handle, pin_data, pin_length)) + + return True + + return False + def logout(self): + cdef CK_OBJECT_HANDLE handle = self._handle + + with nogil: + assertRV(_funclist.C_Logout(handle)) + + return True + + def login(self, user_pin, user_type=None): + cdef CK_OBJECT_HANDLE handle = self._handle + cdef CK_USER_TYPE final_user_type + cdef CK_ULONG pin_len + cdef CK_UTF8CHAR *pin_data + + pin = user_pin.encode("utf-8") + + if pin is not None: + final_user_type = user_type if user_type is not None else CKU_USER + pin_data = pin + pin_len = len(pin) + + print("Attempting login as:", final_user_type) + + with nogil: + assertRV(_funclist.C_Login(handle, final_user_type, pin_data, pin_len)) + + print("Login successful as:", final_user_type) + return True + else: + pin_data = NULL + pin_len = 0 + final_user_type = UserType.NOBODY + + print("Logged in as:", final_user_type) + return False class Object(types.Object): diff --git a/pkcs11/types.py b/pkcs11/types.py index 34f7678..a6619a9 100644 --- a/pkcs11/types.py +++ b/pkcs11/types.py @@ -6,6 +6,7 @@ from threading import RLock from binascii import hexlify +import chardet from cached_property import cached_property @@ -32,9 +33,21 @@ def _CK_UTF8CHAR_to_str(data): """Convert CK_UTF8CHAR to string.""" - return data.rstrip(b'\0').decode('utf-8').rstrip() - - + try: + decoded_data = data.rstrip(b'\0').decode('utf-8').rstrip() + return decoded_data + except UnicodeDecodeError as exc: + print(f"Decoding error: {exc}") + encoding_info = chardet.detect(data) + detected_encoding = encoding_info['encoding'] + try: + decoded_data = data.rstrip(b'\0').decode(detected_encoding).rstrip() + print(f"Decoded using {detected_encoding}: {decoded_data}") + return decoded_data + except UnicodeDecodeError: + print("Unable to determine the encoding.") + return "FallbackValue" + def _CK_VERSION_to_tuple(data): """Convert CK_VERSION to tuple.""" return (data['major'], data['minor']) @@ -529,8 +542,8 @@ def generate_random(self, nbits): :param int nbits: Number of bits to generate. :rtype: bytes """ - raise NotImplementedError() - + raise NotImplementedError() + def digest(self, data, **kwargs): """ Digest `data` using `mechanism`. @@ -559,7 +572,19 @@ def digest(self, data, **kwargs): data = (data,) return self._digest_generator(data, **kwargs) - + + def set_pin(self, old_pin, new_pin): + raise NotImplementedError() + + def init_pin(self, user_pin): + raise NotImplementedError() + + def login(self, user_pin, user_type=None): + raise NotImplementedError() + + def logout(self): + raise NotImplementedError() + class Object: """ diff --git a/tests/test_sessions.py b/tests/test_sessions.py index f9a24e7..815f948 100644 --- a/tests/test_sessions.py +++ b/tests/test_sessions.py @@ -158,3 +158,14 @@ def test_generate_random(self): self.assertEqual(len(random), 16) # Ensure we didn't get 16 bytes of zeros self.assertTrue(all(c != '\0' for c in random)) + + @Only.softhsm2 + def test_set_pin(self): + old_token_pin = "1234" + new_token_pin = "12345" + + with self.token.open(rw=True, user_pin=old_token_pin) as session: + self.assertTrue(session.set_pin(old_token_pin, new_token_pin)) + + with self.token.open(user_pin=new_token_pin) as session: + self.assertIsInstance(session, pkcs11.Session)