Public bug reported: Because of how python3 handles byte>str conversion, the passcode generation function produces a mangled result in python3. The reason the unit tests still pass in python3 is because the tests also use the same function and thus the server and the tests are both sending and expecting the same mangled passcode.
This would then mean that anyone correctly generating the passcode and attempting to authenticate via TOTP would fail because the server is expecting a mangled passcode. The fix is to not use six.text_type, as it does the wrong thing, and instead use .decode('utf-8') which produces the correct result in both python2 and python3. Example of why and how this happens: Python2: >>> passcode = b'123456' >>> print passcode 123456 >>> type(passcode) <type 'str'> >>> import six >>> six.text_type(passcode) u'123456' >>> type(six.text_type(passcode)) <type 'unicode'> >>> otherstring = "openstack" >>> otherstring + passcode 'openstack123456' >>> passcode.decode('utf-8') u'123456' >>> type(passcode.decode('utf-8')) <type 'unicode'> Python3: >>> passcode = b'123456' >>> print(passcode) b'123456' >>> type(passcode) <class 'bytes'> >>> import six >>> six.text_type(passcode) "b'123456'" >>> type(six.text_type(passcode)) <class 'str'> >>> otherstring = "openstack" >>> otherstring + passcode Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: Can't convert 'bytes' object to str implicitly >>> otherstring + str(passcode) "openstackb'123456'" >>> passcode.decode('utf-8') '123456' >>> type(passcode.decode('utf-8')) <class 'str'> ** Affects: keystone Importance: Undecided Assignee: Adrian Turjak (adriant-y) Status: New ** Description changed: Because of how python3 handles byte>str conversion, the passcode generation function produces a mangled result in python3. The reason the unit tests still pass in python3 is because the tests also use the same function and thus the server and the tests are both sending and expecting the same mangled passcode. + + This would then mean that anyone correctly generating the passcode and + attempting to authenticate via TOTP would fail because the server is + expecting a mangled passcode. The fix is to not use six.text_type, as it does the wrong thing, and instead use .decode('utf-8') which produces the correct result in both python2 and python3. Example of why and how this happens: Python2: >>> passcode = b'123456' >>> print passcode 123456 >>> type(passcode) <type 'str'> >>> import six >>> six.text_type(passcode) u'123456' >>> type(six.text_type(passcode)) <type 'unicode'> >>> otherstring = "openstack" >>> otherstring + passcode 'openstack123456' >>> passcode.decode('utf-8') u'123456' >>> type(passcode.decode('utf-8')) <type 'unicode'> Python3: >>> passcode = b'123456' >>> print(passcode) b'123456' >>> type(passcode) <class 'bytes'> >>> import six >>> six.text_type(passcode) "b'123456'" >>> type(six.text_type(passcode)) <class 'str'> >>> otherstring = "openstack" >>> otherstring + passcode Traceback (most recent call last): - File "<stdin>", line 1, in <module> + File "<stdin>", line 1, in <module> TypeError: Can't convert 'bytes' object to str implicitly >>> otherstring + str(passcode) "openstackb'123456'" >>> passcode.decode('utf-8') '123456' >>> type(passcode.decode('utf-8')) <class 'str'> -- You received this bug notification because you are a member of Yahoo! Engineering Team, which is subscribed to OpenStack Identity (keystone). https://bugs.launchpad.net/bugs/1607119 Title: TOTP auth not functional in python3 Status in OpenStack Identity (keystone): New Bug description: Because of how python3 handles byte>str conversion, the passcode generation function produces a mangled result in python3. The reason the unit tests still pass in python3 is because the tests also use the same function and thus the server and the tests are both sending and expecting the same mangled passcode. This would then mean that anyone correctly generating the passcode and attempting to authenticate via TOTP would fail because the server is expecting a mangled passcode. The fix is to not use six.text_type, as it does the wrong thing, and instead use .decode('utf-8') which produces the correct result in both python2 and python3. Example of why and how this happens: Python2: >>> passcode = b'123456' >>> print passcode 123456 >>> type(passcode) <type 'str'> >>> import six >>> six.text_type(passcode) u'123456' >>> type(six.text_type(passcode)) <type 'unicode'> >>> otherstring = "openstack" >>> otherstring + passcode 'openstack123456' >>> passcode.decode('utf-8') u'123456' >>> type(passcode.decode('utf-8')) <type 'unicode'> Python3: >>> passcode = b'123456' >>> print(passcode) b'123456' >>> type(passcode) <class 'bytes'> >>> import six >>> six.text_type(passcode) "b'123456'" >>> type(six.text_type(passcode)) <class 'str'> >>> otherstring = "openstack" >>> otherstring + passcode Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: Can't convert 'bytes' object to str implicitly >>> otherstring + str(passcode) "openstackb'123456'" >>> passcode.decode('utf-8') '123456' >>> type(passcode.decode('utf-8')) <class 'str'> To manage notifications about this bug go to: https://bugs.launchpad.net/keystone/+bug/1607119/+subscriptions -- Mailing list: https://launchpad.net/~yahoo-eng-team Post to : yahoo-eng-team@lists.launchpad.net Unsubscribe : https://launchpad.net/~yahoo-eng-team More help : https://help.launchpad.net/ListHelp