Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restore from backup made on old phone to new phone asks for password but doesn't import keys #283

Open
sheepbop opened this issue Jan 7, 2023 · 35 comments

Comments

@sheepbop
Copy link

sheepbop commented Jan 7, 2023

Hi,

FreeOTP updated to 2.0 today. Was using previous version on Android. Backup feature looks good, thanks.

I'm trying to transfer my keys from old phone to new phone without having to type them all in again.

I've made a backup from old phone to Google Drive. Then I restore this backup from Drive to FreeOTP 2.0 running on new phone. It sees the backup, and asks me for password. But then returns to an empty screen.

How do you transfer keys to a new phone if this is not the method? The set-up process when installing FreeOTP does not say "would you like to import from backup?"

Thanks,

Sean

@justin-stephenson
Copy link
Contributor

Hello,

Is your old phone also upgraded to FreeOTP 2.0 ? Backup and Restore only works if both phones are using FreeOTP 2.0.

Some documentation is available here: https://github.com/freeotp/freeotp-android/blob/master/BACKUP.md

You can back up manually within the App, or relying on the Android device backup to Google Drive.

@sheepbop
Copy link
Author

Both are running version 2.0. AFAIK you need version 2.0 to create a backup anyway. OK, so it is designed to export to another phone, so at least there's that. Thanks, will have another look.

@justin-stephenson
Copy link
Contributor

If you are using the Android native backup and restore via Google Drive, you may sign in to Google Drive or Google One to ensure that FreeOTP data exists in your backup before attempting to login to FreeOTP on the the new phone. If you continue having issues please document the exact steps taken so I can investigate further.

@Slater91
Copy link

I can reproduce the bug. Here are the steps I took:

  • I updated FreeOTP to version 2.0 on both phones
  • I backed up the old phone and placed the backup file on my Nextcloud server (which is on the same network as both devices)
  • I used the "restore" function on the new phone
  • it asked for a password, which I gave it

The result is that nothing is restored. The backup file is 4.9 KB and should contain 8 keys, so from an outsider's perspective it looks like there is at least something in the file. The old phone runs Android 8.1, while the new one runs Android 13. I'll be happy to provide further information if needed.

@justin-stephenson
Copy link
Contributor

Thanks for the steps, I tested these steps without issues using an Android 8.1 emulator. Could you please provide logcat logs from the new phone covering the time of the restore attempt?

@omrimann
Copy link

omrimann commented Mar 8, 2023

I am having the same problem.

relevant logcat section:

03-08 13:20:38.014 766 766 W keymaster_tee: [WRN]start nwd_import_key
03-08 13:20:38.016 766 766 D keymaster_swd: keymaster_swd [WRN] (swd_run_cb:254) swd_import_key() returns 0
03-08 13:20:38.016 766 766 W keymaster_tee: [WRN]ret OK PARAMS: A32 P0 B32 P1 NAR1 S256 2023.03.08,13:20:38.014
03-08 13:20:38.016 763 3431 I keystore2: keystore2::security_level: In import_key. 10356, Some("masterKey")
03-08 13:20:38.017 763 3431 I keystore2: keystore2::database: In store_new_key "masterKey", uid=10356, cert=false, cert_chain=false rebound=true
03-08 13:20:38.017 1272 1272 I GestureDetector: handleMessage TAP
03-08 13:20:38.018 1272 1292 I GestureDetector: handleMessage TAP
03-08 13:20:38.020 8731 8731 I TokenPersistence: Found [50c729cb-a923-4578-b886-3ed333d26a40-token] in backup
03-08 13:20:38.020 8731 8731 I TokenPersistence: Skipping [50c729cb-a923-4578-b886-3ed333d26a40-token]
03-08 13:20:38.020 8731 8731 I TokenPersistence: Found [masterKey] in backup
03-08 13:20:38.020 8731 8731 I TokenPersistence: Skipping [masterKey]
03-08 13:20:38.020 8731 8731 I TokenPersistence: Found [14831444-c524-444a-aa74-62a1df152cf2-token] in backup
03-08 13:20:38.020 8731 8731 I TokenPersistence: Skipping [14831444-c524-444a-aa74-62a1df152cf2-token]
03-08 13:20:38.020 8731 8731 I TokenPersistence: Found [b2956383-b766-4d18-bb66-b9edfc01f790] in backup
03-08 13:20:38.021 763 3431 E keystore2: keystore2::error: Rc(ResponseCode(7)), "In get_key_entry, while trying to load key info. 10356, Some("b2956383-b766-4d18-bb66-b9edfc01f790")"
03-08 13:20:38.022 8731 8731 E Adapter : Exception
03-08 13:20:38.022 8731 8731 E Adapter : javax.crypto.AEADBadTagException: error:1e000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT
03-08 13:20:38.022 8731 8731 E Adapter : at java.lang.reflect.Constructor.newInstance0(Native Method)
03-08 13:20:38.022 8731 8731 E Adapter : at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
03-08 13:20:38.022 8731 8731 E Adapter : at com.android.org.conscrypt.OpenSSLAeadCipher.throwAEADBadTagExceptionIfAvailable(OpenSSLAeadCipher.java:320)
03-08 13:20:38.022 8731 8731 E Adapter : at com.android.org.conscrypt.OpenSSLAeadCipher.doFinalInternal(OpenSSLAeadCipher.java:371)
03-08 13:20:38.022 8731 8731 E Adapter : at com.android.org.conscrypt.OpenSSLCipher.engineDoFinal(OpenSSLCipher.java:374)
03-08 13:20:38.022 8731 8731 E Adapter : at javax.crypto.Cipher.doFinal(Cipher.java:2056)
03-08 13:20:38.022 8731 8731 E Adapter : at org.fedorahosted.freeotp.encryptor.EncryptedKey.decrypt(EncryptedKey.java:59)
03-08 13:20:38.022 8731 8731 E Adapter : at org.fedorahosted.freeotp.TokenPersistence.restore(TokenPersistence.java:209)
03-08 13:20:38.022 8731 8731 E Adapter : at org.fedorahosted.freeotp.main.Adapter.restoreTokens(Adapter.java:265)
03-08 13:20:38.022 8731 8731 E Adapter : at org.fedorahosted.freeotp.main.Activity$5.onClick(Activity.java:404)
03-08 13:20:38.022 8731 8731 E Adapter : at androidx.appcompat.app.AlertController$ButtonHandler.handleMessage(AlertController.java:167)
03-08 13:20:38.022 8731 8731 E Adapter : at android.os.Handler.dispatchMessage(Handler.java:106)
03-08 13:20:38.022 8731 8731 E Adapter : at android.os.Looper.loopOnce(Looper.java:226)
03-08 13:20:38.022 8731 8731 E Adapter : at android.os.Looper.loop(Looper.java:313)
03-08 13:20:38.022 8731 8731 E Adapter : at android.app.ActivityThread.main(ActivityThread.java:8741)
03-08 13:20:38.022 8731 8731 E Adapter : at java.lang.reflect.Method.invoke(Native Method)
03-08 13:20:38.022 8731 8731 E Adapter : at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
03-08 13:20:38.022 8731 8731 E Adapter : at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)
03-08 13:20:38.022 1272 5834 D CoreBackPreview: Window{6aaa8ff u0 org.fedorahosted.freeotp/org.fedorahosted.freeotp.main.Activity}: Setting back callback null

