Google Authenticator code does not match server generated code

Background


I am currently working on a two-factor authentication system where a user can authenticate using his smartphone. Before a user can use his device, he must first check it. To do this, they need to scan the QR code that I give them and enter the code, which will subsequently be shown.

Problem


Scanning a QR code works fine and it is correctly read by Google Authenticator. However, the generated codes do not match those that I generate on the server.

What i tried


I tried a couple of things in the hope of finding my problem.
  • I tried to directly insert as the default secret: 'thiswasmysecretkeyused'and a base64.b32encode()encoded version of the privacy: 'ORUGS43XMFZW26LTMVRXEZLUNNSXS5LTMVSA===='in the Google Authenticator application, but these two generated codes are different from the server.

  • I read that the end ====of the key can lead to its inoperability, so I tried to add it without them. Still no good results (they generate the same codes)

  • I tried to use a different algorithm to generate TOTP codes, since in the unlikely event that the algorithm I use ( django-otp ) is incorrect. Another algorithm I used was taken from this answer. Both algorithms generate the same codes when using the same key.

  • I checked what time was on my system. I saw that the operating system showed the 15:03same way as my smartphone. After resetting the time in python with time.time()and datetime.datetime.now()I saw that the return time was one hour below the operating system time; showing 14:03. I tried adding 3600seconds to the timestamp used to generate the code, but to no avail.

  • I tried several other things, but I can’t completely remember who they were.

  • I was looking for code that accepts keys in Google Authenticator, and verified that it was expecting a base32 string. Therefore, as far as I know, my key encoding is correct. From the code ( EnterKeyActivity.java , line 78):

    Make sure the input field contains a valid base32 string

Code


Create a secret key;
def generate_shared_key(self):
    # create hash etc.
    return base64.b32encode(hasher.hexdigest())

Creation of a QR code;

key = authenticator.generate_shared_key()
qrcode = pyqrcode.create('otpauth://totp/someurl.nl?secret=' + key)

Creating 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 other code like django-otp, the actual tap generation code, let me know.

Mistakes


No mistakes.

premonitions


My guess is that I should be mistaken somewhere with key generation or with passing the Google Authenticator key. Since even manual installation of a key in Google Authenticator does not allow generating the correct codes. Does Google Authenticator do something else with the key after saving it, for example, with the addition of a user?

I also noticed that in another algorithm I used that the secret there is first decoded,

key = base64.b32decode(secret, True) 

( SHA512) ? base64.b32encode()? QR-, , Google Authenticator , () .

+4
1

, Google Authenticator , , , .

: Google Authenticator , base32 . , QR-, , base32, Google Authenticator.

EnterKeyActivity:

/*
 * Verify that the input field contains a valid base32 string,
 * and meets minimum key requirements.
 */
private boolean validateKeyAndUpdateStatus(boolean submitting) {
    //...
}

Google Authenticator , , . , , base32 .

EnterKeyActivity:

private String getEnteredKey() {
    String enteredKey = mKeyEntryField.getText().toString();
    return enteredKey.replace('1', 'I').replace('0', 'O');
}

protected void onRightButtonPressed() {
    //...
    if (validateKeyAndUpdateStatus(true)) {
        AuthenticatorActivity.saveSecret(this, mAccountName.getText().toString(), getEnteredKey(), null, mode, AccountDb.DEFAULT_HOTP_COUNTER);
        exitWizard();
    }
    //...
}

AuthenticatorActivity:

static boolean saveSecret(Context context, String user, String secret, String originalUser, OtpType type, Integer counter) {
    //...
    if (secret != null) {
          AccountDb accountDb = DependencyInjector.getAccountDb();
          accountDb.update(user, secret, originalUser, type, counter);

          //...
    }
}

Google Authenticator , base32, .

OtpProvider:

private String computePin(String secret, long otp_state, byte[] challenge) throws OtpSourceException {
    //...

    try {
        Signer signer = AccountDb.getSigningOracle(secret);
        //...
    }
}

AccountDb:

static Signer getSigningOracle(String secret) {
    try {
        byte[] keyBytes = decodeKey(secret);
        //...
    }
}

private static byte[] decodeKey(String secret) throws DecodingException {
  return Base32String.decode(secret);
}

, base32 TOTP, , Google Authenticator . , , , , . , .

TL; DR

, /, Google Authenticator, base32. , base32, . Python / :

import base64

base64.b32encode(self.key)
base64.b32decode(self.key)
+6

Source: https://habr.com/ru/post/1617994/


All Articles