@ -3,13 +3,16 @@ package com.beemdevelopment.aegis.importers;
import android.content.Context ;
import android.util.Xml ;
import com.beemdevelopment.aegis. vault.VaultEntry ;
import com.beemdevelopment.aegis. R ;
import com.beemdevelopment.aegis.encoding.Base32 ;
import com.beemdevelopment.aegis.encoding.Base64 ;
import com.beemdevelopment.aegis.encoding.EncodingException ;
import com.beemdevelopment.aegis.otp.OtpInfo ;
import com.beemdevelopment.aegis.otp.OtpInfoException ;
import com.beemdevelopment.aegis.otp.TotpInfo ;
import com.beemdevelopment.aegis.ui.Dialogs ;
import com.beemdevelopment.aegis.util.PreferenceParser ;
import com.beemdevelopment.aegis.vault.VaultEntry ;
import org.json.JSONArray ;
import org.json.JSONException ;
@ -18,11 +21,33 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException ;
import java.io.IOException ;
import java.nio.charset.StandardCharsets ;
import java.security.InvalidAlgorithmParameterException ;
import java.security.InvalidKeyException ;
import java.security.NoSuchAlgorithmException ;
import java.security.spec.InvalidKeySpecException ;
import java.security.spec.KeySpec ;
import javax.crypto.BadPaddingException ;
import javax.crypto.Cipher ;
import javax.crypto.IllegalBlockSizeException ;
import javax.crypto.NoSuchPaddingException ;
import javax.crypto.SecretKey ;
import javax.crypto.SecretKeyFactory ;
import javax.crypto.spec.IvParameterSpec ;
import javax.crypto.spec.PBEKeySpec ;
public class AuthyImporter extends DatabaseImporter {
private static final String _subPath = "shared_prefs/com.authy.storage.tokens.authenticator.xml" ;
private static final String _pkgName = "com.authy.authy" ;
private static final int ITERATIONS = 1000 ;
private static final int KEY_SIZE = 256 ;
private static final byte [ ] IV = new byte [ ] {
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00
} ;
public AuthyImporter ( Context context ) {
super ( context ) ;
}
@ -52,18 +77,76 @@ public class AuthyImporter extends DatabaseImporter {
}
}
return new State ( array ) ;
for ( int i = 0 ; i < array . length ( ) ; i + + ) {
if ( ! array . getJSONObject ( i ) . has ( "decryptedSecret" ) ) {
return new EncryptedState ( array ) ;
}
}
return new DecryptedState ( array ) ;
} catch ( XmlPullParserException | JSONException | IOException e ) {
throw new DatabaseImporterException ( e ) ;
}
}
public static class State extends DatabaseImporter . State {
private JSONArray _obj ;
public static class EncryptedState extends DatabaseImporter . State {
private JSONArray _array ;
private EncryptedState ( JSONArray array ) {
super ( true ) ;
_array = array ;
}
@Override
public void decrypt ( Context context , DecryptListener listener ) {
Dialogs . showPasswordInputDialog ( context , R . string . enter_password_authy_message , password - > {
try {
for ( int i = 0 ; i < _array . length ( ) ; i + + ) {
JSONObject obj = _array . getJSONObject ( i ) ;
String secretString = obj . optString ( "encryptedSecret" , null ) ;
if ( secretString = = null ) {
continue ;
}
byte [ ] encryptedSecret = Base64 . decode ( secretString ) ;
byte [ ] salt = obj . getString ( "salt" ) . getBytes ( StandardCharsets . UTF_8 ) ;
SecretKeyFactory factory = SecretKeyFactory . getInstance ( "PBKDF2WithHmacSHA1" ) ;
KeySpec spec = new PBEKeySpec ( password , salt , ITERATIONS , KEY_SIZE ) ;
SecretKey key = factory . generateSecret ( spec ) ;
Cipher cipher = Cipher . getInstance ( "AES/CBC/PKCS7Padding" ) ;
IvParameterSpec ivSpec = new IvParameterSpec ( IV ) ;
cipher . init ( Cipher . DECRYPT_MODE , key , ivSpec ) ;
byte [ ] secret = cipher . doFinal ( encryptedSecret ) ;
obj . remove ( "encryptedSecret" ) ;
obj . remove ( "salt" ) ;
obj . put ( "decryptedSecret" , new String ( secret , StandardCharsets . UTF_8 ) ) ;
}
DecryptedState state = new DecryptedState ( _array ) ;
listener . onStateDecrypted ( state ) ;
} catch ( JSONException
| EncodingException
| NoSuchAlgorithmException
| InvalidKeySpecException
| InvalidAlgorithmParameterException
| InvalidKeyException
| NoSuchPaddingException
| BadPaddingException
| IllegalBlockSizeException e ) {
listener . onError ( e ) ;
}
} ) ;
}
}
public static class DecryptedState extends DatabaseImporter . State {
private JSONArray _array ;
private State ( JSONArray obj ) {
private DecryptedState( JSONArray array ) {
super ( false ) ;
_obj = obj ;
_ array = array ;
}
@Override
@ -71,8 +154,8 @@ public class AuthyImporter extends DatabaseImporter {
Result result = new Result ( ) ;
try {
for ( int i = 0 ; i < _ obj . length ( ) ; i + + ) {
JSONObject entryObj = _ obj . getJSONObject ( i ) ;
for ( int i = 0 ; i < _ array . length ( ) ; i + + ) {
JSONObject entryObj = _ array . getJSONObject ( i ) ;
try {
VaultEntry entry = convertEntry ( entryObj ) ;
result . addEntry ( entry ) ;
@ -90,7 +173,8 @@ public class AuthyImporter extends DatabaseImporter {
private static VaultEntry convertEntry ( JSONObject entry ) throws DatabaseImporterEntryException {
try {
AuthyEntryInfo authyEntryInfo = new AuthyEntryInfo ( ) ;
authyEntryInfo . OriginalName = entry . getString ( "originalName" ) ;
authyEntryInfo . OriginalName = entry . optString ( "originalName" , null ) ;
authyEntryInfo . OriginalIssuer = entry . optString ( "originalIssuer" , null ) ;
authyEntryInfo . AccountType = entry . getString ( "accountType" ) ;
authyEntryInfo . Name = entry . optString ( "name" ) ;
@ -108,24 +192,27 @@ public class AuthyImporter extends DatabaseImporter {
}
private static void sanitizeEntryInfo ( AuthyEntryInfo info ) {
String sep e rator = "" ;
String sep a rator = "" ;
if ( info . OriginalName . contains ( ":" ) ) {
if ( info . OriginalIssuer ! = null ) {
info . Issuer = info . OriginalIssuer ;
} else if ( info . OriginalName ! = null & & info . OriginalName . contains ( ":" ) ) {
info . Issuer = info . OriginalName . substring ( 0 , info . OriginalName . indexOf ( ":" ) ) ;
seperator = ":" ;
sep a rator = ":" ;
} else if ( info . Name . contains ( " - " ) ) {
info . Issuer = info . Name . substring ( 0 , info . Name . indexOf ( " - " ) ) ;
sep e rator = " - " ;
sep a rator = " - " ;
} else {
info . Issuer = info . AccountType . substring ( 0 , 1 ) . toUpperCase ( ) + info . AccountType . substring ( 1 ) ;
}
info . Name = info . Name . replace ( info . Issuer + sep e rator, "" ) ;
info . Name = info . Name . replace ( info . Issuer + sep a rator, "" ) ;
}
}
private static class AuthyEntryInfo {
String OriginalName ;
String OriginalIssuer ;
String AccountType ;
String Issuer ;
String Name ;