The problem seams to be a BadTagException which is caused by BadPaddingException inside OpenSSL

@acigolotti
Copy link

acigolotti commented Mar 8, 2023

Hello, I'm currently facing the same problem,

My old phone is on Android 9, I was running freeOTP 1.X and has been running freeOTP 2.X since the update
My new phone is also on Android 13, but i've tried in a android 9 emulator and same issue.

I'm pretty sure I haven't added a new token since the new update, but if I create a new token on my old phone, export the backup and import it in my android 13 emulator, every token except the new one fail to import with the execption bellow

I've added a try/catch/continue in TokenPersistence.java#L209 so it can skip errors

SecretKey skKey;
try {
    // Decrypt the token
    skKey = ekKey.decrypt(sk);
} catch (Exception e) {
    // Invalid JSON backup data
    Log.e(LOGTAG, "Exception", e);
    continue;
}

I still don't know where the issue is but it's probably related to the export or persistence of old tokens maybe?
Hope this can help !

old Android 9.1 phone logcat
03-07 21:07:54.966   589   601 E keystore2: keystore2::error: Rc(ResponseCode(7)), "In get_key_entry, while trying to load key info. 10411, Some(\"8b7ec453-444c-42e0-8feb-bffbec25bfec\")"
03-07 21:07:54.967 29545 29545 E Adapter : Exception
03-07 21:07:54.967 29545 29545 E Adapter : javax.crypto.AEADBadTagException: error:1e000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT
03-07 21:07:54.967 29545 29545 E Adapter :      at java.lang.reflect.Constructor.newInstance0(Native Method)
03-07 21:07:54.967 29545 29545 E Adapter :      at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
03-07 21:07:54.967 29545 29545 E Adapter :      at com.android.org.conscrypt.OpenSSLAeadCipher.throwAEADBadTagExceptionIfAvailable(OpenSSLAeadCipher.java:320)
03-07 21:07:54.967 29545 29545 E Adapter :      at com.android.org.conscrypt.OpenSSLAeadCipher.doFinalInternal(OpenSSLAeadCipher.java:371)
03-07 21:07:54.967 29545 29545 E Adapter :      at com.android.org.conscrypt.OpenSSLCipher.engineDoFinal(OpenSSLCipher.java:374)
03-07 21:07:54.967 29545 29545 E Adapter :      at javax.crypto.Cipher.doFinal(Cipher.java:2056)
03-07 21:07:54.967 29545 29545 E Adapter :      at org.fedorahosted.freeotp.encryptor.EncryptedKey.decrypt(EncryptedKey.java:59)
03-07 21:07:54.967 29545 29545 E Adapter :      at org.fedorahosted.freeotp.TokenPersistence.restore(TokenPersistence.java:209)
03-07 21:07:54.967 29545 29545 E Adapter :      at org.fedorahosted.freeotp.main.Adapter.restoreTokens(Adapter.java:265)
03-07 21:07:54.967 29545 29545 E Adapter :      at org.fedorahosted.freeotp.main.Activity$5.onClick(Activity.java:404)
03-07 21:07:54.967 29545 29545 E Adapter :      at androidx.appcompat.app.AlertController$ButtonHandler.handleMessage(AlertController.java:167)
03-07 21:07:54.967 29545 29545 E Adapter :      at android.os.Handler.dispatchMessage(Handler.java:106)
03-07 21:07:54.967 29545 29545 E Adapter :      at android.os.Looper.loopOnce(Looper.java:226)
03-07 21:07:54.967 29545 29545 E Adapter :      at android.os.Looper.loop(Looper.java:313)
03-07 21:07:54.967 29545 29545 E Adapter :      at android.app.ActivityThread.main(ActivityThread.java:8757)
03-07 21:07:54.967 29545 29545 E Adapter :      at java.lang.reflect.Method.invoke(Native Method)
03-07 21:07:54.967 29545 29545 E Adapter :      at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
03-07 21:07:54.967 29545 29545 E Adapter :      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)
Android 13 emulator logcat
I/TokenPersistence: Found [8b7ec453-444c-42e0-8feb-bffbec25bfec] in backup
W/ahosted.freeotp: Got a deoptimization request on un-deoptimizable method int com.android.org.conscrypt.NativeCrypto.EVP_AEAD_CTX_open(long, byte[], int, byte[], int, byte[], byte[], int, int, byte[])
E/TokenPersistence: Exception
    javax.crypto.AEADBadTagException: error:1e000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT
        at java.lang.reflect.Constructor.newInstance0(Native Method)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
        at com.android.org.conscrypt.OpenSSLAeadCipher.throwAEADBadTagExceptionIfAvailable(OpenSSLAeadCipher.java:320)
        at com.android.org.conscrypt.OpenSSLAeadCipher.doFinalInternal(OpenSSLAeadCipher.java:371)
        at com.android.org.conscrypt.OpenSSLCipher.engineDoFinal(OpenSSLCipher.java:374)
        at javax.crypto.Cipher.doFinal(Cipher.java:2056)
        at org.fedorahosted.freeotp.encryptor.EncryptedKey.decrypt(EncryptedKey.java:59)
        at org.fedorahosted.freeotp.TokenPersistence.restore(TokenPersistence.java:211)
        at org.fedorahosted.freeotp.main.Adapter.restoreTokens(Adapter.java:265)
        at org.fedorahosted.freeotp.main.Activity$5.onClick(Activity.java:404)
        at androidx.appcompat.app.AlertController$ButtonHandler.handleMessage(AlertController.java:167)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at android.app.ActivityThread.main(ActivityThread.java:7872)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)

