Repository: privacyidea/privacyidea

Issue:
/token/init: Supplying a fixed key size fails
Steps to reproduce:
```
$ curl 'http://localhost:5000/token/init' -H "PI-Authorization: $(cat token)" -F genkey=1 -F keysize=32
{"jsonrpc": "2.0", "signature": "2683685158929432383906195720129520352265837266095201676285167652865700449994338891572355390882151192239435668022210762554706876811081578898173892413663149242842432645562415372037107821830963956035374142310810272141368479735165861979508503496020795831011879818552133131630952437418018211234428222603977222381274122137273272773639468845751118398677877149523891988760244118341500317164943693695817280950826084024636398906248851238419425023371559864172838053285804645798600135252786852597272725383540405016957343095157673354506685093919787843194866199492003047390394471736945280691686250209539217369015141338925704687363", "detail": null, "version": "privacyIDEA unknown", "result": {"status": false, "error": {"message": "an integer is required", "code": -500}}, "time": 1509033254.855614, "id": 1}
```

The log says:

<pre>
[2017-10-26 17:54:14,747][5133][139741250626752][ERROR][privacyidea.app:1423] Exception on /token/init [POST]
Traceback (most recent call last):
  File "/home/fred/dev/netknights/privacyidea/privacyidea/venv/lib/python2.7/site-packages/flask/app.py", line 1817, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/fred/dev/netknights/privacyidea/privacyidea/venv/lib/python2.7/site-packages/flask/app.py", line 1477, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/venv/lib/python2.7/site-packages/flask/app.py", line 1381, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/venv/lib/python2.7/site-packages/flask/app.py", line 1475, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/fred/dev/netknights/privacyidea/privacyidea/venv/lib/python2.7/site-packages/flask/app.py", line 1461, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/api/lib/prepolicy.py", line 117, in policy_wrapper
    return wrapped_function(*args, **kwds)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/api/lib/prepolicy.py", line 117, in policy_wrapper
    return wrapped_function(*args, **kwds)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/api/lib/prepolicy.py", line 117, in policy_wrapper
    return wrapped_function(*args, **kwds)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/api/lib/prepolicy.py", line 117, in policy_wrapper
    return wrapped_function(*args, **kwds)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/api/lib/prepolicy.py", line 117, in policy_wrapper
    return wrapped_function(*args, **kwds)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/api/lib/prepolicy.py", line 117, in policy_wrapper
    return wrapped_function(*args, **kwds)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/api/lib/prepolicy.py", line 117, in policy_wrapper
    return wrapped_function(*args, **kwds)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/api/lib/prepolicy.py", line 117, in policy_wrapper
    return wrapped_function(*args, **kwds)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/api/lib/prepolicy.py", line 117, in policy_wrapper
    return wrapped_function(*args, **kwds)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/api/lib/prepolicy.py", line 117, in policy_wrapper
    return wrapped_function(*args, **kwds)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/api/lib/prepolicy.py", line 117, in policy_wrapper
    return wrapped_function(*args, **kwds)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/api/lib/prepolicy.py", line 117, in policy_wrapper
    return wrapped_function(*args, **kwds)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/api/lib/postpolicy.py", line 102, in policy_wrapper
    response = wrapped_function(*args, **kwds)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/lib/subscriptions.py", line 316, in check_subscription_wrapper
    f_result = func(*args, **kwds)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/lib/event.py", line 60, in event_wrapper
    f_result = func(*args, **kwds)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/lib/log.py", line 154, in log_wrapper
    return func(*args, **kwds)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/api/token.py", line 243, in init
    tokenrealms=tokenrealms)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/lib/log.py", line 154, in log_wrapper
    return func(*args, **kwds)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/lib/token.py", line 925, in init_token
    tokenobject.update(upd_params)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/lib/log.py", line 154, in log_wrapper
    return func(*args, **kwds)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/lib/tokens/hotptoken.py", line 255, in update
    TokenClass.update(self, upd_param, reset_failcount)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/lib/tokenclass.py", line 502, in update
    otpKey = self._genOtpKey_(key_size)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/lib/tokenclass.py", line 553, in _genOtpKey_
    return generate_otpkey(otpkeylen)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/lib/utils.py", line 175, in generate_otpkey
    return binascii.hexlify(geturandom(key_size))
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/lib/crypto.py", line 498, in geturandom
    ret = hsm.random(length)
  File "/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/lib/security/default.py", line 278, in random
    return os.urandom(length)
TypeError: an integer is required
</pre>



Discussion:
OK, we should convert the string of ``key_size`` to an int...

somewhere in 

"/home/fred/dev/netknights/privacyidea/privacyidea/privacyidea/lib/utils.py", line 175,

Resolving patch:
diff --git a/privacyidea/lib/tokenclass.py b/privacyidea/lib/tokenclass.py
--- a/privacyidea/lib/tokenclass.py
+++ b/privacyidea/lib/tokenclass.py
@@ -463,9 +463,7 @@ def update(self, param, reset_failcount=True):
 
         # key_size as parameter overrules a prevoiusly set
         # value e.g. in hashlib in the upper classes
-        key_size = getParam(param, "keysize", optional)
-        if key_size is None:
-            key_size = 20
+        key_size = int(getParam(param, "keysize", optional) or 20)
 
         #
         # process the otpkey:


Test patch:
diff --git a/tests/test_api_token.py b/tests/test_api_token.py
--- a/tests/test_api_token.py
+++ b/tests/test_api_token.py
@@ -1360,3 +1360,29 @@ def test_25_user_init_defaults(self):
             self.assertEqual(token.hashlib, "sha256")
 
         delete_policy("init_details")
+
+    def test_26_supply_key_size(self):
+        with self.app.test_request_context('/token/init',
+                                           method='POST',
+                                           data={"type": "HOTP",
+                                                 "genkey": '1',
+                                                 "pin": "1234",
+                                                 "user": "cornelius",
+                                                 "keysize": "42",
+                                                 "realm": self.realm1},
+                                           headers={'Authorization': self.at}):
+            res = self.app.full_dispatch_request()
+            data = json.loads(res.data)
+            self.assertTrue(res.status_code == 200, res)
+            result = data.get("result")
+            detail = data.get("detail")
+            self.assertTrue(result.get("status"), result)
+            self.assertTrue(result.get("value"), result)
+            self.assertTrue("value" in detail.get("googleurl"), detail)
+            serial = detail.get("serial")
+            self.assertTrue("OATH" in serial, detail)
+            seed_url = detail.get("otpkey").get("value")
+            self.assertEqual(seed_url[:len('seed://')], 'seed://')
+            seed = seed_url[len('seed://'):]
+            self.assertEqual(len(seed.decode('hex')), 42)
+        remove_token(serial)
\ No newline at end of file