mirror of
https://github.com/antonioribeiro/google2fa.git
synced 2026-04-26 16:45:49 +03:00
[GH-ISSUE #123] Google2FA::generateSecretKey() is not compatible with Google 2FA #526
Labels
No labels
bug
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/google2fa#526
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @hopeseekr on GitHub (Jul 14, 2019).
Original GitHub issue: https://github.com/antonioribeiro/google2fa/issues/123
The Problem
Google 2FA requires a minimum of 20 bytes for a secret key.
Google2FA::generateSecretKey()only produces 16 bytes by default.The Solution
Up the default to 20.
@antonioribeiro commented on GitHub (Sep 11, 2019):
Was this somehow changed?
Because it was 16, I did this years ago and it was ever 16 bytes...
@raftalks commented on GitHub (Sep 11, 2019):
I have tested this and works without any issues.
@antonioribeiro commented on GitHub (Sep 11, 2019):
I believe it does. What I want to know is why is this even necessary since we have thousands of users using it without any trouble using 16 bytes. This was never necessary for Google 2FA.
@antonioribeiro commented on GitHub (Sep 12, 2019):
I see now. Thank you.
@antonioribeiro commented on GitHub (Sep 12, 2019):
Changing it made tests to break. I still cannot find the error. Could you provide some snippet showing it?
@raftalks commented on GitHub (Sep 12, 2019):
20 is not the correct length by bits, 16 then 32 then 64, so I still don't think that is the root issue reported by @hopeseekr
What I can tell you is that it will throw an exception where if the token secret was placed as the first argument in place of where the user secret must be (which I did without checking the docs) and had similar issue that brought me to this thread, but I figured it out and all works good for me.
$valid = $google2fa->verifyKey($user->google2fa_secret, $secret);
$valid = $google2fa->verifyKey($secret, $user->google2fa_secret); <--- this is wrong and throws error with compatibility issue to GoogleAuthenticator
@wells commented on GitHub (Sep 18, 2019):
According to the
PragmaRX\Google2FA\Support\Base32trait, theIncompatibleWithGoogleAuthenticatorExceptionis thrown as a result of failing the bitwise&operator test found within thecheckGoogleAuthenticatorCompatibility()function.Here's what I see:
(strlen($b32) & (strlen($b32) - 1)) !== 0(20 & 19) !== 0The
&bitwise operator provides "the bits that are set in both $a and $b."In this instance, we can see the action more easily in base 2 (i.e. binary):
@antonioribeiro commented on GitHub (Sep 18, 2019):
Let me try to explain this better:
1) You can use whatever shared secret you want.
You don't really need to use the
generateSecretKey(), this is just a helper.You can generate it yourself, as long as it is converted to a base32 string when you give it to Google Authenticator. Usually base32 strings are generated by converting a base256 string to a base32 one.
2) I prefer 20 chars to generate keys because
20 chars in base256 === 32 chars in base32 === 256 bits of a strong shared secret
Base 256 is the whole ASCII charset.
You can use as few as 10 base256 chars (16 base32 chars === 128 bits), which is the minimum.
The recommended is 160 bits (20 base32 chars === 12 base256 chars), which is the minimum.
3) But... there's a catch here, Google Authenticator will not let you use 160 bits
Because this is a 12 chars length string in base32 and it MUST be a power of 2 string length.
So the minimum (above the recommended) is 256 bits (32 chars in base32).
Again: Google Authenticator needs it to be power of 2 chars: 16, 32, 64, 128 chars, as stated by @raftalks
Which brings us to
4) The method
checkGoogleAuthenticatorCompatibilityis only checking for power of 2 sized strings:Your examples, @wells, shows exactly this behaviour:
@antonioribeiro commented on GitHub (Sep 18, 2019):
I'm changing it to be more readable:
@wells commented on GitHub (Sep 18, 2019):
This has sent me down the RFC rabbit hole this afternoon.
RFC 4226 (https://tools.ietf.org/html/rfc4226) states the following:
128 bits == 10 chars (base 256) == 16 chars (base 32)160 bits == 12 chars (base 256) == 20 chars (base 32)@antonioribeiro I'm having trouble finding the power of 2 requirement for secret length. Would you have some documentation from Google on this?
Based on what I found in the Google Authenticator wiki, it looks like this package could potentially support sha256 and sha512 hashing algorithms in addition to sha1 (used in
oathHotp()function).I also performed a cursory source dive of the iOS Google Authenticator and I'd say these algorithms may be available now. This would require an update to also include the algorithm parameter in the
getQRCodeUrl()function.https://github.com/google/google-authenticator/wiki/Key-Uri-Format#algorithm
https://github.com/google/google-authenticator/blob/master/mobile/ios/Classes/OTPGenerator.m#L105-L113
@antonioribeiro Do you know if these hashing algorithms are actually supported or is it still just SHA1?
@antonioribeiro commented on GitHub (Sep 18, 2019):
@wells, I could not find it too, but the need for a power of two string is something I checked when I first developed Google2FA, but now, to be sure, I had to do it all over again, so I used a different tool to be sure mines were ok:
https://rootprojects.org/authenticator/
Tested all of those one by one:
@antonioribeiro commented on GitHub (Sep 18, 2019):
This is it with 20 chars (160 bits):
@wells commented on GitHub (Sep 18, 2019):
Powers of 2 is it then.
@antonioribeiro commented on GitHub (Sep 19, 2019):
Are we all good with a this point? @hopeseekr , are you still following your thread, mate?