@justin-stephenson
Copy link
Contributor

Thank you for the logcat logs.

This keystore error is expected because we first check if this key already exists in the app so it is expected to fail.

E keystore2: keystore2::error: Rc(ResponseCode(7)), "In get_key_entry, while trying to load key info. 10356, Some("b2956383-b766-4d18-bb66-b9edfc01f790")"

The error javax.crypto.AEADBadTagException: error:1e000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT is a generic one, it is also returned if the incorrect password is provided on restore attempt.

I am interested to know if all users affected here are only seeing this issue with previously migrated tokens, and not tokens newly added in FreeOTP 2.X.

On upgrade from FreeOTP 1.X to 2.X, tokens are all added into the Android keystore, and also added into the backup file, the token secret is encrypted with the master backup password key. I speculate that there may have been an issue saving the token data into the backup file during this migration. This upgrade path happens in the compat() function in Adapter.java, which calls add() to add the token into the keystore and also save the token into the token backup file, if token backups are provisioned.

The decrypt line that fails here is attempting to decrypt the encrypted key data from the backup file (representing the token secret) with the master password key. We know the master password is correct because otherwise this line would trigger an exception.

Unfortunately I still have been unable to reproduce this issue with installing FreeOTP 1.5 from F-droid, upgrading to 2.0.1 then adding fake tokens with https://freeotp.github.io/qrcode.html backing them up and restoring to another device or emulator.

If I add code to print the keys and values of the backup shared preferences file from my working test device, the contents are:

org.fedorahosted.freeotp D/Activity: key: 88ccc1cf-db00-4a50-98f0-c2f7ee15b864-token
org.fedorahosted.freeotp D/Activity: value: {"algo":"SHA256","counter":1,"digits":6,"issuerExt":"amazon","label":"amazon","period":30,"type":"HOTP"}
org.fedorahosted.freeotp D/Activity: ==========================
org.fedorahosted.freeotp D/Activity: key: a2ee1617-057b-4cf5-b659-cd435fe9c07f
org.fedorahosted.freeotp D/Activity: value: {"key":"{\"mCipher\":\"AES\/GCM\/NoPadding\",\"mCipherText\":[59,-21,-54,60,-93,101,-81,77,-62,2,-125,5,-46,110,-3,-105,22,-126,-2,65,-128,-78,-73,-61,-8,82,-55,-55,29,106,62,68,-93,-87,-101,5,-113,29,1,-87,3,73,-101,-40,114,0,102,-82,-98,0,9],\"mParameters\":[48,17,4,12,53,-112,-44,-46,86,-108,-120,45,-71,-26,109,-93,2,1,16],\"mToken\":\"HmacSHA224\"}"}
org.fedorahosted.freeotp D/Activity: ==========================
org.fedorahosted.freeotp D/Activity: key: a2ee1617-057b-4cf5-b659-cd435fe9c07f-token
org.fedorahosted.freeotp D/Activity: value: {"algo":"SHA224","counter":0,"digits":6,"issuerExt":"redhat","label":"redhat","period":30,"type":"TOTP"}
org.fedorahosted.freeotp D/Activity: ==========================
org.fedorahosted.freeotp D/Activity: key: 88ccc1cf-db00-4a50-98f0-c2f7ee15b864
org.fedorahosted.freeotp D/Activity: value: {"key":"{\"mCipher\":\"AES\/GCM\/NoPadding\",\"mCipherText\":[-51,101,37,6,49,122,-82,99,94,6,43,-105,-33,-119,33,-18,-37,33,55,81,-104,-51,-14,51,-84,-22,96,18,-7,-40,-84,58,-95,-38,50,-45,-9,102,115,-128,122,60,-32,-89,59,79,-57,-56,105,97,-107],\"mParameters\":[48,17,4,12,-125,26,79,-111,-79,-67,-52,111,70,117,19,-105,2,1,16],\"mToken\":\"HmacSHA256\"}"}
org.fedorahosted.freeotp D/Activity: ==========================
org.fedorahosted.freeotp D/Activity: key: masterKey
org.fedorahosted.freeotp D/Activity: value: {"mAlgorithm":"PBKDF2withHmacSHA512","mEncryptedKey":{"mCipher":"AES/GCM/NoPadding","mCipherText":[8,125,101,-12,115,-83,-123,73,70,-128,35,-77,121,76,-36,-50,-64,-70,3,32,38,108,116,-52,-127,80,-63,-61,-128,99,-37,18,-55,-93,42,-111,74,-73,-76,106,-6,10,-62,45,80,-45,-72,71],"mParameters":[48,17,4,12,-41,-10,56,57,-9,27,84,-76,19,70,-109,97,2,1,16],"mToken":"AES"},"mIterations":100000,"mSalt":[-29,-127,2,48,38,-106,-46,-91,73,-118,-17,58,115,55,-128,35,-53,-115,111,-94,40,19,-67,-122,94,-80,-6,-69,-6,18,26,-7]}

