Move a lot of the static constants to the Constants class

This commit is contained in:
Jakob Nixdorf 2018-01-04 14:12:52 +01:00
parent a3a285bebc
commit bcc82f9d5b
No known key found for this signature in database
GPG key ID: BE99BF86574A7DBC
15 changed files with 218 additions and 211 deletions

View file

@ -32,8 +32,10 @@ import org.apache.commons.codec.binary.Hex;
import org.json.JSONException;
import org.json.JSONObject;
import org.shadowice.flocke.andotp.Database.Entry;
import org.shadowice.flocke.andotp.Utilities.Constants;
import org.shadowice.flocke.andotp.Utilities.DatabaseHelper;
import org.shadowice.flocke.andotp.Utilities.EncryptionHelper;
import org.shadowice.flocke.andotp.Utilities.KeyStoreHelper;
import org.shadowice.flocke.andotp.Utilities.TokenCalculator;
import java.io.File;
@ -52,11 +54,10 @@ import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import static org.shadowice.flocke.andotp.Utilities.TokenCalculator.TOTP_DEFAULT_PERIOD;
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
@ -69,29 +70,29 @@ public class ApplicationTest extends ApplicationTestCase<Application> {
byte[] keySHA256 = "12345678901234567890123456789012".getBytes(StandardCharsets.US_ASCII);
byte[] keySHA512 = "1234567890123456789012345678901234567890123456789012345678901234".getBytes(StandardCharsets.US_ASCII);
assertEquals(94287082, TokenCalculator.TOTP_RFC6238(keySHA1, TOTP_DEFAULT_PERIOD, 59L, 8, TokenCalculator.HashAlgorithm.SHA1));
assertEquals(46119246, TokenCalculator.TOTP_RFC6238(keySHA256, TOTP_DEFAULT_PERIOD, 59L, 8, TokenCalculator.HashAlgorithm.SHA256));
assertEquals(90693936, TokenCalculator.TOTP_RFC6238(keySHA512, TOTP_DEFAULT_PERIOD, 59L, 8, TokenCalculator.HashAlgorithm.SHA512));
assertEquals(94287082, TokenCalculator.TOTP_RFC6238(keySHA1, TokenCalculator.TOTP_DEFAULT_PERIOD, 59L, 8, TokenCalculator.HashAlgorithm.SHA1));
assertEquals(46119246, TokenCalculator.TOTP_RFC6238(keySHA256, TokenCalculator.TOTP_DEFAULT_PERIOD, 59L, 8, TokenCalculator.HashAlgorithm.SHA256));
assertEquals(90693936, TokenCalculator.TOTP_RFC6238(keySHA512, TokenCalculator.TOTP_DEFAULT_PERIOD, 59L, 8, TokenCalculator.HashAlgorithm.SHA512));
assertEquals(7081804, TokenCalculator.TOTP_RFC6238(keySHA1, TOTP_DEFAULT_PERIOD, 1111111109L, 8, TokenCalculator.HashAlgorithm.SHA1));
assertEquals(68084774, TokenCalculator.TOTP_RFC6238(keySHA256, TOTP_DEFAULT_PERIOD, 1111111109L, 8, TokenCalculator.HashAlgorithm.SHA256));
assertEquals(25091201, TokenCalculator.TOTP_RFC6238(keySHA512, TOTP_DEFAULT_PERIOD, 1111111109L, 8, TokenCalculator.HashAlgorithm.SHA512));
assertEquals(7081804, TokenCalculator.TOTP_RFC6238(keySHA1, TokenCalculator.TOTP_DEFAULT_PERIOD, 1111111109L, 8, TokenCalculator.HashAlgorithm.SHA1));
assertEquals(68084774, TokenCalculator.TOTP_RFC6238(keySHA256, TokenCalculator.TOTP_DEFAULT_PERIOD, 1111111109L, 8, TokenCalculator.HashAlgorithm.SHA256));
assertEquals(25091201, TokenCalculator.TOTP_RFC6238(keySHA512, TokenCalculator.TOTP_DEFAULT_PERIOD, 1111111109L, 8, TokenCalculator.HashAlgorithm.SHA512));
assertEquals(14050471, TokenCalculator.TOTP_RFC6238(keySHA1, TOTP_DEFAULT_PERIOD, 1111111111L, 8, TokenCalculator.HashAlgorithm.SHA1));
assertEquals(67062674, TokenCalculator.TOTP_RFC6238(keySHA256, TOTP_DEFAULT_PERIOD, 1111111111L, 8, TokenCalculator.HashAlgorithm.SHA256));
assertEquals(99943326, TokenCalculator.TOTP_RFC6238(keySHA512, TOTP_DEFAULT_PERIOD, 1111111111L, 8, TokenCalculator.HashAlgorithm.SHA512));
assertEquals(14050471, TokenCalculator.TOTP_RFC6238(keySHA1, TokenCalculator.TOTP_DEFAULT_PERIOD, 1111111111L, 8, TokenCalculator.HashAlgorithm.SHA1));
assertEquals(67062674, TokenCalculator.TOTP_RFC6238(keySHA256, TokenCalculator.TOTP_DEFAULT_PERIOD, 1111111111L, 8, TokenCalculator.HashAlgorithm.SHA256));
assertEquals(99943326, TokenCalculator.TOTP_RFC6238(keySHA512, TokenCalculator.TOTP_DEFAULT_PERIOD, 1111111111L, 8, TokenCalculator.HashAlgorithm.SHA512));
assertEquals(89005924, TokenCalculator.TOTP_RFC6238(keySHA1, TOTP_DEFAULT_PERIOD, 1234567890L, 8, TokenCalculator.HashAlgorithm.SHA1));
assertEquals(91819424, TokenCalculator.TOTP_RFC6238(keySHA256, TOTP_DEFAULT_PERIOD, 1234567890L, 8, TokenCalculator.HashAlgorithm.SHA256));
assertEquals(93441116, TokenCalculator.TOTP_RFC6238(keySHA512, TOTP_DEFAULT_PERIOD, 1234567890L, 8, TokenCalculator.HashAlgorithm.SHA512));
assertEquals(89005924, TokenCalculator.TOTP_RFC6238(keySHA1, TokenCalculator.TOTP_DEFAULT_PERIOD, 1234567890L, 8, TokenCalculator.HashAlgorithm.SHA1));
assertEquals(91819424, TokenCalculator.TOTP_RFC6238(keySHA256, TokenCalculator.TOTP_DEFAULT_PERIOD, 1234567890L, 8, TokenCalculator.HashAlgorithm.SHA256));
assertEquals(93441116, TokenCalculator.TOTP_RFC6238(keySHA512, TokenCalculator.TOTP_DEFAULT_PERIOD, 1234567890L, 8, TokenCalculator.HashAlgorithm.SHA512));
assertEquals(69279037, TokenCalculator.TOTP_RFC6238(keySHA1, TOTP_DEFAULT_PERIOD, 2000000000L, 8, TokenCalculator.HashAlgorithm.SHA1));
assertEquals(90698825, TokenCalculator.TOTP_RFC6238(keySHA256, TOTP_DEFAULT_PERIOD, 2000000000L, 8, TokenCalculator.HashAlgorithm.SHA256));
assertEquals(38618901, TokenCalculator.TOTP_RFC6238(keySHA512, TOTP_DEFAULT_PERIOD, 2000000000L, 8, TokenCalculator.HashAlgorithm.SHA512));
assertEquals(69279037, TokenCalculator.TOTP_RFC6238(keySHA1, TokenCalculator.TOTP_DEFAULT_PERIOD, 2000000000L, 8, TokenCalculator.HashAlgorithm.SHA1));
assertEquals(90698825, TokenCalculator.TOTP_RFC6238(keySHA256, TokenCalculator.TOTP_DEFAULT_PERIOD, 2000000000L, 8, TokenCalculator.HashAlgorithm.SHA256));
assertEquals(38618901, TokenCalculator.TOTP_RFC6238(keySHA512, TokenCalculator.TOTP_DEFAULT_PERIOD, 2000000000L, 8, TokenCalculator.HashAlgorithm.SHA512));
assertEquals(65353130, TokenCalculator.TOTP_RFC6238(keySHA1, TOTP_DEFAULT_PERIOD, 20000000000L, 8, TokenCalculator.HashAlgorithm.SHA1));
assertEquals(77737706, TokenCalculator.TOTP_RFC6238(keySHA256, TOTP_DEFAULT_PERIOD, 20000000000L, 8, TokenCalculator.HashAlgorithm.SHA256));
assertEquals(47863826, TokenCalculator.TOTP_RFC6238(keySHA512, TOTP_DEFAULT_PERIOD, 20000000000L, 8, TokenCalculator.HashAlgorithm.SHA512));
assertEquals(65353130, TokenCalculator.TOTP_RFC6238(keySHA1, TokenCalculator.TOTP_DEFAULT_PERIOD, 20000000000L, 8, TokenCalculator.HashAlgorithm.SHA1));
assertEquals(77737706, TokenCalculator.TOTP_RFC6238(keySHA256, TokenCalculator.TOTP_DEFAULT_PERIOD, 20000000000L, 8, TokenCalculator.HashAlgorithm.SHA256));
assertEquals(47863826, TokenCalculator.TOTP_RFC6238(keySHA512, TokenCalculator.TOTP_DEFAULT_PERIOD, 20000000000L, 8, TokenCalculator.HashAlgorithm.SHA512));
}
@ -106,6 +107,8 @@ public class ApplicationTest extends ApplicationTestCase<Application> {
"\"digits\":6," +
"\"type\":\"TOTP\"," +
"\"algorithm\":\"SHA1\"," +
"\"thumbnail\":\"Default\"," +
"\"last_used\":0," +
"\"tags\":[\"test1\",\"test2\"]}";
Entry e = new Entry(new JSONObject(s));
@ -170,10 +173,11 @@ public class ApplicationTest extends ApplicationTestCase<Application> {
keyStore.load(null);
keyStore.deleteEntry("settings");
new File(context.getFilesDir() + "/" + DatabaseHelper.SETTINGS_FILE).delete();
new File(context.getFilesDir() + "/" + DatabaseHelper.KEY_FILE).delete();
new File(context.getFilesDir() + "/" + Constants.FILENAME_DATABASE).delete();
new File(context.getFilesDir() + "/" + Constants.FILENAME_ENCRYPTED_KEY).delete();
ArrayList<Entry> b = DatabaseHelper.loadDatabase(context);
SecretKey encryptionKey = KeyStoreHelper.loadEncryptionKeyFromKeyStore(context);
ArrayList<Entry> b = DatabaseHelper.loadDatabase(context, encryptionKey);
assertEquals(0, b.size());
ArrayList<Entry> a = new ArrayList<>();
@ -187,13 +191,13 @@ public class ApplicationTest extends ApplicationTestCase<Application> {
e.setSecret("secret2".getBytes());
a.add(e);
DatabaseHelper.saveDatabase(context, a);
b = DatabaseHelper.loadDatabase(context);
DatabaseHelper.saveDatabase(context, a, encryptionKey);
b = DatabaseHelper.loadDatabase(context, encryptionKey);
assertEquals(a, b);
new File(context.getFilesDir() + "/" + DatabaseHelper.SETTINGS_FILE).delete();
new File(context.getFilesDir() + "/" + DatabaseHelper.KEY_FILE).delete();
new File(context.getFilesDir() + "/" + Constants.FILENAME_DATABASE).delete();
new File(context.getFilesDir() + "/" + Constants.FILENAME_ENCRYPTED_KEY).delete();
}
public void testEncryptionHelper() throws NoSuchPaddingException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, UnsupportedEncodingException, InvalidAlgorithmParameterException, DecoderException {

View file

@ -42,6 +42,7 @@ import android.widget.Toast;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.shadowice.flocke.andotp.R;
import org.shadowice.flocke.andotp.Utilities.Constants;
import org.shadowice.flocke.andotp.Utilities.EncryptionHelper;
import java.security.NoSuchAlgorithmException;
@ -52,10 +53,6 @@ import static org.shadowice.flocke.andotp.Utilities.Constants.AuthMethod;
public class AuthenticateActivity extends ThemedActivity
implements EditText.OnEditorActionListener {
public static final String AUTH_EXTRA_NAME_PASSWORD_KEY = "password_key";
public static final String AUTH_EXTRA_NAME_NEW_ENCRYPTION = "new_encryption";
public static final String AUTH_EXTRA_NAME_MESSAGE = "message";
private String password;
AuthMethod authMethod;
@ -80,8 +77,8 @@ public class AuthenticateActivity extends ThemedActivity
View v = stub.inflate();
Intent callingIntent = getIntent();
int labelMsg = callingIntent.getIntExtra(AUTH_EXTRA_NAME_MESSAGE, R.string.auth_msg_authenticate);
newEncryption = callingIntent.getStringExtra(AUTH_EXTRA_NAME_NEW_ENCRYPTION);
int labelMsg = callingIntent.getIntExtra(Constants.EXTRA_AUTH_MESSAGE, R.string.auth_msg_authenticate);
newEncryption = callingIntent.getStringExtra(Constants.EXTRA_AUTH_NEW_ENCRYPTION);
TextView passwordLabel = v.findViewById(R.id.passwordLabel);
TextInputLayout passwordLayout = v.findViewById(R.id.passwordLayout);
@ -172,10 +169,10 @@ public class AuthenticateActivity extends ThemedActivity
Intent data = new Intent();
if (newEncryption != null && ! newEncryption.isEmpty())
data.putExtra(AUTH_EXTRA_NAME_NEW_ENCRYPTION, newEncryption);
data.putExtra(Constants.EXTRA_AUTH_NEW_ENCRYPTION, newEncryption);
if (key != null)
data.putExtra(AUTH_EXTRA_NAME_PASSWORD_KEY, key);
data.putExtra(Constants.EXTRA_AUTH_PASSWORD_KEY, key);
if (success)
setResult(RESULT_OK, data);

View file

@ -50,6 +50,7 @@ import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpServiceConnection;
import org.shadowice.flocke.andotp.Database.Entry;
import org.shadowice.flocke.andotp.R;
import org.shadowice.flocke.andotp.Utilities.Constants;
import org.shadowice.flocke.andotp.Utilities.DatabaseHelper;
import org.shadowice.flocke.andotp.Utilities.EncryptionHelper;
import org.shadowice.flocke.andotp.Utilities.FileHelper;
@ -64,31 +65,6 @@ import java.util.ArrayList;
import javax.crypto.SecretKey;
public class BackupActivity extends BaseActivity {
private final static int INTENT_OPEN_DOCUMENT_PLAIN = 200;
private final static int INTENT_SAVE_DOCUMENT_PLAIN = 201;
private final static int INTENT_OPEN_DOCUMENT_CRYPT = 202;
private final static int INTENT_SAVE_DOCUMENT_CRYPT = 203;
private final static int INTENT_OPEN_DOCUMENT_PGP = 204;
private final static int INTENT_SAVE_DOCUMENT_PGP = 205;
private final static int INTENT_ENCRYPT_PGP = 206;
private final static int INTENT_DECRYPT_PGP = 207;
private final static int PERMISSIONS_REQUEST_READ_IMPORT_PLAIN = 210;
private final static int PERMISSIONS_REQUEST_WRITE_EXPORT_PLAIN = 211;
private final static int PERMISSIONS_REQUEST_READ_IMPORT_CRYPT = 212;
private final static int PERMISSIONS_REQUEST_WRITE_EXPORT_CRYPT = 213;
private final static int PERMISSIONS_REQUEST_READ_IMPORT_PGP = 214;
private final static int PERMISSIONS_REQUEST_WRITE_EXPORT_PGP = 215;
private static final String DEFAULT_BACKUP_FILENAME_PLAIN = "otp_accounts.json";
private static final String DEFAULT_BACKUP_FILENAME_CRYPT = "otp_accounts.json.aes";
private static final String DEFAULT_BACKUP_FILENAME_PGP = "otp_accounts.json.gpg";
private static final String DEFAULT_BACKUP_MIMETYPE_PLAIN = "application/json";
private static final String DEFAULT_BACKUP_MIMETYPE_CRYPT = "binary/aes";
private static final String DEFAULT_BACKUP_MIMETYPE_PGP = "application/pgp-encrypted";
public static final String BACKUP_EXTRA_NAME_ENCRYPTION_KEY = "encryption_key";
private SecretKey encryptionKey = null;
private OpenPgpServiceConnection pgpServiceConnection;
@ -116,7 +92,7 @@ public class BackupActivity extends BaseActivity {
View v = stub.inflate();
Intent callingIntent = getIntent();
byte[] keyMaterial = callingIntent.getByteArrayExtra(BACKUP_EXTRA_NAME_ENCRYPTION_KEY);
byte[] keyMaterial = callingIntent.getByteArrayExtra(Constants.EXTRA_BACKUP_ENCRYPTION_KEY);
encryptionKey = EncryptionHelper.generateSymmetricKey(keyMaterial);
// Plain-text
@ -134,7 +110,7 @@ public class BackupActivity extends BaseActivity {
restorePlain.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
openFileWithPermissions(INTENT_OPEN_DOCUMENT_PLAIN, PERMISSIONS_REQUEST_READ_IMPORT_PLAIN);
openFileWithPermissions(Constants.INTENT_BACKUP_OPEN_DOCUMENT_PLAIN, Constants.PERMISSIONS_BACKUP_READ_IMPORT_PLAIN);
}
});
@ -157,14 +133,14 @@ public class BackupActivity extends BaseActivity {
backupCrypt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
saveFileWithPermissions(DEFAULT_BACKUP_MIMETYPE_CRYPT, DEFAULT_BACKUP_FILENAME_CRYPT, INTENT_SAVE_DOCUMENT_CRYPT, PERMISSIONS_REQUEST_WRITE_EXPORT_CRYPT);
saveFileWithPermissions(Constants.BACKUP_MIMETYPE_CRYPT, Constants.BACKUP_FILENAME_CRYPT, Constants.INTENT_BACKUP_SAVE_DOCUMENT_CRYPT, Constants.PERMISSIONS_BACKUP_WRITE_EXPORT_CRYPT);
}
});
restoreCrypt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
openFileWithPermissions(INTENT_OPEN_DOCUMENT_CRYPT, PERMISSIONS_REQUEST_READ_IMPORT_CRYPT);
openFileWithPermissions(Constants.INTENT_BACKUP_OPEN_DOCUMENT_CRYPT, Constants.PERMISSIONS_BACKUP_READ_IMPORT_CRYPT);
}
});
@ -192,14 +168,14 @@ public class BackupActivity extends BaseActivity {
backupPGP.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
saveFileWithPermissions(DEFAULT_BACKUP_MIMETYPE_PGP, DEFAULT_BACKUP_FILENAME_PGP, INTENT_SAVE_DOCUMENT_PGP, PERMISSIONS_REQUEST_WRITE_EXPORT_PGP);
saveFileWithPermissions(Constants.BACKUP_MIMETYPE_PGP, Constants.BACKUP_FILENAME_PGP, Constants.INTENT_BACKUP_SAVE_DOCUMENT_PGP, Constants.PERMISSIONS_BACKUP_WRITE_EXPORT_PGP);
}
});
restorePGP.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
openFileWithPermissions(INTENT_OPEN_DOCUMENT_PGP, PERMISSIONS_REQUEST_READ_IMPORT_PGP);
openFileWithPermissions(Constants.INTENT_BACKUP_OPEN_DOCUMENT_PGP, Constants.PERMISSIONS_BACKUP_READ_IMPORT_PGP);
}
});
}
@ -240,39 +216,39 @@ public class BackupActivity extends BaseActivity {
// Get the result from permission requests
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
if (requestCode == PERMISSIONS_REQUEST_READ_IMPORT_PLAIN) {
if (requestCode == Constants.PERMISSIONS_BACKUP_READ_IMPORT_PLAIN) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
showOpenFileSelector(INTENT_OPEN_DOCUMENT_PLAIN);
showOpenFileSelector(Constants.INTENT_BACKUP_OPEN_DOCUMENT_PLAIN);
} else {
Toast.makeText(this, R.string.backup_toast_storage_permissions, Toast.LENGTH_LONG).show();
}
} else if (requestCode == PERMISSIONS_REQUEST_WRITE_EXPORT_PLAIN) {
} else if (requestCode == Constants.PERMISSIONS_BACKUP_WRITE_EXPORT_PLAIN) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
showSaveFileSelector(DEFAULT_BACKUP_MIMETYPE_PLAIN, DEFAULT_BACKUP_FILENAME_PLAIN, INTENT_SAVE_DOCUMENT_PLAIN);
showSaveFileSelector(Constants.BACKUP_MIMETYPE_PLAIN, Constants.BACKUP_FILENAME_PLAIN, Constants.INTENT_BACKUP_SAVE_DOCUMENT_PLAIN);
} else {
Toast.makeText(this, R.string.backup_toast_storage_permissions, Toast.LENGTH_LONG).show();
}
} else if (requestCode == PERMISSIONS_REQUEST_READ_IMPORT_CRYPT) {
} else if (requestCode == Constants.PERMISSIONS_BACKUP_READ_IMPORT_CRYPT) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
showOpenFileSelector(INTENT_OPEN_DOCUMENT_CRYPT);
showOpenFileSelector(Constants.INTENT_BACKUP_OPEN_DOCUMENT_CRYPT);
} else {
Toast.makeText(this, R.string.backup_toast_storage_permissions, Toast.LENGTH_LONG).show();
}
} else if (requestCode == PERMISSIONS_REQUEST_WRITE_EXPORT_CRYPT) {
} else if (requestCode == Constants.PERMISSIONS_BACKUP_WRITE_EXPORT_CRYPT) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
showSaveFileSelector(DEFAULT_BACKUP_MIMETYPE_CRYPT, DEFAULT_BACKUP_FILENAME_CRYPT, INTENT_SAVE_DOCUMENT_CRYPT);
showSaveFileSelector(Constants.BACKUP_MIMETYPE_CRYPT, Constants.BACKUP_FILENAME_CRYPT, Constants.INTENT_BACKUP_SAVE_DOCUMENT_CRYPT);
} else {
Toast.makeText(this, R.string.backup_toast_storage_permissions, Toast.LENGTH_LONG).show();
}
} else if (requestCode == PERMISSIONS_REQUEST_READ_IMPORT_PGP) {
} else if (requestCode == Constants.PERMISSIONS_BACKUP_READ_IMPORT_PGP) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
showOpenFileSelector(INTENT_OPEN_DOCUMENT_PGP);
showOpenFileSelector(Constants.INTENT_BACKUP_OPEN_DOCUMENT_PGP);
} else {
Toast.makeText(this, R.string.backup_toast_storage_permissions, Toast.LENGTH_LONG).show();
}
} else if (requestCode == PERMISSIONS_REQUEST_WRITE_EXPORT_PGP) {
} else if (requestCode == Constants.PERMISSIONS_BACKUP_WRITE_EXPORT_PGP) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
showSaveFileSelector(DEFAULT_BACKUP_MIMETYPE_PGP, DEFAULT_BACKUP_FILENAME_PGP, INTENT_SAVE_DOCUMENT_PGP);
showSaveFileSelector(Constants.BACKUP_MIMETYPE_PGP, Constants.BACKUP_FILENAME_PGP, Constants.INTENT_BACKUP_SAVE_DOCUMENT_PGP);
} else {
Toast.makeText(this, R.string.backup_toast_storage_permissions, Toast.LENGTH_LONG).show();
}
@ -286,31 +262,31 @@ public class BackupActivity extends BaseActivity {
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
if (requestCode == INTENT_OPEN_DOCUMENT_PLAIN && resultCode == RESULT_OK) {
if (requestCode == Constants.INTENT_BACKUP_OPEN_DOCUMENT_PLAIN && resultCode == RESULT_OK) {
if (intent != null) {
doRestorePlain(intent.getData());
}
} else if (requestCode == INTENT_SAVE_DOCUMENT_PLAIN && resultCode == RESULT_OK) {
} else if (requestCode == Constants.INTENT_BACKUP_SAVE_DOCUMENT_PLAIN && resultCode == RESULT_OK) {
if (intent != null) {
doBackupPlain(intent.getData());
}
} else if (requestCode == INTENT_OPEN_DOCUMENT_CRYPT && resultCode == RESULT_OK) {
} else if (requestCode == Constants.INTENT_BACKUP_OPEN_DOCUMENT_CRYPT && resultCode == RESULT_OK) {
if (intent != null) {
doRestoreCrypt(intent.getData());
}
} else if (requestCode == INTENT_SAVE_DOCUMENT_CRYPT && resultCode == RESULT_OK) {
} else if (requestCode == Constants.INTENT_BACKUP_SAVE_DOCUMENT_CRYPT && resultCode == RESULT_OK) {
if (intent != null) {
doBackupCrypt(intent.getData());
}
} else if (requestCode == INTENT_OPEN_DOCUMENT_PGP && resultCode == RESULT_OK) {
} else if (requestCode == Constants.INTENT_BACKUP_OPEN_DOCUMENT_PGP && resultCode == RESULT_OK) {
if (intent != null)
restoreEncryptedWithPGP(intent.getData(), null);
} else if (requestCode == INTENT_SAVE_DOCUMENT_PGP && resultCode == RESULT_OK) {
} else if (requestCode == Constants.INTENT_BACKUP_SAVE_DOCUMENT_PGP && resultCode == RESULT_OK) {
if (intent != null)
backupEncryptedWithPGP(intent.getData(), null);
} else if (requestCode == INTENT_ENCRYPT_PGP && resultCode == RESULT_OK) {
} else if (requestCode == Constants.INTENT_BACKUP_ENCRYPT_PGP && resultCode == RESULT_OK) {
backupEncryptedWithPGP(encryptTargetFile, intent);
} else if (requestCode == INTENT_DECRYPT_PGP && resultCode == RESULT_OK) {
} else if (requestCode == Constants.INTENT_BACKUP_DECRYPT_PGP && resultCode == RESULT_OK) {
restoreEncryptedWithPGP(decryptSourceFile, intent);
}
}
@ -324,12 +300,12 @@ public class BackupActivity extends BaseActivity {
intent.setType("*/*");
startActivityForResult(intent, intentId);
} else {
if (intentId == INTENT_OPEN_DOCUMENT_PLAIN)
doRestorePlain(Tools.buildUri(settings.getBackupDir(), DEFAULT_BACKUP_FILENAME_PLAIN));
else if (intentId == INTENT_OPEN_DOCUMENT_CRYPT)
doRestoreCrypt(Tools.buildUri(settings.getBackupDir(), DEFAULT_BACKUP_FILENAME_CRYPT));
else if (intentId == INTENT_OPEN_DOCUMENT_PGP)
restoreEncryptedWithPGP(Tools.buildUri(settings.getBackupDir(), DEFAULT_BACKUP_FILENAME_PGP), null);
if (intentId == Constants.INTENT_BACKUP_OPEN_DOCUMENT_PLAIN)
doRestorePlain(Tools.buildUri(settings.getBackupDir(), Constants.BACKUP_FILENAME_PLAIN));
else if (intentId == Constants.INTENT_BACKUP_OPEN_DOCUMENT_CRYPT)
doRestoreCrypt(Tools.buildUri(settings.getBackupDir(), Constants.BACKUP_FILENAME_CRYPT));
else if (intentId == Constants.INTENT_BACKUP_OPEN_DOCUMENT_PGP)
restoreEncryptedWithPGP(Tools.buildUri(settings.getBackupDir(), Constants.BACKUP_FILENAME_PGP), null);
}
}
@ -342,12 +318,12 @@ public class BackupActivity extends BaseActivity {
startActivityForResult(intent, intentId);
} else {
if (Tools.mkdir(settings.getBackupDir())) {
if (intentId == INTENT_SAVE_DOCUMENT_PLAIN)
doBackupPlain(Tools.buildUri(settings.getBackupDir(), DEFAULT_BACKUP_FILENAME_PLAIN));
else if (intentId == INTENT_SAVE_DOCUMENT_CRYPT)
doBackupCrypt(Tools.buildUri(settings.getBackupDir(), DEFAULT_BACKUP_FILENAME_CRYPT));
else if (intentId == INTENT_SAVE_DOCUMENT_PGP)
backupEncryptedWithPGP(Tools.buildUri(settings.getBackupDir(), DEFAULT_BACKUP_FILENAME_PGP), null);
if (intentId == Constants.INTENT_BACKUP_SAVE_DOCUMENT_PLAIN)
doBackupPlain(Tools.buildUri(settings.getBackupDir(), Constants.BACKUP_FILENAME_PLAIN));
else if (intentId == Constants.INTENT_BACKUP_SAVE_DOCUMENT_CRYPT)
doBackupCrypt(Tools.buildUri(settings.getBackupDir(), Constants.BACKUP_FILENAME_CRYPT));
else if (intentId == Constants.INTENT_BACKUP_SAVE_DOCUMENT_PGP)
backupEncryptedWithPGP(Tools.buildUri(settings.getBackupDir(), Constants.BACKUP_FILENAME_PGP), null);
} else {
Toast.makeText(this, R.string.backup_toast_mkdir_failed, Toast.LENGTH_LONG).show();
}
@ -428,7 +404,7 @@ public class BackupActivity extends BaseActivity {
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
saveFileWithPermissions(DEFAULT_BACKUP_MIMETYPE_PLAIN, DEFAULT_BACKUP_FILENAME_PLAIN, INTENT_SAVE_DOCUMENT_PLAIN, PERMISSIONS_REQUEST_WRITE_EXPORT_PLAIN);
saveFileWithPermissions(Constants.BACKUP_MIMETYPE_PLAIN, Constants.BACKUP_FILENAME_PLAIN, Constants.INTENT_BACKUP_SAVE_DOCUMENT_PLAIN, Constants.PERMISSIONS_BACKUP_WRITE_EXPORT_PLAIN);
}
})
.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
@ -523,7 +499,7 @@ public class BackupActivity extends BaseActivity {
ByteArrayOutputStream os = new ByteArrayOutputStream();
OpenPgpApi api = new OpenPgpApi(this, pgpServiceConnection.getService());
Intent result = api.executeApi(decryptIntent, is, os);
handleOpenPGPResult(result, os, uri, INTENT_DECRYPT_PGP);
handleOpenPGPResult(result, os, uri, Constants.INTENT_BACKUP_DECRYPT_PGP);
}
private void doBackupEncrypted(Uri uri, String data) {
@ -563,7 +539,7 @@ public class BackupActivity extends BaseActivity {
ByteArrayOutputStream os = new ByteArrayOutputStream();
OpenPgpApi api = new OpenPgpApi(this, pgpServiceConnection.getService());
Intent result = api.executeApi(encryptIntent, is, os);
handleOpenPGPResult(result, os, uri, INTENT_ENCRYPT_PGP);
handleOpenPGPResult(result, os, uri, Constants.INTENT_BACKUP_ENCRYPT_PGP);
}
public String outputStreamToString(ByteArrayOutputStream os) {
@ -572,10 +548,10 @@ public class BackupActivity extends BaseActivity {
public void handleOpenPGPResult(Intent result, ByteArrayOutputStream os, Uri file, int requestCode) {
if (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR) == OpenPgpApi.RESULT_CODE_SUCCESS) {
if (requestCode == INTENT_ENCRYPT_PGP) {
if (requestCode == Constants.INTENT_BACKUP_ENCRYPT_PGP) {
if (os != null)
doBackupEncrypted(file, outputStreamToString(os));
} else if (requestCode == INTENT_DECRYPT_PGP) {
} else if (requestCode == Constants.INTENT_BACKUP_DECRYPT_PGP) {
if (os != null) {
if (settings.getOpenPGPVerify()) {
OpenPgpSignatureResult sigResult = result.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE);
@ -594,9 +570,9 @@ public class BackupActivity extends BaseActivity {
PendingIntent pi = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
// Small hack to keep the target file even after user interaction
if (requestCode == INTENT_ENCRYPT_PGP) {
if (requestCode == Constants.INTENT_BACKUP_ENCRYPT_PGP) {
encryptTargetFile = file;
} else if (requestCode == INTENT_DECRYPT_PGP) {
} else if (requestCode == Constants.INTENT_BACKUP_DECRYPT_PGP) {
decryptSourceFile = file;
}

View file

@ -58,6 +58,7 @@ import com.google.zxing.integration.android.IntentResult;
import org.shadowice.flocke.andotp.Database.Entry;
import org.shadowice.flocke.andotp.R;
import org.shadowice.flocke.andotp.Utilities.Constants;
import org.shadowice.flocke.andotp.Utilities.EncryptionHelper;
import org.shadowice.flocke.andotp.Utilities.KeyStoreHelper;
import org.shadowice.flocke.andotp.Utilities.TokenCalculator;
@ -72,20 +73,12 @@ import java.util.HashMap;
import javax.crypto.SecretKey;
import static org.shadowice.flocke.andotp.Activities.AuthenticateActivity.AUTH_EXTRA_NAME_MESSAGE;
import static org.shadowice.flocke.andotp.Activities.AuthenticateActivity.AUTH_EXTRA_NAME_PASSWORD_KEY;
import static org.shadowice.flocke.andotp.Activities.BackupActivity.BACKUP_EXTRA_NAME_ENCRYPTION_KEY;
import static org.shadowice.flocke.andotp.Activities.SettingsActivity.SETTINGS_EXTRA_NAME_ENCRYPTION_CHANGED;
import static org.shadowice.flocke.andotp.Activities.SettingsActivity.SETTINGS_EXTRA_NAME_ENCRYPTION_KEY;
import static org.shadowice.flocke.andotp.Utilities.Constants.AuthMethod;
import static org.shadowice.flocke.andotp.Utilities.Constants.EncryptionType;
import static org.shadowice.flocke.andotp.Utilities.Settings.SortMode;
import static org.shadowice.flocke.andotp.Utilities.Constants.SortMode;
public class MainActivity extends BaseActivity
implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final int INTENT_INTERNAL_AUTHENTICATE = 100;
private static final int INTENT_INTERNAL_SETTINGS = 101;
private static final int INTENT_INTERNAL_BACKUP = 102;
private EntriesCardAdapter adapter;
private FloatingActionMenu floatingActionMenu;
@ -135,12 +128,12 @@ public class MainActivity extends BaseActivity
KeyguardManager km = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP && km.isKeyguardSecure()) {
Intent authIntent = km.createConfirmDeviceCredentialIntent(getString(R.string.dialog_title_auth), getString(R.string.dialog_msg_auth));
startActivityForResult(authIntent, INTENT_INTERNAL_AUTHENTICATE);
startActivityForResult(authIntent, Constants.INTENT_MAIN_AUTHENTICATE);
}
} else if (authMethod == AuthMethod.PASSWORD || authMethod == AuthMethod.PIN) {
Intent authIntent = new Intent(this, AuthenticateActivity.class);
authIntent.putExtra(AUTH_EXTRA_NAME_MESSAGE, messageId);
startActivityForResult(authIntent, INTENT_INTERNAL_AUTHENTICATE);
authIntent.putExtra(Constants.EXTRA_AUTH_MESSAGE, messageId);
startActivityForResult(authIntent, Constants.INTENT_MAIN_AUTHENTICATE);
}
}
@ -385,18 +378,18 @@ public class MainActivity extends BaseActivity
Toast.makeText(this, R.string.toast_invalid_qr_code, Toast.LENGTH_LONG).show();
}
}
} else if (requestCode == INTENT_INTERNAL_BACKUP && resultCode == RESULT_OK) {
} else if (requestCode == Constants.INTENT_MAIN_BACKUP && resultCode == RESULT_OK) {
if (intent.getBooleanExtra("reload", false)) {
adapter.loadEntries();
refreshTags();
}
} else if (requestCode == INTENT_INTERNAL_SETTINGS && resultCode == RESULT_OK) {
boolean encryptionChanged = intent.getBooleanExtra(SETTINGS_EXTRA_NAME_ENCRYPTION_CHANGED, false);
byte[] newKey = intent.getByteArrayExtra(SETTINGS_EXTRA_NAME_ENCRYPTION_KEY);
} else if (requestCode == Constants.INTENT_MAIN_SETTINGS && resultCode == RESULT_OK) {
boolean encryptionChanged = intent.getBooleanExtra(Constants.EXTRA_SETTINGS_ENCRYPTION_CHANGED, false);
byte[] newKey = intent.getByteArrayExtra(Constants.EXTRA_SETTINGS_ENCRYPTION_KEY);
if (encryptionChanged)
updateEncryption(newKey);
} else if (requestCode == INTENT_INTERNAL_AUTHENTICATE) {
} else if (requestCode == Constants.INTENT_MAIN_AUTHENTICATE) {
if (resultCode != RESULT_OK) {
Toast.makeText(getBaseContext(), R.string.toast_auth_failed_fatal, Toast.LENGTH_LONG).show();
@ -408,7 +401,7 @@ public class MainActivity extends BaseActivity
} else {
requireAuthentication = false;
byte[] authKey = intent.getByteArrayExtra(AUTH_EXTRA_NAME_PASSWORD_KEY);
byte[] authKey = intent.getByteArrayExtra(Constants.EXTRA_AUTH_PASSWORD_KEY);
updateEncryption(authKey);
}
}
@ -509,12 +502,12 @@ public class MainActivity extends BaseActivity
if (id == R.id.action_backup) {
Intent backupIntent = new Intent(this, BackupActivity.class);
backupIntent.putExtra(BACKUP_EXTRA_NAME_ENCRYPTION_KEY, adapter.getEncryptionKey().getEncoded());
startActivityForResult(backupIntent, INTENT_INTERNAL_BACKUP);
backupIntent.putExtra(Constants.EXTRA_BACKUP_ENCRYPTION_KEY, adapter.getEncryptionKey().getEncoded());
startActivityForResult(backupIntent, Constants.INTENT_MAIN_BACKUP);
} else if (id == R.id.action_settings) {
Intent settingsIntent = new Intent(this, SettingsActivity.class);
settingsIntent.putExtra(SETTINGS_EXTRA_NAME_ENCRYPTION_KEY, adapter.getEncryptionKey().getEncoded());
startActivityForResult(settingsIntent, INTENT_INTERNAL_SETTINGS);
settingsIntent.putExtra(Constants.EXTRA_SETTINGS_ENCRYPTION_KEY, adapter.getEncryptionKey().getEncoded());
startActivityForResult(settingsIntent, Constants.INTENT_MAIN_SETTINGS);
} else if (id == R.id.action_about){
Intent aboutIntent = new Intent(this, AboutActivity.class);
startActivity(aboutIntent);

View file

@ -39,6 +39,7 @@ import org.openintents.openpgp.util.OpenPgpKeyPreference;
import org.shadowice.flocke.andotp.Database.Entry;
import org.shadowice.flocke.andotp.Preferences.CredentialsPreference;
import org.shadowice.flocke.andotp.R;
import org.shadowice.flocke.andotp.Utilities.Constants;
import org.shadowice.flocke.andotp.Utilities.DatabaseHelper;
import org.shadowice.flocke.andotp.Utilities.EncryptionHelper;
import org.shadowice.flocke.andotp.Utilities.KeyStoreHelper;
@ -48,19 +49,11 @@ import java.util.ArrayList;
import javax.crypto.SecretKey;
import static org.shadowice.flocke.andotp.Activities.AuthenticateActivity.AUTH_EXTRA_NAME_MESSAGE;
import static org.shadowice.flocke.andotp.Activities.AuthenticateActivity.AUTH_EXTRA_NAME_NEW_ENCRYPTION;
import static org.shadowice.flocke.andotp.Activities.AuthenticateActivity.AUTH_EXTRA_NAME_PASSWORD_KEY;
import static org.shadowice.flocke.andotp.Utilities.Constants.AuthMethod;
import static org.shadowice.flocke.andotp.Utilities.Constants.EncryptionType;
public class SettingsActivity extends BaseActivity
implements SharedPreferences.OnSharedPreferenceChangeListener{
private static final int INTENT_INTERNAL_AUTHENTICATE = 300;
public static final String SETTINGS_EXTRA_NAME_ENCRYPTION_CHANGED = "encryption_changed";
public static final String SETTINGS_EXTRA_NAME_ENCRYPTION_KEY = "encryption_key";
SettingsFragment fragment;
SecretKey encryptionKey = null;
@ -80,7 +73,7 @@ public class SettingsActivity extends BaseActivity
stub.inflate();
Intent callingIntent = getIntent();
byte[] keyMaterial = callingIntent.getByteArrayExtra(SETTINGS_EXTRA_NAME_ENCRYPTION_KEY);
byte[] keyMaterial = callingIntent.getByteArrayExtra(Constants.EXTRA_SETTINGS_ENCRYPTION_KEY);
encryptionKey = EncryptionHelper.generateSymmetricKey(keyMaterial);
fragment = new SettingsFragment();
@ -96,8 +89,8 @@ public class SettingsActivity extends BaseActivity
public void finishWithResult() {
Intent data = new Intent();
data.putExtra(SETTINGS_EXTRA_NAME_ENCRYPTION_CHANGED, encryptionChanged);
data.putExtra(SETTINGS_EXTRA_NAME_ENCRYPTION_KEY, encryptionKey.getEncoded());
data.putExtra(Constants.EXTRA_SETTINGS_ENCRYPTION_CHANGED, encryptionChanged);
data.putExtra(Constants.EXTRA_SETTINGS_ENCRYPTION_KEY, encryptionKey.getEncoded());
setResult(RESULT_OK, data);
finish();
@ -125,9 +118,9 @@ public class SettingsActivity extends BaseActivity
private void tryEncryptionChangeWithAuth(EncryptionType newEnc) {
Intent authIntent = new Intent(this, AuthenticateActivity.class);
authIntent.putExtra(AUTH_EXTRA_NAME_NEW_ENCRYPTION, newEnc.name());
authIntent.putExtra(AUTH_EXTRA_NAME_MESSAGE, R.string.auth_msg_confirm_encryption);
startActivityForResult(authIntent, INTENT_INTERNAL_AUTHENTICATE);
authIntent.putExtra(Constants.EXTRA_AUTH_NEW_ENCRYPTION, newEnc.name());
authIntent.putExtra(Constants.EXTRA_AUTH_MESSAGE, R.string.auth_msg_confirm_encryption);
startActivityForResult(authIntent, Constants.INTENT_SETTINGS_AUTHENTICATE);
}
private boolean tryEncryptionChange(EncryptionType newEnc, byte[] newKey) {
@ -177,10 +170,10 @@ public class SettingsActivity extends BaseActivity
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == INTENT_INTERNAL_AUTHENTICATE) {
if (requestCode == Constants.INTENT_SETTINGS_AUTHENTICATE) {
if (resultCode == RESULT_OK) {
byte[] authKey = data.getByteArrayExtra(AUTH_EXTRA_NAME_PASSWORD_KEY);
String newEnc = data.getStringExtra(AUTH_EXTRA_NAME_NEW_ENCRYPTION);
byte[] authKey = data.getByteArrayExtra(Constants.EXTRA_AUTH_PASSWORD_KEY);
String newEnc = data.getStringExtra(Constants.EXTRA_AUTH_NEW_ENCRYPTION);
if (authKey != null && authKey.length > 0 && newEnc != null && !newEnc.isEmpty()) {
EncryptionType newEncType = EncryptionType.valueOf(newEnc);

View file

@ -41,7 +41,9 @@ import java.util.Objects;
import java.util.Set;
public class Entry {
public enum OTPType { TOTP, STEAM}
public enum OTPType {
TOTP, STEAM
}
public static Set<OTPType> PublicTypes = EnumSet.of(OTPType.TOTP);
private static final OTPType DEFAULT_TYPE = OTPType.TOTP;

View file

@ -39,6 +39,7 @@ import android.widget.Button;
import android.widget.EditText;
import org.shadowice.flocke.andotp.R;
import org.shadowice.flocke.andotp.Utilities.Constants;
import org.shadowice.flocke.andotp.Utilities.EncryptionHelper;
import org.shadowice.flocke.andotp.Utilities.KeyStoreHelper;
@ -52,7 +53,6 @@ public class PasswordEncryptedPreference extends DialogPreference
PASSWORD, PIN
}
public static final String KEY_ALIAS = "password";
private KeyPair key;
private static final String DEFAULT_VALUE = "";
@ -70,7 +70,7 @@ public class PasswordEncryptedPreference extends DialogPreference
super(context, attrs);
try {
key = KeyStoreHelper.loadOrGenerateAsymmetricKeyPair(context, KEY_ALIAS);
key = KeyStoreHelper.loadOrGenerateAsymmetricKeyPair(context, Constants.KEYSTORE_ALIAS_PASSWORD);
} catch (Exception e) {
e.printStackTrace();
}

View file

@ -22,7 +22,12 @@
package org.shadowice.flocke.andotp.Utilities;
import android.os.Environment;
import java.io.File;
public class Constants {
// Enums
public enum AuthMethod {
NONE, PASSWORD, PIN, DEVICE
}
@ -31,8 +36,74 @@ public class Constants {
KEYSTORE, PASSWORD
}
public enum SortMode {
UNSORTED, LABEL, LAST_USED
}
// Intents (Format: A0x with A = parent Activity, x = number of the intent)
public final static int INTENT_MAIN_AUTHENTICATE = 100;
public final static int INTENT_MAIN_SETTINGS = 101;
public final static int INTENT_MAIN_BACKUP = 102;
public final static int INTENT_BACKUP_OPEN_DOCUMENT_PLAIN = 200;
public final static int INTENT_BACKUP_SAVE_DOCUMENT_PLAIN = 201;
public final static int INTENT_BACKUP_OPEN_DOCUMENT_CRYPT = 202;
public final static int INTENT_BACKUP_SAVE_DOCUMENT_CRYPT = 203;
public final static int INTENT_BACKUP_OPEN_DOCUMENT_PGP = 204;
public final static int INTENT_BACKUP_SAVE_DOCUMENT_PGP = 205;
public final static int INTENT_BACKUP_ENCRYPT_PGP = 206;
public final static int INTENT_BACKUP_DECRYPT_PGP = 207;
public static final int INTENT_SETTINGS_AUTHENTICATE = 300;
// Permission requests (Format: A1x with A = parent Activity, x = number of the request)
public final static int PERMISSIONS_BACKUP_READ_IMPORT_PLAIN = 210;
public final static int PERMISSIONS_BACKUP_WRITE_EXPORT_PLAIN = 211;
public final static int PERMISSIONS_BACKUP_READ_IMPORT_CRYPT = 212;
public final static int PERMISSIONS_BACKUP_WRITE_EXPORT_CRYPT = 213;
public final static int PERMISSIONS_BACKUP_READ_IMPORT_PGP = 214;
public final static int PERMISSIONS_BACKUP_WRITE_EXPORT_PGP = 215;
// Intent extras
public final static String EXTRA_AUTH_PASSWORD_KEY = "password_key";
public final static String EXTRA_AUTH_NEW_ENCRYPTION = "new_encryption";
public final static String EXTRA_AUTH_MESSAGE = "message";
public final static String EXTRA_BACKUP_ENCRYPTION_KEY = "encryption_key";
public final static String EXTRA_SETTINGS_ENCRYPTION_CHANGED = "encryption_changed";
public final static String EXTRA_SETTINGS_ENCRYPTION_KEY = "encryption_key";
// Encryption algorithms and definitions
final static String ALGORITHM_SYMMETRIC = "AES/GCM/NoPadding";
final static String ALGORITHM_ASYMMETRIC = "RSA/ECB/PKCS1Padding";
final static int AUTH_SALT_LENGTH = 16;
final static int ENCRYPTION_KEY_LENGTH = 16;
final static int ENCRYPTION_IV_LENGTH = 12;
final static int PBKDF2_MIN_ITERATIONS = 1000;
final static int PBKDF2_MAX_ITERATIONS = 5000;
final static int PBKDF2_DEFAULT_ITERATIONS = 1000;
final static int PBKDF2_LENGTH = 512;
final static int PBKDF2_SALT_LENGTH = 16;
// KeyStore
public final static String KEYSTORE_ALIAS_PASSWORD = "password";
public final static String KEYSTORE_ALIAS_WRAPPING = "settings";
// Database files
public final static String FILENAME_ENCRYPTED_KEY = "otp.key";
public final static String FILENAME_DATABASE = "secrets.dat";
public final static String FILENAME_DATABASE_BACKUP = "secrets.dat.bck";
// Backup files
public final static String BACKUP_FOLDER = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "andOTP";
public final static String BACKUP_FILENAME_PLAIN = "otp_accounts.json";
public final static String BACKUP_FILENAME_CRYPT = "otp_accounts.json.aes";
public final static String BACKUP_FILENAME_PGP = "otp_accounts.json.gpg";
public final static String BACKUP_MIMETYPE_PLAIN = "application/json";
public final static String BACKUP_MIMETYPE_CRYPT = "binary/aes";
public final static String BACKUP_MIMETYPE_PGP = "application/pgp-encrypted";
}

View file

@ -41,11 +41,9 @@ import java.util.ArrayList;
import javax.crypto.SecretKey;
public class DatabaseHelper {
public static final String SETTINGS_FILE = "secrets.dat";
public static final String SETTINGS_FILE_BACKUP = "secrets.dat.bck";
public static void wipeDatabase(Context context) {
File db = new File(context.getFilesDir() + "/" + SETTINGS_FILE);
File db = new File(context.getFilesDir() + "/" + Constants.FILENAME_DATABASE);
db.delete();
}
@ -63,8 +61,8 @@ public class DatabaseHelper {
}
public static boolean backupDatabase(Context context) {
File original = new File(context.getFilesDir() + "/" + SETTINGS_FILE);
File backup = new File(context.getFilesDir() + "/" + SETTINGS_FILE_BACKUP);
File original = new File(context.getFilesDir() + "/" + Constants.FILENAME_DATABASE);
File backup = new File(context.getFilesDir() + "/" + Constants.FILENAME_DATABASE_BACKUP);
try {
copyFile(original, backup);
@ -76,8 +74,8 @@ public class DatabaseHelper {
}
public static boolean restoreDatabaseBackup(Context context) {
File original = new File(context.getFilesDir() + "/" + SETTINGS_FILE);
File backup = new File(context.getFilesDir() + "/" + SETTINGS_FILE_BACKUP);
File original = new File(context.getFilesDir() + "/" + Constants.FILENAME_DATABASE);
File backup = new File(context.getFilesDir() + "/" + Constants.FILENAME_DATABASE_BACKUP);
try {
copyFile(backup, original);
@ -100,7 +98,7 @@ public class DatabaseHelper {
try {
byte[] data = EncryptionHelper.encrypt(encryptionKey, jsonString.getBytes());
FileHelper.writeBytesToFile(new File(context.getFilesDir() + "/" + SETTINGS_FILE), data);
FileHelper.writeBytesToFile(new File(context.getFilesDir() + "/" + Constants.FILENAME_DATABASE), data);
} catch (Exception error) {
error.printStackTrace();
@ -115,7 +113,7 @@ public class DatabaseHelper {
if (encryptionKey != null) {
try {
byte[] data = FileHelper.readFileToBytes(new File(context.getFilesDir() + "/" + SETTINGS_FILE));
byte[] data = FileHelper.readFileToBytes(new File(context.getFilesDir() + "/" + Constants.FILENAME_DATABASE));
data = EncryptionHelper.decrypt(encryptionKey, data);
entries = stringToEntries(new String(data));

View file

@ -51,18 +51,7 @@ import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import static org.shadowice.flocke.andotp.Utilities.Constants.ALGORITHM_ASYMMETRIC;
import static org.shadowice.flocke.andotp.Utilities.Constants.ALGORITHM_SYMMETRIC;
public class EncryptionHelper {
private final static int KEY_LENGTH = 16;
private final static int IV_LENGTH = 12;
public final static int PBKDF2_MIN_ITERATIONS = 1000;
public final static int PBKDF2_MAX_ITERATIONS = 5000;
public final static int PBKDF2_OLD_DEFAULT_ITERATIONS = 1000;
private final static int PBKDF2_LENGTH = 512;
public static class PBKDF2Credentials {
public byte[] password;
public byte[] key;
@ -70,7 +59,7 @@ public class EncryptionHelper {
public static int generateRandomIterations() {
Random rand = new Random();
return rand.nextInt((PBKDF2_MAX_ITERATIONS - PBKDF2_MIN_ITERATIONS) + 1) + PBKDF2_MIN_ITERATIONS;
return rand.nextInt((Constants.PBKDF2_MAX_ITERATIONS - Constants.PBKDF2_MIN_ITERATIONS) + 1) + Constants.PBKDF2_MIN_ITERATIONS;
}
public static byte[] generateRandom(int length) {
@ -83,7 +72,7 @@ public class EncryptionHelper {
public static PBKDF2Credentials generatePBKDF2Credentials(String password, byte[] salt, int iter)
throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, iter, PBKDF2_LENGTH);
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, iter, Constants.PBKDF2_LENGTH);
byte[] array = secretKeyFactory.generateSecret(keySpec).getEncoded();
@ -109,7 +98,7 @@ public class EncryptionHelper {
public static byte[] encrypt(SecretKey secretKey, IvParameterSpec iv, byte[] plainText)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException {
Cipher cipher = Cipher.getInstance(ALGORITHM_SYMMETRIC);
Cipher cipher = Cipher.getInstance(Constants.ALGORITHM_SYMMETRIC);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
return cipher.doFinal(plainText);
@ -117,7 +106,7 @@ public class EncryptionHelper {
public static byte[] encrypt(SecretKey secretKey, byte[] plaintext)
throws NoSuchPaddingException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, UnsupportedEncodingException, InvalidAlgorithmParameterException {
final byte[] iv = new byte[IV_LENGTH];
final byte[] iv = new byte[Constants.ENCRYPTION_IV_LENGTH];
new SecureRandom().nextBytes(iv);
byte[] cipherText = encrypt(secretKey, new IvParameterSpec(iv), plaintext);
@ -131,7 +120,7 @@ public class EncryptionHelper {
public static byte[] encrypt(PublicKey publicKey, byte[] plaintext)
throws NoSuchPaddingException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, UnsupportedEncodingException, InvalidAlgorithmParameterException {
Cipher cipher = Cipher.getInstance(ALGORITHM_ASYMMETRIC);
Cipher cipher = Cipher.getInstance(Constants.ALGORITHM_ASYMMETRIC);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(plaintext);
@ -139,7 +128,7 @@ public class EncryptionHelper {
public static byte[] decrypt(SecretKey secretKey, IvParameterSpec iv, byte[] cipherText)
throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
Cipher cipher = Cipher.getInstance(ALGORITHM_SYMMETRIC);
Cipher cipher = Cipher.getInstance(Constants.ALGORITHM_SYMMETRIC);
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
return cipher.doFinal(cipherText);
@ -147,15 +136,15 @@ public class EncryptionHelper {
public static byte[] decrypt(SecretKey secretKey, byte[] cipherText)
throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
byte[] iv = Arrays.copyOfRange(cipherText, 0, IV_LENGTH);
byte[] encrypted = Arrays.copyOfRange(cipherText, IV_LENGTH, cipherText.length);
byte[] iv = Arrays.copyOfRange(cipherText, 0, Constants.ENCRYPTION_IV_LENGTH);
byte[] encrypted = Arrays.copyOfRange(cipherText, Constants.ENCRYPTION_IV_LENGTH, cipherText.length);
return decrypt(secretKey, new IvParameterSpec(iv), encrypted);
}
public static byte[] decrypt(PrivateKey privateKey, byte[] cipherText)
throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
Cipher cipher = Cipher.getInstance(ALGORITHM_ASYMMETRIC);
Cipher cipher = Cipher.getInstance(Constants.ALGORITHM_ASYMMETRIC);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(cipherText);
@ -172,7 +161,7 @@ public class EncryptionHelper {
// Generate secret key if none exists
if (!keyFile.exists()) {
final byte[] raw = EncryptionHelper.generateRandom(KEY_LENGTH);
final byte[] raw = EncryptionHelper.generateRandom(Constants.ENCRYPTION_KEY_LENGTH);
final SecretKey key = new SecretKeySpec(raw, "AES");
final byte[] wrapped = wrapper.wrap(key);

View file

@ -45,19 +45,16 @@ import javax.crypto.SecretKey;
import javax.security.auth.x500.X500Principal;
public class KeyStoreHelper {
public static final String KEY_FILE = "otp.key";
public static final String KEYSTORE_ALIAS_WRAPPING = "settings";
public static void wipeKeys(Context context) {
File keyFile = new File(context.getFilesDir() + "/" + KEY_FILE);
File keyFile = new File(context.getFilesDir() + "/" + Constants.FILENAME_ENCRYPTED_KEY);
keyFile.delete();
try {
final KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
if (keyStore.containsAlias(KEYSTORE_ALIAS_WRAPPING))
keyStore.deleteEntry(KEYSTORE_ALIAS_WRAPPING);
if (keyStore.containsAlias(Constants.KEYSTORE_ALIAS_WRAPPING))
keyStore.deleteEntry(Constants.KEYSTORE_ALIAS_WRAPPING);
} catch (GeneralSecurityException | IOException e) {
e.printStackTrace();
}
@ -106,7 +103,7 @@ public class KeyStoreHelper {
KeyPair pair = null;
try {
pair = KeyStoreHelper.loadOrGenerateAsymmetricKeyPair(context, KEYSTORE_ALIAS_WRAPPING);
pair = KeyStoreHelper.loadOrGenerateAsymmetricKeyPair(context, Constants.KEYSTORE_ALIAS_WRAPPING);
} catch (GeneralSecurityException | IOException e) {
e.printStackTrace();
UIHelper.showGenericDialog(context, R.string.dialog_title_encryption_error, R.string.dialog_msg_keystore_error);
@ -114,7 +111,7 @@ public class KeyStoreHelper {
if (pair != null) {
try {
return EncryptionHelper.loadOrGenerateWrappedKey(new File(context.getFilesDir() + "/" + KEY_FILE), pair);
return EncryptionHelper.loadOrGenerateWrappedKey(new File(context.getFilesDir() + "/" + Constants.FILENAME_ENCRYPTED_KEY), pair);
} catch (GeneralSecurityException | IOException e) {
e.printStackTrace();
UIHelper.showGenericDialog(context, R.string.dialog_title_encryption_error, R.string.dialog_msg_unwrap_error);

View file

@ -26,8 +26,6 @@ import java.security.KeyStore;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import static org.shadowice.flocke.andotp.Utilities.Constants.ALGORITHM_ASYMMETRIC;
/**
* Wraps {@link SecretKey} instances using a public/private key pair stored in
* the platform {@link KeyStore}. This allows us to protect symmetric keys with
@ -49,7 +47,7 @@ public class SecretKeyWrapper {
@SuppressLint("GetInstance")
public SecretKeyWrapper(KeyPair keyPair)
throws GeneralSecurityException, IOException {
mCipher = Cipher.getInstance(ALGORITHM_ASYMMETRIC);
mCipher = Cipher.getInstance(Constants.ALGORITHM_ASYMMETRIC);
mPair = keyPair;
}

View file

@ -24,15 +24,12 @@ package org.shadowice.flocke.andotp.Utilities;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.util.Base64;
import android.widget.Toast;
import org.shadowice.flocke.andotp.Preferences.CredentialsPreference;
import org.shadowice.flocke.andotp.R;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
@ -42,21 +39,14 @@ import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import static org.shadowice.flocke.andotp.Preferences.PasswordEncryptedPreference.KEY_ALIAS;
import static org.shadowice.flocke.andotp.Utilities.EncryptionHelper.PBKDF2_OLD_DEFAULT_ITERATIONS;
import static org.shadowice.flocke.andotp.Utilities.Constants.AuthMethod;
import static org.shadowice.flocke.andotp.Utilities.Constants.EncryptionType;
import static org.shadowice.flocke.andotp.Utilities.Constants.SortMode;
public class Settings {
private static final String DEFAULT_BACKUP_FOLDER = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "andOTP";
private Context context;
private SharedPreferences settings;
public enum SortMode {
UNSORTED, LABEL, LAST_USED
}
public Settings(Context context) {
this.context = context;
this.settings = PreferenceManager.getDefaultSharedPreferences(context);
@ -68,7 +58,7 @@ public class Settings {
private void setupDeviceDependedDefaults() {
if (! settings.contains(getResString(R.string.settings_key_backup_directory))
|| settings.getString(getResString(R.string.settings_key_backup_directory), "").isEmpty()) {
setString(R.string.settings_key_backup_directory, DEFAULT_BACKUP_FOLDER);
setString(R.string.settings_key_backup_directory, Constants.BACKUP_FOLDER);
}
}
@ -87,7 +77,7 @@ public class Settings {
String plainPassword = getBackupPassword();
try {
KeyPair key = KeyStoreHelper.loadOrGenerateAsymmetricKeyPair(context, KEY_ALIAS);
KeyPair key = KeyStoreHelper.loadOrGenerateAsymmetricKeyPair(context, Constants.KEYSTORE_ALIAS_PASSWORD);
byte[] encPassword = EncryptionHelper.encrypt(key.getPublic(), plainPassword.getBytes(StandardCharsets.UTF_8));
setString(R.string.settings_key_backup_password_enc, Base64.encodeToString(encPassword, Base64.URL_SAFE));
@ -270,7 +260,7 @@ public class Settings {
String storedSalt = getString(R.string.settings_key_auth_salt, "");
if (storedSalt.isEmpty()) {
byte[] newSalt = EncryptionHelper.generateRandom(Constants.AUTH_SALT_LENGTH);
byte[] newSalt = EncryptionHelper.generateRandom(Constants.PBKDF2_SALT_LENGTH);
setSalt(newSalt);
return newSalt;
@ -281,9 +271,9 @@ public class Settings {
public int getIterations(AuthMethod method) {
if (method == AuthMethod.PASSWORD)
return getIntValue(R.string.settings_key_auth_password_iter, PBKDF2_OLD_DEFAULT_ITERATIONS);
return getIntValue(R.string.settings_key_auth_password_iter, Constants.PBKDF2_DEFAULT_ITERATIONS);
else if (method == AuthMethod.PIN)
return getIntValue(R.string.settings_key_auth_pin_iter, PBKDF2_OLD_DEFAULT_ITERATIONS);
return getIntValue(R.string.settings_key_auth_pin_iter, Constants.PBKDF2_DEFAULT_ITERATIONS);
else
return 0;
}
@ -359,7 +349,7 @@ public class Settings {
}
public String getBackupDir() {
return getString(R.string.settings_key_backup_directory, DEFAULT_BACKUP_FOLDER);
return getString(R.string.settings_key_backup_directory, Constants.BACKUP_FOLDER);
}
public String getBackupPassword() {
@ -373,7 +363,7 @@ public class Settings {
String password = "";
try {
KeyPair key = KeyStoreHelper.loadOrGenerateAsymmetricKeyPair(context, KEY_ALIAS);
KeyPair key = KeyStoreHelper.loadOrGenerateAsymmetricKeyPair(context, Constants.KEYSTORE_ALIAS_PASSWORD);
password = new String(EncryptionHelper.decrypt(key.getPrivate(), encPassword), StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();

View file

@ -64,7 +64,7 @@ import java.util.concurrent.Callable;
import javax.crypto.SecretKey;
import static org.shadowice.flocke.andotp.Utilities.Settings.SortMode;
import static org.shadowice.flocke.andotp.Utilities.Constants.SortMode;
public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
implements ItemTouchHelperAdapter, Filterable {

View file

@ -43,7 +43,6 @@ import java.util.List;
public class EntryViewHolder extends RecyclerView.ViewHolder
implements ItemTouchHelperViewHolder {
private Context context;
private Callback callback;
private boolean tapToReveal;