Background
I'm currently working on a two-factor authentication system where user are able to authenticate using their smartphone. Before the user can make use of their device they need to verify it first. For this they need to scan a QR code I give them and enter the code that subsequently shows.
Problem
The scanning of the QR code works fine, and it gets read correctly by the Google Authenticator app. However, the generated codes don't match with the ones I'm generating on the server.
What have I tried
I have tried a couple of things in the hope of finding my problem.
I have tried directly inserting both a default secret:
'thiswasmysecretkeyused'
and abase64.b32encode()
encoded version of the secret:'ORUGS43XMFZW26LTMVRXEZLUNNSXS5LTMVSA===='
in the Google Authenticator app, but these both generated codes different from the server.I read that the trailing
====
from the key may cause it to not work, so I tried adding one without those as well. Still no good results (they generate the same codes)I have tried using a different algorithm for generating TOTP codes, for in the unlikely event that the algorithm I'm using (django-otp) is incorrect. The different algorithm I used was taken from this answer. Both algorithms generated the same codes when using the same key.
I checked what the time on my system was. I saw that the operating system was showing
15:03
just like my smartphone was. After dumping the time in python with bothtime.time()
anddatetime.datetime.now()
I saw the returned time was one hour behind the operating system time; showing14:03
. I tried adding3600
seconds in to the timestamp used for code generation, but to no avail.I have tried several other things, but can't quite recall what they all were.
I've looked up the code that accepts keys in Google Authenticator and have verified that it is expecting a base32 string. So my encoding of the key is correct, as far as I'm aware. From the code (EnterKeyActivity.java, line 78):
Verify that the input field contains a valid base32 string
Code
Generating the secret key;
def generate_shared_key(self):
# create hash etc.
return base64.b32encode(hasher.hexdigest())
Generating the QR code;
key = authenticator.generate_shared_key()
qrcode = pyqrcode.create('otpauth://totp/someurl.nl?secret=' + key)
Generating the TOTP code;
def generate_code(self, drift_steps=0, creation_interval=30, digits=6, t0=0):
code = str(totp(self.generate_shared_key(), creation_interval, timestamp, digits, drift_steps))
return code.zfill(digits)
If you need any more code, such as django-otp actual totp generating code, let me know.
Errors
No errors.
Hunches
My hunch is that I must be going wrong somewhere with the key generation or with passing the key to Google Authenticator. Since even manually putting the key in Google Authenticator fails to generate the correct codes. Does Google Authenticator do something more with the key once it's been saved, such as adding an user?
I also noticed in the other algorithm I used that the secret there gets decoded first with;
key = base64.b32decode(secret, True)
Is my original key (a SHA512 hash) wrong? Should I or should I not encode it with base64.b32encode()
? If I try to scan the QR code generated without encoding the hash, Google Authenticator says it does not recognize it as a (valid) key.
base64.base32encode()
string is correct, so maybe post the one that doesn't match also. – Elyshabase64.b32encode()
encoded string to Google Authenticator? What do you mean by "the one that doesn't match"? – Rutherfordium