Here you can see the token metadata is stored in the key $UUID-token, while the encrypted key is stored in $UUID. The master backup password key is also stored.

I don't have much idea yet but I am writing comments here to track this initial investigation. Also, the FreeOTP upgrade path 1.X to 2.X is a one-time operation, troubleshooting this requires essentially rolling back to this 1.X version which is not something that can be done safely.

@acigolotti
Copy link

Thank you for your time !

I've been trying to debug this issue almost every evening for the last 2 weeks
I don't know if it's the right way, but when the compat() happens, the master key is recovered from the keystore, with the associated KeyProtection
So I was wondering if there was some funky keystore magick and edited TokenPersistence#L176 as follow:

KeyProtection kp = new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                .build();

mKeyStore.setEntry(MASTER, new KeyStore.SecretKeyEntry(sk), kp);
sk = (SecretKey) mKeyStore.getKey(MASTER, null);
and got this exception :
javax.crypto.AEADBadTagException
	at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:517)
	at javax.crypto.Cipher.doFinal(Cipher.java:2055)
	at org.fedorahosted.freeotp.encryptor.EncryptedKey.decrypt(EncryptedKey.java:68)
	at org.fedorahosted.freeotp.TokenPersistence.restore(TokenPersistence.java:213)
	at org.fedorahosted.freeotp.main.Adapter.restoreTokens(Adapter.java:265)
	at org.fedorahosted.freeotp.main.Activity$5.onClick(Activity.java:404)
	at androidx.appcompat.app.AlertController$ButtonHandler.handleMessage(AlertController.java:167)
	at android.os.Handler.dispatchMessage(Handler.java:106)
	at android.os.Looper.loop(Looper.java:193)
	at android.app.ActivityThread.main(ActivityThread.java:6669)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: android.security.KeyStoreException: Signature/MAC verification failed
	at android.security.KeyStore.getKeyStoreException(KeyStore.java:839)
	at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.doFinal(KeyStoreCryptoOperationChunkedStreamer.java:224)
	at android.security.keystore.AndroidKeyStoreAuthenticatedAESCipherSpi$BufferAllOutputUntilDoFinalStreamer.doFinal(AndroidKeyStoreAuthenticatedAESCipherSpi.java:373)
	at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:506)
	at javax.crypto.Cipher.doFinal(Cipher.java:2055) 
	at org.fedorahosted.freeotp.encryptor.EncryptedKey.decrypt(EncryptedKey.java:68) 
	at org.fedorahosted.freeotp.TokenPersistence.restore(TokenPersistence.java:213) 
	at org.fedorahosted.freeotp.main.Adapter.restoreTokens(Adapter.java:265) 
	at org.fedorahosted.freeotp.main.Activity$5.onClick(Activity.java:404) 
	at androidx.appcompat.app.AlertController$ButtonHandler.handleMessage(AlertController.java:167) 
	at android.os.Handler.dispatchMessage(Handler.java:106) 
	at android.os.Looper.loop(Looper.java:193) 
	at android.app.ActivityThread.main(ActivityThread.java:6669) 
	at java.lang.reflect.Method.invoke(Native Method) 
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 

Even try other purposes like KeyProperties.PURPOSE_VERIFY without success

I found this article from another OTP app https://alexbakker.me/post/mysterious-google-titan-m-bug-cve-2019-9465.html
It's similar but not quite, I've check all ciphers and ivs, they seems fine,
I've tried waiting after the cipher init, creating a brand new GCMParameterSpec just from the IV and tag length
Adding buffers, etc…

But it might be related to my old phone ? it was a Huawei Honor 9 STF-L09

BTW, all my token are SHA1 also, might be related or might not

The screen broke on my old phone and I've got a new phone, so I don't realy care if I brick it, I've tried to root it, unfortunatly it's not as easy as I was hopping for, I'm pretty sure I'm almost there but I don't know what I'm doing

If you think you can help me, I'll take it,
If I can be useful, I will gladly help

@omrimann
Copy link

My case is also of an upgraded software, where tokens were created on 1.x and then upgraded to 2.0

@justin-stephenson
Copy link
Contributor

justin-stephenson commented Mar 22, 2023

I'm still trying to figure out what may cause this.

If someone has a throwaway phone and they can reproduce this problem with these steps below, then I could create a test build which adds additional logging and sanity checks in the upgrade path code.

  • installing FreeOTP 1.5 from F-Droid (./adb install -r org.fedorahosted.freeotp_17.apk)
  • adding one or more affected tokens
  • upgrade device to FreeOTP 2.0.1 (./adb install -r org.fedorahosted.freeotp_42.apk)
  • Perform a backup, ensure that the restore fails in the same way

This same process could be repeated with tokens from https://freeotp.github.io/qrcode.html to determine if the token data is related or unrelated to the issue.

@justin-stephenson
Copy link
Contributor

Could someone provide some examples of 2FA providers for tokens which are not being backed up properly? (i.e, Github, Slack, Paypal, etc).

@Slater91
Copy link

