Repository: privacyidea/privacyidea

Issue:
PSKC sometimes fails to export an import PW token type
Up to version 2.22.2. We do this in branch-2.22

When exporting and importing PW token types, many tokens are simply not imported.

This seems due to the fact, that the seed (in PW case the passphrase) is unhexlified.
This would result in a sometimes odd-length string, that can not be encrypted in the pskc file.

The problems are here:

export: Certain tokens would not even get exported!
https://github.com/privacyidea/privacyidea/blob/master/privacyidea/lib/importotp.py#L629

import:
https://github.com/privacyidea/privacyidea/blob/master/privacyidea/lib/importotp.py#L502


We can solve this like this:

<pre>
diff --git a/privacyidea/lib/importotp.py b/privacyidea/lib/importotp.py
index 53bcb09..502d1e1 100644
--- a/privacyidea/lib/importotp.py
+++ b/privacyidea/lib/importotp.py
@@ -496,7 +496,10 @@ def parsePSKCdata(xml_data,
                 enc_data = key.data.secret.encryptedvalue.ciphervalue.text
                 enc_data = enc_data.strip()
                 secret = aes_decrypt_b64(binascii.unhexlify(preshared_key_hex), enc_data)
-                token["otpkey"] = binascii.hexlify(secret)
+                if token["type"].lower() in ["hotp", "totp"]:
+                    token["otpkey"] = binascii.hexlify(secret)
+                else:
+                    token["otpkey"] = secret
         except Exception as exx:
             log.error("Failed to import tokendata: {0!s}".format(exx))
             log.debug(traceback.format_exc())
@@ -623,7 +626,10 @@ def export_pskc(tokenobj_list, psk=None):
             timedrift = 0
         otpkey = tokenobj.token.get_otpkey().getKey()
         try:
-            encrypted_otpkey = aes_encrypt_b64(psk, binascii.unhexlify(otpkey))
+            if tokenobj.type.lower() in ["totp", "hotp"]:
+                encrypted_otpkey = aes_encrypt_b64(psk, binascii.unhexlify(otpkey))
+            else:
+                encrypted_otpkey = aes_encrypt_b64(psk, otpkey)
         except TypeError:
             # Some keys might be odd string length
             continue
</pre>


Discussion:

Resolving patch:
diff --git a/privacyidea/lib/importotp.py b/privacyidea/lib/importotp.py
--- a/privacyidea/lib/importotp.py
+++ b/privacyidea/lib/importotp.py
@@ -496,7 +496,10 @@ def parsePSKCdata(xml_data,
                 enc_data = key.data.secret.encryptedvalue.ciphervalue.text
                 enc_data = enc_data.strip()
                 secret = aes_decrypt_b64(binascii.unhexlify(preshared_key_hex), enc_data)
-                token["otpkey"] = binascii.hexlify(secret)
+                if token["type"].lower() in ["hotp", "totp"]:
+                    token["otpkey"] = binascii.hexlify(secret)
+                else:
+                    token["otpkey"] = secret
         except Exception as exx:
             log.error("Failed to import tokendata: {0!s}".format(exx))
             log.debug(traceback.format_exc())
@@ -623,7 +626,10 @@ def export_pskc(tokenobj_list, psk=None):
             timedrift = 0
         otpkey = tokenobj.token.get_otpkey().getKey()
         try:
-            encrypted_otpkey = aes_encrypt_b64(psk, binascii.unhexlify(otpkey))
+            if tokenobj.type.lower() in ["totp", "hotp"]:
+                encrypted_otpkey = aes_encrypt_b64(psk, binascii.unhexlify(otpkey))
+            else:
+                encrypted_otpkey = aes_encrypt_b64(psk, otpkey)
         except TypeError:
             # Some keys might be odd string length
             continue


Test patch:
diff --git a/tests/test_lib_importotp.py b/tests/test_lib_importotp.py
--- a/tests/test_lib_importotp.py
+++ b/tests/test_lib_importotp.py
@@ -586,20 +586,24 @@ def test_06_export_pskc(self):
         t2 = init_token({"serial": "t2", "type": "totp", "otpkey": "123456",
                          "description": "something <with> xml!"})
         t3 = init_token({"serial": "t3", "type": "spass", "otpkey": "123456"})
-        tlist = [t1, t2, t3]
+        t4 = init_token({"serial": "t4", "type": "pw", "otpkey": "lässig",
+                         "description": "password token"})
+        tlist = [t1, t2, t3, t4]
         # export the tokens
         psk, token_num, soup = export_pskc(tlist)
         # Only 2 tokens exported, the spass token does not get exported!
-        self.assertEqual(token_num, 2)
+        self.assertEqual(token_num, 3)
         self.assertEqual(len(psk), 32)
         export = "{0!s}".format(soup)
         # remote the tokens
         remove_token("t1")
         remove_token("t2")
         remove_token("t3")
+        remove_token("t4")
+        remove_token("t5")
         # import the tokens again
         tokens = parsePSKCdata(export, preshared_key_hex=psk)
-        self.assertEqual(len(tokens), 2)
+        self.assertEqual(len(tokens), 3)
         self.assertEqual(tokens.get("t1").get("type"), "hotp")
         self.assertEqual(tokens.get("t1").get("otpkey"), "123456")
         # unicode does not get exported
@@ -607,6 +611,8 @@ def test_06_export_pskc(self):
         self.assertEqual(tokens.get("t2").get("type"), "totp")
         self.assertEqual(tokens.get("t2").get("timeStep"), "30")
         self.assertEqual(tokens.get("t2").get("description"), "something <with> xml!")
+        # password token
+        self.assertEqual(tokens.get("t4").get("otpkey"), "lässig")
 
 
 class GPGTestCase(MyTestCase):