In my case there's no provider which works. Amazon, Facebook, Nextcloud, WordPress... Nothing actually works. I've tried creating new tokens after the update to version 2.0, but those aren't restored either on the new device.

@justin-stephenson
Copy link
Contributor

In my case there's no provider which works. Amazon, Facebook, Nextcloud, WordPress... Nothing actually works. I've tried creating new tokens after the update to version 2.0, but those aren't restored either on the new device.

Could you please provide your backup file, you can email to [email protected] (your token secrets are encrypted with your backup password so I, or others, cannot decrypt them)

@m-from-space
Copy link

I have the same issue here.

Upgraded my phone to a newer LineageOS and I am not able to manually restore my externalBackup using the "Restore" button.

  1. Keys were created with an old FreeOTP version.
  2. FreeOTP was updated to 2.x version
  3. Made externalBackup on my phone with "Backup" button and saved that file elsewhere
  4. Installed new FreeOTP on the new OS and used the same master password (don't know if that is necessary)
  5. Tried restoring the externalBackup file. If I put in a wrong password an error occurs (as expected). If I provide the correct password, nothing happens and no message or keys appear.

I really want my keys back.

@justin-stephenson
Copy link
Contributor

It would be great if someone having this issue would be able to run the FreeOTP Unit tests in Android Studio to see if any backup related encryption/decryption operations fail.

I still have not been able to reproduce this issue therefore I don't have a reliable way to troubleshoot exactly what is failing.

@m-from-space
Copy link

It would be great if someone having this issue would be able to run the FreeOTP Unit tests in Android Studio to see if any backup related encryption/decryption operations fail.

Would you be able to explain what exactly I have to do to help out?

@justin-stephenson
Copy link
Contributor

It would be great if someone having this issue would be able to run the FreeOTP Unit tests in Android Studio to see if any backup related encryption/decryption operations fail.

Would you be able to explain what exactly I have to do to help out?

Connect your phone to your laptop/desktop over USB/Bluetooth. Install Android Studio, clone this repo and open it as a Project. Select your phone as the device in Android Studio, right-click org.fedorahosted.freeotp (androidTest) and Run tests.

@m-from-space
Copy link

Connect your phone to your laptop/desktop over USB/Bluetooth. Install Android Studio, clone this repo and open it as a Project. Select your phone as the device in Android Studio, right-click org.fedorahosted.freeotp (androidTest) and Run tests.

Alright, I did run the tests. Out of 214 tests on my phone (OnePlus One) 209 were successful, 1 failed and I don't know about the rest, because it just stopped after the fail. Here is the log:

05-24 20:25:37.530 21410 21431 I TestRunner: started: tokenCompatBackupRestore[38](org.fedorahosted.freeotp.TokenCompatTest)
05-24 20:25:38.380 26967 26981 E ocessService0:: failed to create Unix domain socket: Operation not permitted
05-24 20:25:40.380 26967 26981 I chatty  : uid=99075(u0_i9075) ADB-JDWP Connec identical 1 line
05-24 20:25:42.381 26967 26981 E ocessService0:: failed to create Unix domain socket: Operation not permitted
05-24 20:25:43.211 21410 21422 I ahosted.freeot: Background concurrent copying GC freed 82803(6475KB) AllocSpace objects, 0(0B) LOS objects, 62% free, 3651KB/9795KB, paused 405us total 199.605ms
05-24 20:25:44.381 26967 26981 E ocessService0:: failed to create Unix domain socket: Operation not permitted
05-24 20:25:45.306 21410 21422 I ahosted.freeot: Background young concurrent copying GC freed 73736(5791KB) AllocSpace objects, 0(0B) LOS objects, 60% free, 3843KB/9795KB, paused 410us total 139.239ms
05-24 20:25:46.382 26967 26981 E ocessService0:: failed to create Unix domain socket: Operation not permitted
05-24 20:25:47.445 21410 21422 I ahosted.freeot: Background concurrent copying GC freed 80850(6336KB) AllocSpace objects, 0(0B) LOS objects, 62% free, 3650KB/9794KB, paused 388us total 196.418ms
05-24 20:25:48.383 26967 26981 E ocessService0:: failed to create Unix domain socket: Operation not permitted
05-24 20:25:48.564   442   442 I keystore: del USRPKEY_masterKey 10168
05-24 20:25:48.566   442   442 I keystore: del USRCERT_masterKey 10168
05-24 20:25:48.566   442   442 I keystore: del CACERT_masterKey 10168
05-24 20:25:48.567   442   442 I keystore: del USRPKEY_masterKey 10168
05-24 20:25:48.567   442   442 I keystore: del USRSKEY_masterKey 10168
05-24 20:25:48.567   442   442 I keystore: del USRCERT_masterKey 10168
05-24 20:25:48.568   442   442 I keystore: del CACERT_masterKey 10168
05-24 20:25:48.617 21410 21431 I Adapter : Adding token uuid [c52fa7e2-d2f2-4365-9d73-11c0d654ec9c]
05-24 20:25:48.618   442   442 I keystore: del USRPKEY_c52fa7e2-d2f2-4365-9d73-11c0d654ec9c 10168
05-24 20:25:48.619   442   442 I keystore: del USRSKEY_c52fa7e2-d2f2-4365-9d73-11c0d654ec9c 10168
05-24 20:25:48.619   442   442 I keystore: del USRCERT_c52fa7e2-d2f2-4365-9d73-11c0d654ec9c 10168
05-24 20:25:48.620   442   442 I keystore: del CACERT_c52fa7e2-d2f2-4365-9d73-11c0d654ec9c 10168
05-24 20:25:48.620   442   442 I keystore: del USRPKEY_c52fa7e2-d2f2-4365-9d73-11c0d654ec9c 10168
05-24 20:25:48.621   442   442 I keystore: del USRSKEY_c52fa7e2-d2f2-4365-9d73-11c0d654ec9c 10168
05-24 20:25:48.621   442   442 I keystore: del USRCERT_c52fa7e2-d2f2-4365-9d73-11c0d654ec9c 10168
05-24 20:25:48.621   442   442 I keystore: del CACERT_c52fa7e2-d2f2-4365-9d73-11c0d654ec9c 10168
05-24 20:25:48.652 21410 21431 I TokenPersistence: Saving [c52fa7e2-d2f2-4365-9d73-11c0d654ec9c] token backup
05-24 20:25:48.653 21410 21431 I Adapter : Token added uuid [c52fa7e2-d2f2-4365-9d73-11c0d654ec9c]
05-24 20:25:48.654   442   442 I keystore: del USRPKEY_c52fa7e2-d2f2-4365-9d73-11c0d654ec9c 10168
05-24 20:25:48.655   442   442 I keystore: del USRCERT_c52fa7e2-d2f2-4365-9d73-11c0d654ec9c 10168
05-24 20:25:48.656   442   442 I keystore: del CACERT_c52fa7e2-d2f2-4365-9d73-11c0d654ec9c 10168
05-24 20:25:50.383 26967 26981 E ocessService0:: failed to create Unix domain socket: Operation not permitted
05-24 20:25:51.653 21410 21422 I ahosted.freeot: Background concurrent copying GC freed 81018(6355KB) AllocSpace objects, 0(0B) LOS objects, 62% free, 3621KB/9765KB, paused 153us total 126.379ms
05-24 20:25:52.384 26967 26981 E ocessService0:: failed to create Unix domain socket: Operation not permitted
05-24 20:25:54.125 21410 21431 I TokenPersistence: Found [c52fa7e2-d2f2-4365-9d73-11c0d654ec9c] in backup
05-24 20:25:54.131 21410 21431 I TokenPersistence: Added [c52fa7e2-d2f2-4365-9d73-11c0d654ec9c] token to backup list
05-24 20:25:54.131 21410 21431 I TokenPersistence: Found [masterKey] in backup
05-24 20:25:54.131 21410 21431 I TokenPersistence: Skipping [masterKey]
05-24 20:25:54.131 21410 21431 I TokenPersistence: Found [c52fa7e2-d2f2-4365-9d73-11c0d654ec9c-token] in backup
05-24 20:25:54.131 21410 21431 I TokenPersistence: Skipping [c52fa7e2-d2f2-4365-9d73-11c0d654ec9c-token]
05-24 20:25:54.132 21410 21431 I Adapter : Adding token uuid [c52fa7e2-d2f2-4365-9d73-11c0d654ec9c]
05-24 20:25:54.132   442   442 I keystore: del USRPKEY_c52fa7e2-d2f2-4365-9d73-11c0d654ec9c 10168
05-24 20:25:54.133   442   442 I keystore: del USRSKEY_c52fa7e2-d2f2-4365-9d73-11c0d654ec9c 10168
05-24 20:25:54.133   442   442 I keystore: del USRCERT_c52fa7e2-d2f2-4365-9d73-11c0d654ec9c 10168
05-24 20:25:54.133   442   442 I keystore: del CACERT_c52fa7e2-d2f2-4365-9d73-11c0d654ec9c 10168
05-24 20:25:54.149 21410 21431 E Adapter : Exception
05-24 20:25:54.149 21410 21431 E Adapter : java.security.KeyStoreException: java.lang.IllegalStateException: Secure lock screen must be enabled to create keys requiring user authentication
05-24 20:25:54.149 21410 21431 E Adapter : 	at android.security.keystore.AndroidKeyStoreSpi.setSecretKeyEntry(AndroidKeyStoreSpi.java:737)
05-24 20:25:54.149 21410 21431 E Adapter : 	at android.security.keystore.AndroidKeyStoreSpi.engineSetEntry(AndroidKeyStoreSpi.java:1076)
05-24 20:25:54.149 21410 21431 E Adapter : 	at java.security.KeyStore.setEntry(KeyStore.java:1596)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.fedorahosted.freeotp.main.Adapter.add(Adapter.java:195)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.fedorahosted.freeotp.main.Adapter.restoreTokens(Adapter.java:268)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.fedorahosted.freeotp.TokenCompatTest.tokenCompatBackupRestore(TokenCompatTest.java:333)
05-24 20:25:54.149 21410 21431 E Adapter : 	at java.lang.reflect.Method.invoke(Native Method)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.Suite.runChild(Suite.java:128)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.Suite.runChild(Suite.java:27)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.Suite.runChild(Suite.java:128)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.Suite.runChild(Suite.java:27)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
05-24 20:25:54.149 21410 21431 E Adapter : 	at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
05-24 20:25:54.149 21410 21431 E Adapter : 	at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
05-24 20:25:54.149 21410 21431 E Adapter : 	at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:389)
05-24 20:25:54.149 21410 21431 E Adapter : 	at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2205)
05-24 20:25:54.149 21410 21431 E Adapter : Caused by: java.lang.IllegalStateException: Secure lock screen must be enabled to create keys requiring user authentication
05-24 20:25:54.149 21410 21431 E Adapter : 	at android.security.keystore.KeymasterUtils.getRootSid(KeymasterUtils.java:236)
05-24 20:25:54.149 21410 21431 E Adapter : 	at android.security.keystore.KeymasterUtils.addSids(KeymasterUtils.java:96)
05-24 20:25:54.149 21410 21431 E Adapter : 	at android.security.keystore.KeymasterUtils.addUserAuthArgs(KeymasterUtils.java:182)
05-24 20:25:54.149 21410 21431 E Adapter : 	at android.security.keystore.AndroidKeyStoreSpi.setSecretKeyEntry(AndroidKeyStoreSpi.java:718)
05-24 20:25:54.149 21410 21431 E Adapter : 	... 40 more
05-24 20:25:54.150 21410 21431 I TestRunner: failed: tokenCompatBackupRestore[38](org.fedorahosted.freeotp.TokenCompatTest)
05-24 20:25:54.150 21410 21431 I TestRunner: ----- begin exception -----
05-24 20:25:54.153 21410 21431 I TestRunner: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.length()' on a null object reference
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.json.JSONTokener.nextCleanInternal(JSONTokener.java:121)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.json.JSONTokener.nextValue(JSONTokener.java:98)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.json.JSONArray.<init>(JSONArray.java:94)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.json.JSONArray.<init>(JSONArray.java:110)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.fedorahosted.freeotp.TokenCompatTest.tokenCompatBackupRestore(TokenCompatTest.java:336)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at java.lang.reflect.Method.invoke(Native Method)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.Suite.runChild(Suite.java:128)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.Suite.runChild(Suite.java:27)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.Suite.runChild(Suite.java:128)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.Suite.runChild(Suite.java:27)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:389)
05-24 20:25:54.153 21410 21431 I TestRunner: 	at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2205)
05-24 20:25:54.153 21410 21431 I TestRunner: ----- end exception -----
05-24 20:25:54.157 21410 21431 I TestRunner: finished: tokenCompatBackupRestore[38](org.fedorahosted.freeotp.TokenCompatTest)

java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.length()' on a null object reference
at org.json.JSONTokener.nextCleanInternal(JSONTokener.java:121)
at org.json.JSONTokener.nextValue(JSONTokener.java:98)
at org.json.JSONArray.<init>(JSONArray.java:94)
at org.json.JSONArray.<init>(JSONArray.java:110)
at org.fedorahosted.freeotp.TokenCompatTest.tokenCompatBackupRestore(TokenCompatTest.java:336)
at java.lang.reflect.Method.invoke(Native Method)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:389)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2205)

@acigolotti
Copy link

Sorry for the lack of response from me, I didn't had time anymore.

I had to change the applicationId otherwise I couldn't run the tests.

Caused by: com.android.ddmlib.InstallException: INSTALL_FAILED_UPDATE_INCOMPATIBLE: Package org.fedorahosted.freeotp signatures do not match previously installed version; ignoring!

I have a secure lockscreen setup on my old phone, so all 210 tests ran successfully

@lemenkov
Copy link

lemenkov commented Sep 1, 2023

The same issue for me (described in #352). Eventually some tokens (just a few of them) cannot be restored even using the same password.

@Slater91
Copy link

Slater91 commented Sep 2, 2023

Version 2.0.2 has fixed this issue for me. I was able to import all of the tokens on the new phone without any problem. It should be noted that the files were the same ones I created back in January.

@bfdb
Copy link

bfdb commented Sep 21, 2023

I believe I have the same issue:

I got a new phone a couple of months ago, installed version 2.0.2 (43) on Android 13, and added a whole bunch of OTPs.
As the phone needed repairs, I backed up the database, and restored it on my factory reset previous phone (version 2.0.2 (43) on Android 8) with password.

While the password is accepted, the restored database is empty.

Moreover, as I'm restoring to an older version of Android, I cannot make use of restoring the database through Google. Instead, I have to manually restore.

@m-from-space
Copy link

Version 2.0.2 has fixed this issue for me. I was able to import all of the tokens on the new phone without any problem. It should be noted that the files were the same ones I created back in January.

That's nice to hear. But I still have the same issue. Version 2.0.2 (43) is still not able to import my old backup of keys. It just tries to import and then finishes without throwing an error and without having the keys back.

How come none of the developers can fix this serious issue here?

@rotdrop
Copy link

rotdrop commented Oct 29, 2023

I have the very same problem. One Lenovo tablet and one Fairphone, both using version 2.0.2. Both installed from play-store, however, the previously install version from F-Droid (app-version 2.0.2) also is not able to restore the backup.

I was also brave enough to try the restore on the tablet -- but well, it is hard to tell whether just nothing happen or if all keys are restored ;) Sad story. Luckily the tablet has all data still available. I think I will gradually migrate to another app.

@esspe2
Copy link

esspe2 commented Nov 3, 2023

Same kind of problem for me : impossible to transfer the database from one phone to another.
On the first one (Android 8), entries were added a long time ago in V1.X, via qr-code or manually, then FreeOTP was upgraded to V2.0.2, and one entry has been added by qr-code.
When launching the backup, no password was requested, and on the other phone (Android 13), the restore asks for a password which I don't have, so no way to transfer the database.
Since I had a backup from v1.X, I managed to generate all qr-codes and scanned them one by one following the old guide "Backing up and recovering 2FA tokens from FreeOTP", except for the last one where I had to ask the issuer to generate another secret.
Now on the Android 13 phone, no password is requested to save it in the Download directory, so same problem, no usable backup as it seems.

@boszormenyip
Copy link

I have the same problem.
Steps I do:

  • Start FreeOTP
  • Select Hamburger menu in the top right corner
  • Select backup.
  • Select Google Drive to save target

Phone specification:

  • Xiaomi MI A1
  • LineageOS version 19-20231010-NIGTHLY-tissot, which correponds an Android version 12

What really interesting is that even thought the filename has xml extension (externalBackup.xml) in fact is standard java serialized object, which I can read without any problem with a following code:

package test;

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.Map;

public class Main {
    public static void main(String[] args) throws Exception {
        var input = new ObjectInputStream(new FileInputStream("externalBackup.xml"));
        Map<String, String> obj = (Map<String, String>) input.readObject();
        obj.forEach((key, value) -> {
            System.out.format("key: %s, value: %s\n", key, value);
        });
    }


}

Excerpt from the output of the code:

key: masterKey, value: {"mAlgorithm":"PBKDF2withHmacSHA512","mEncryptedKey":{"mCipher":"AES/GCM/NoPadding","mCipherText":[],"mParameters":[],"mToken":"AES"},"mIterations":1,"mSalt":[]}
key: 0b354d0a-b1ff-4065-9903-f215d40bc36d, value: {"key":"{\"mCipher\":\"AES\/GCM\/NoPadding\",\"mCipherText\":[19,-107,97,79,16,68,54,65,-96,118,-66,22,37,101,82,62,-26,76,58,-47,-38,-64],\"mParameters\":[48,17,4,12,-97,-73,-55,-95,43,52,55,97,82,126,-99,68,2,1,16],\"mToken\":\"HmacSHA256\"}"}
key: 0b354d0a-b1ff-4065-9903-f215d40bc36d-token, value: {"algo":"SHA256","counter":0,"digits":6,"issuerExt":"Example inc","label":"[email protected]","period":30,"type":"HOTP"}

Note that I removed the content of the mCipherText, mParameters and mSalt arrays, and I changed the mIterations of the masterKey

For everyone else: You can confirm that you also have a java serialized export by opening the exported file in google drive. If you see some garbage characters, and a beginning java.util.HashMap string, then it's a serialized export.

@boekhoffm
Copy link

  1. I exported from latest version of FreeOtp (2.0.2).
  2. I uninstalled and re-installed it from Android marketplace.
  3. I restored from the backup file created in (1)

No keys were restored and no error returned.

After reading the above, I looked in the file and it is a Java Serialised Hashmap that I can read using the code posted above, although typing these entries in manually gives the "not valid base32" error in the new Authenticator (Aegis)

@lister169126
Copy link

All what we exported from our phones (I and my friend) in version 2.0.3 (44) can't import back or to other phones. Tested on 4 different devices.

I have backup from my old phone and its working but I don't now what version was here bcs I have wiped it.

@rprofijt
Copy link

Exactly the same issue here. Version 2.0.3 (44). Before I wiped my phone I tried the backup/restore and it seemed to work (tokens were all there). On the clean install the password question comes up, I enter it, it gets accepted and then the token list remains empty. As you can imagine this was very disappointing.. Lucky I had my OTP restore keys in order.

Will be looking into a replacement for this app.

@lister169126
Copy link

Exactly the same issue here. Version 2.0.3 (44). Before I wiped my phone I tried the backup/restore and it seemed to work (tokens were all there). On the clean install the password question comes up, I enter it, it gets accepted and then the token list remains empty. As you can imagine this was very disappointing.. Lucky I had my OTP restore keys in order.

Will be looking into a replacement for this app.

look at aegis - https://github.com/beemdevelopment/Aegis

@HardDie
Copy link

HardDie commented Jun 3, 2024

I seem to have started using FreeOTP when it was in version 2.0.2. I backed it up from time to time, and old backups created on version 2.0.2 could be restored on both version 2.0.2 and 2.0.3. But new backups created on version 2.0.3 could not be restored on all versions of the application.

@HardDie
Copy link

HardDie commented Jun 3, 2024

Is there a guides on how to import working backups into Aegis?
Because Aegis shows an error on all backups, even the working one in FreeOTP.

@lister169126
Copy link

Is there a guides on how to import working backups into Aegis? Because Aegis shows an error on all backups, even the working one in FreeOTP.

Sorry, from version >= 2.0 no, it's in keystorage and you don't have access to secrets, just the freeotp app.

@alexbakker
Copy link

alexbakker commented Sep 29, 2024

I took a look at this while giving beemdevelopment/Aegis#1084 another shot. There appear to be multiple issues here:

  1. When restoring, the existing in-app master key in tokenBackup.xml is overwritten with the master key from the imported backup. Imported entries are re-encrypted with the existing in-app master key, but due to the overwrite the encrypted entries and master key in tokenBackup.xml now no longer match.

    This means that if you set up a fresh installation of FreeOTP, restore a backup, and then export again: That export will be completely unrecoverable.

  2. A different KDF is used depending on the version of Android:

    private static SecretKeyFactory getSecretKeyFactory() throws NoSuchAlgorithmException {
    try {
    return SecretKeyFactory.getInstance("PBKDF2withHmacSHA512");
    } catch (NoSuchAlgorithmException e) {
    return SecretKeyFactory.getInstance("PBKDF2withHmacSHA1");
    }
    }

    Some older versions of Android do not support PBKDF2withHmacSHA512. This means that backups produced on Android 8 or later cannot be imported on prior versions of Android.

  3. The encoding of the parameters used for encryption (the nonce) differs across Android versions and Java security providers.

    mParameters = cipher.getParameters().getEncoded();

    For example, a backup made on Android 6 cannot be imported on Android 14, because conscrypt complains about the encoding of the parameters:

    java.io.IOException: Error reading ASN.1 encoding
        at com.android.org.conscrypt.NativeCrypto.asn1_read_sequence(Native Method)
    

    On older Android versions, it also seems like AlgorithmParameters.init expects a different format than the one produced by Cipher.getParameters().getEncoded().

  4. The usage of Java object serialization is a potential security issue, as previously reported: Revise backup format to not consist of serialized Java objects #381

Aegis will support importing FreeOTP 2 backups soon: beemdevelopment/Aegis#1506. I've implemented workarounds for 2, 3 and 4. But 1 is usually not (fully) recoverable. We try to salvage as many entries as possible, but if an entry was encrypted with a different master key than the one present in the file, it cannot be recovered.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests