mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-05-04 20:30:36 +00:00
commit
a5ec7666ec
40 changed files with 974 additions and 321 deletions
|
@ -4,11 +4,13 @@ import android.content.Context;
|
|||
|
||||
import com.beemdevelopment.aegis.encoding.EncodingException;
|
||||
import com.beemdevelopment.aegis.otp.OtpInfoException;
|
||||
import com.beemdevelopment.aegis.ui.tasks.PasswordSlotDecryptTask;
|
||||
import com.beemdevelopment.aegis.util.IOUtils;
|
||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||
import com.beemdevelopment.aegis.vault.VaultFile;
|
||||
import com.beemdevelopment.aegis.vault.VaultFileCredentials;
|
||||
import com.beemdevelopment.aegis.vault.VaultFileException;
|
||||
import com.beemdevelopment.aegis.vault.slots.PasswordSlot;
|
||||
import com.beemdevelopment.aegis.vault.slots.SlotList;
|
||||
import com.topjohnwu.superuser.io.SuFile;
|
||||
|
||||
|
@ -18,6 +20,7 @@ import org.json.JSONObject;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
public class AegisImporter extends DatabaseImporter {
|
||||
|
||||
|
@ -56,11 +59,24 @@ public class AegisImporter extends DatabaseImporter {
|
|||
return _file.getHeader().getSlots();
|
||||
}
|
||||
|
||||
public State decrypt(VaultFileCredentials creds) throws VaultFileException {
|
||||
JSONObject obj = _file.getContent(creds);
|
||||
public State decrypt(VaultFileCredentials creds) throws DatabaseImporterException {
|
||||
JSONObject obj;
|
||||
try {
|
||||
obj = _file.getContent(creds);
|
||||
} catch (VaultFileException e) {
|
||||
throw new DatabaseImporterException(e);
|
||||
}
|
||||
|
||||
return new DecryptedState(obj);
|
||||
}
|
||||
|
||||
public State decrypt(char[] password) throws DatabaseImporterException {
|
||||
List<PasswordSlot> slots = getSlots().findAll(PasswordSlot.class);
|
||||
PasswordSlotDecryptTask.Result result = PasswordSlotDecryptTask.decrypt(slots, password);
|
||||
VaultFileCredentials creds = new VaultFileCredentials(result.getKey(), getSlots());
|
||||
return decrypt(creds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decrypt(Context context, DecryptListener listener) {
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ public class AndOtpImporter extends DatabaseImporter {
|
|||
_data = data;
|
||||
}
|
||||
|
||||
private DecryptedState decryptData(SecretKey key, int offset) throws DatabaseImporterException {
|
||||
private DecryptedState decryptContent(SecretKey key, int offset) throws DatabaseImporterException {
|
||||
byte[] nonce = Arrays.copyOfRange(_data, offset, offset + NONCE_SIZE);
|
||||
byte[] tag = Arrays.copyOfRange(_data, _data.length - TAG_SIZE, _data.length);
|
||||
CryptParameters params = new CryptParameters(nonce, tag);
|
||||
|
@ -116,35 +116,52 @@ public class AndOtpImporter extends DatabaseImporter {
|
|||
}
|
||||
}
|
||||
|
||||
private KeyDerivationParams getKeyDerivationParams(char[] password) throws DatabaseImporterException {
|
||||
byte[] iterBytes = Arrays.copyOfRange(_data, 0, INT_SIZE);
|
||||
int iterations = ByteBuffer.wrap(iterBytes).getInt();
|
||||
if (iterations < 1) {
|
||||
throw new DatabaseImporterException(String.format("Invalid number of iterations for PBKDF: %d", iterations));
|
||||
}
|
||||
|
||||
byte[] salt = Arrays.copyOfRange(_data, INT_SIZE, INT_SIZE + SALT_SIZE);
|
||||
return new KeyDerivationParams(password, salt, iterations);
|
||||
}
|
||||
|
||||
protected DecryptedState decryptOldFormat(char[] password) throws DatabaseImporterException {
|
||||
// WARNING: DON'T DO THIS IN YOUR OWN CODE
|
||||
// this exists solely to support the old andOTP backup format
|
||||
// it is not a secure way to derive a key from a password
|
||||
MessageDigest hash;
|
||||
try {
|
||||
hash = MessageDigest.getInstance("SHA-256");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
byte[] keyBytes = hash.digest(CryptoUtils.toBytes(password));
|
||||
SecretKey key = new SecretKeySpec(keyBytes, "AES");
|
||||
return decryptContent(key, 0);
|
||||
}
|
||||
|
||||
protected DecryptedState decryptNewFormat(SecretKey key) throws DatabaseImporterException {
|
||||
return decryptContent(key, INT_SIZE + SALT_SIZE);
|
||||
}
|
||||
|
||||
protected DecryptedState decryptNewFormat(char[] password)
|
||||
throws DatabaseImporterException {
|
||||
KeyDerivationParams params = getKeyDerivationParams(password);
|
||||
SecretKey key = AndOtpKeyDerivationTask.deriveKey(params);
|
||||
return decryptNewFormat(key);
|
||||
}
|
||||
|
||||
private void decrypt(Context context, char[] password, boolean oldFormat, DecryptListener listener) throws DatabaseImporterException {
|
||||
if (oldFormat) {
|
||||
// WARNING: DON'T DO THIS IN YOUR OWN CODE
|
||||
// this exists solely to support the old andOTP backup format
|
||||
// it is not a secure way to derive a key from a password
|
||||
MessageDigest hash;
|
||||
try {
|
||||
hash = MessageDigest.getInstance("SHA-256");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
byte[] keyBytes = hash.digest(CryptoUtils.toBytes(password));
|
||||
SecretKey key = new SecretKeySpec(keyBytes, "AES");
|
||||
DecryptedState state = decryptData(key, 0);
|
||||
DecryptedState state = decryptOldFormat(password);
|
||||
listener.onStateDecrypted(state);
|
||||
} else {
|
||||
int offset = INT_SIZE + SALT_SIZE;
|
||||
|
||||
byte[] iterBytes = Arrays.copyOfRange(_data, 0, INT_SIZE);
|
||||
int iterations = ByteBuffer.wrap(iterBytes).getInt();
|
||||
if (iterations < 1) {
|
||||
throw new DatabaseImporterException(String.format("Invalid number of iterations for PBKDF: %d", iterations));
|
||||
}
|
||||
|
||||
byte[] salt = Arrays.copyOfRange(_data, INT_SIZE, offset);
|
||||
AndOtpKeyDerivationTask.Params params = new AndOtpKeyDerivationTask.Params(password, salt, iterations);
|
||||
AndOtpKeyDerivationTask task = new AndOtpKeyDerivationTask(context, key1 -> {
|
||||
KeyDerivationParams params = getKeyDerivationParams(password);
|
||||
AndOtpKeyDerivationTask task = new AndOtpKeyDerivationTask(context, key -> {
|
||||
try {
|
||||
DecryptedState state = decryptData(key1, offset);
|
||||
DecryptedState state = decryptNewFormat(key);
|
||||
listener.onStateDecrypted(state);
|
||||
} catch (DatabaseImporterException e) {
|
||||
listener.onError(e);
|
||||
|
@ -251,7 +268,7 @@ public class AndOtpImporter extends DatabaseImporter {
|
|||
}
|
||||
}
|
||||
|
||||
private static class AndOtpKeyDerivationTask extends ProgressDialogTask<AndOtpKeyDerivationTask.Params, SecretKey> {
|
||||
protected static class AndOtpKeyDerivationTask extends ProgressDialogTask<AndOtpImporter.KeyDerivationParams, SecretKey> {
|
||||
private Callback _cb;
|
||||
|
||||
public AndOtpKeyDerivationTask(Context context, Callback cb) {
|
||||
|
@ -260,20 +277,22 @@ public class AndOtpImporter extends DatabaseImporter {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected SecretKey doInBackground(AndOtpKeyDerivationTask.Params... args) {
|
||||
protected SecretKey doInBackground(AndOtpImporter.KeyDerivationParams... args) {
|
||||
setPriority();
|
||||
|
||||
AndOtpKeyDerivationTask.Params params = args[0];
|
||||
SecretKey key;
|
||||
AndOtpImporter.KeyDerivationParams params = args[0];
|
||||
return deriveKey(params);
|
||||
}
|
||||
|
||||
protected static SecretKey deriveKey(KeyDerivationParams params) {
|
||||
try {
|
||||
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
|
||||
KeySpec spec = new PBEKeySpec(params.getPassword(), params.getSalt(), params.getIterations(), KEY_SIZE);
|
||||
key = factory.generateSecret(spec);
|
||||
SecretKey key = factory.generateSecret(spec);
|
||||
return new SecretKeySpec(key.getEncoded(), "AES");
|
||||
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -282,32 +301,32 @@ public class AndOtpImporter extends DatabaseImporter {
|
|||
_cb.onTaskFinished(key);
|
||||
}
|
||||
|
||||
public static class Params {
|
||||
private char[] _password;
|
||||
private byte[] _salt;
|
||||
private int _iterations;
|
||||
|
||||
public Params(char[] password, byte[] salt, int iterations) {
|
||||
_iterations = iterations;
|
||||
_password = password;
|
||||
_salt = salt;
|
||||
}
|
||||
|
||||
public char[] getPassword() {
|
||||
return _password;
|
||||
}
|
||||
|
||||
public int getIterations() {
|
||||
return _iterations;
|
||||
}
|
||||
|
||||
public byte[] getSalt() {
|
||||
return _salt;
|
||||
}
|
||||
}
|
||||
|
||||
public interface Callback {
|
||||
void onTaskFinished(SecretKey key);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class KeyDerivationParams {
|
||||
private final char[] _password;
|
||||
private final byte[] _salt;
|
||||
private final int _iterations;
|
||||
|
||||
public KeyDerivationParams(char[] password, byte[] salt, int iterations) {
|
||||
_iterations = iterations;
|
||||
_password = password;
|
||||
_salt = salt;
|
||||
}
|
||||
|
||||
public char[] getPassword() {
|
||||
return _password;
|
||||
}
|
||||
|
||||
public int getIterations() {
|
||||
return _iterations;
|
||||
}
|
||||
|
||||
public byte[] getSalt() {
|
||||
return _salt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,31 +37,38 @@ public class AuthenticatorPlusImporter extends DatabaseImporter {
|
|||
}
|
||||
|
||||
public static class EncryptedState extends DatabaseImporter.State {
|
||||
private byte[] _data;
|
||||
private final byte[] _data;
|
||||
|
||||
private EncryptedState(byte[] data) {
|
||||
super(true);
|
||||
_data = data;
|
||||
}
|
||||
|
||||
protected State decrypt(char[] password) throws DatabaseImporterException {
|
||||
try (ByteArrayInputStream inStream = new ByteArrayInputStream(_data);
|
||||
ZipInputStream zipStream = new ZipInputStream(inStream, password)) {
|
||||
LocalFileHeader header;
|
||||
while ((header = zipStream.getNextEntry()) != null) {
|
||||
File file = new File(header.getFileName());
|
||||
if (file.getName().equals(FILENAME)) {
|
||||
GoogleAuthUriImporter importer = new GoogleAuthUriImporter(null);
|
||||
return importer.read(zipStream);
|
||||
}
|
||||
}
|
||||
|
||||
throw new FileNotFoundException(FILENAME);
|
||||
} catch (IOException e) {
|
||||
throw new DatabaseImporterException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decrypt(Context context, DecryptListener listener) {
|
||||
Dialogs.showPasswordInputDialog(context, password -> {
|
||||
try (ByteArrayInputStream inStream = new ByteArrayInputStream(_data);
|
||||
ZipInputStream zipStream = new ZipInputStream(inStream, password)) {
|
||||
LocalFileHeader header;
|
||||
while ((header = zipStream.getNextEntry()) != null) {
|
||||
File file = new File(header.getFileName());
|
||||
if (file.getName().equals(FILENAME)) {
|
||||
GoogleAuthUriImporter importer = new GoogleAuthUriImporter(context);
|
||||
DatabaseImporter.State state = importer.read(zipStream);
|
||||
listener.onStateDecrypted(state);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new FileNotFoundException(FILENAME);
|
||||
} catch (IOException | DatabaseImporterException e) {
|
||||
try {
|
||||
DatabaseImporter.State state = decrypt(password);
|
||||
listener.onStateDecrypted(state);
|
||||
} catch (DatabaseImporterException e) {
|
||||
listener.onError(e);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -42,6 +42,7 @@ import javax.crypto.SecretKey;
|
|||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
public class AuthyImporter extends DatabaseImporter {
|
||||
private static final String _subPath = "shared_prefs";
|
||||
|
@ -154,44 +155,53 @@ public class AuthyImporter extends DatabaseImporter {
|
|||
_array = array;
|
||||
}
|
||||
|
||||
protected DecryptedState decrypt(char[] password) throws DatabaseImporterException {
|
||||
try {
|
||||
for (int i = 0; i < _array.length(); i++) {
|
||||
JSONObject obj = _array.getJSONObject(i);
|
||||
String secretString = JsonUtils.optString(obj, "encryptedSecret");
|
||||
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);
|
||||
key = new SecretKeySpec(key.getEncoded(), "AES");
|
||||
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
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));
|
||||
}
|
||||
|
||||
return new DecryptedState(_array);
|
||||
} catch (JSONException
|
||||
| EncodingException
|
||||
| NoSuchAlgorithmException
|
||||
| InvalidKeySpecException
|
||||
| InvalidAlgorithmParameterException
|
||||
| InvalidKeyException
|
||||
| NoSuchPaddingException
|
||||
| BadPaddingException
|
||||
| IllegalBlockSizeException e) {
|
||||
throw new DatabaseImporterException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@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 = JsonUtils.optString(obj, "encryptedSecret");
|
||||
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);
|
||||
DecryptedState state = decrypt(password);
|
||||
listener.onStateDecrypted(state);
|
||||
} catch (JSONException
|
||||
| EncodingException
|
||||
| NoSuchAlgorithmException
|
||||
| InvalidKeySpecException
|
||||
| InvalidAlgorithmParameterException
|
||||
| InvalidKeyException
|
||||
| NoSuchPaddingException
|
||||
| BadPaddingException
|
||||
| IllegalBlockSizeException e) {
|
||||
} catch (DatabaseImporterException e) {
|
||||
listener.onError(e);
|
||||
}
|
||||
});
|
||||
|
@ -274,6 +284,10 @@ public class AuthyImporter extends DatabaseImporter {
|
|||
info.Issuer = info.Name;
|
||||
info.Name = "";
|
||||
}
|
||||
|
||||
if (info.Name.startsWith(": ")) {
|
||||
info.Name = info.Name.substring(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,9 +28,9 @@ public abstract class DatabaseImporter {
|
|||
// note: keep these lists sorted alphabetically
|
||||
_importers = new ArrayList<>();
|
||||
_importers.add(new Definition("Aegis", AegisImporter.class, R.string.importer_help_aegis, false));
|
||||
_importers.add(new Definition("andOTP", AndOtpImporter.class, R.string.importer_help_andotp, false));
|
||||
_importers.add(new Definition("Authenticator Plus", AuthenticatorPlusImporter.class, R.string.importer_help_authenticator_plus, false));
|
||||
_importers.add(new Definition("Authy", AuthyImporter.class, R.string.importer_help_authy, true));
|
||||
_importers.add(new Definition("andOTP", AndOtpImporter.class, R.string.importer_help_andotp, false));
|
||||
_importers.add(new Definition("FreeOTP", FreeOtpImporter.class, R.string.importer_help_freeotp, true));
|
||||
_importers.add(new Definition("FreeOTP+", FreeOtpPlusImporter.class, R.string.importer_help_freeotp_plus, true));
|
||||
_importers.add(new Definition("Google Authenticator", GoogleAuthImporter.class, R.string.importer_help_google_authenticator, true));
|
||||
|
|
|
@ -115,30 +115,6 @@ public class FreeOtpImporter extends DatabaseImporter {
|
|||
}
|
||||
}
|
||||
|
||||
private static List<JSONObject> parseXml(XmlPullParser parser)
|
||||
throws IOException, XmlPullParserException, JSONException {
|
||||
List<JSONObject> entries = new ArrayList<>();
|
||||
|
||||
parser.require(XmlPullParser.START_TAG, null, "map");
|
||||
while (parser.next() != XmlPullParser.END_TAG) {
|
||||
if (parser.getEventType() != XmlPullParser.START_TAG) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!parser.getName().equals("string")) {
|
||||
skip(parser);
|
||||
continue;
|
||||
}
|
||||
|
||||
JSONObject entry = parseXmlEntry(parser);
|
||||
if (entry != null) {
|
||||
entries.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
private static byte[] toBytes(JSONArray array) throws JSONException {
|
||||
byte[] bytes = new byte[array.length()];
|
||||
for (int i = 0; i < array.length(); i++) {
|
||||
|
@ -146,46 +122,4 @@ public class FreeOtpImporter extends DatabaseImporter {
|
|||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private static JSONObject parseXmlEntry(XmlPullParser parser)
|
||||
throws IOException, XmlPullParserException, JSONException {
|
||||
parser.require(XmlPullParser.START_TAG, null, "string");
|
||||
String name = parser.getAttributeValue(null, "name");
|
||||
String value = parseXmlText(parser);
|
||||
parser.require(XmlPullParser.END_TAG, null, "string");
|
||||
|
||||
if (name.equals("tokenOrder")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new JSONObject(value);
|
||||
}
|
||||
|
||||
private static String parseXmlText(XmlPullParser parser) throws IOException, XmlPullParserException {
|
||||
String text = "";
|
||||
if (parser.next() == XmlPullParser.TEXT) {
|
||||
text = parser.getText();
|
||||
parser.nextTag();
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private static void skip(XmlPullParser parser) throws IOException, XmlPullParserException {
|
||||
// source: https://developer.android.com/training/basics/network-ops/xml.html
|
||||
if (parser.getEventType() != XmlPullParser.START_TAG) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
int depth = 1;
|
||||
while (depth != 0) {
|
||||
switch (parser.next()) {
|
||||
case XmlPullParser.END_TAG:
|
||||
depth--;
|
||||
break;
|
||||
case XmlPullParser.START_TAG:
|
||||
depth++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,8 +104,8 @@ public class TotpAuthenticatorImporter extends DatabaseImporter {
|
|||
|
||||
List<JSONObject> entries = new ArrayList<>();
|
||||
for (int i = 0; i < array.length(); ++i) {
|
||||
String s = array.getString(i);
|
||||
entries.add(new JSONObject(s));
|
||||
JSONObject obj = array.getJSONObject(i);
|
||||
entries.add(obj);
|
||||
}
|
||||
|
||||
return entries;
|
||||
|
@ -119,7 +119,7 @@ public class TotpAuthenticatorImporter extends DatabaseImporter {
|
|||
_data = data;
|
||||
}
|
||||
|
||||
private DecryptedState decrypt(char[] password) throws DatabaseImporterException {
|
||||
protected DecryptedState decrypt(char[] password) throws DatabaseImporterException {
|
||||
try {
|
||||
// WARNING: DON'T DO THIS IN YOUR OWN CODE
|
||||
// this is not a secure way to derive a key from a password
|
||||
|
@ -127,7 +127,7 @@ public class TotpAuthenticatorImporter extends DatabaseImporter {
|
|||
byte[] keyBytes = hash.digest(CryptoUtils.toBytes(password));
|
||||
SecretKey key = new SecretKeySpec(keyBytes, "AES");
|
||||
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
IvParameterSpec spec = new IvParameterSpec(IV);
|
||||
cipher.init(Cipher.DECRYPT_MODE, key, spec);
|
||||
|
||||
|
|
|
@ -34,6 +34,15 @@ public class TotpInfo extends OtpInfo {
|
|||
}
|
||||
}
|
||||
|
||||
public String getOtp(long time) {
|
||||
try {
|
||||
OTP otp = TOTP.generateOTP(getSecret(), getAlgorithm(true), getDigits(), getPeriod(), time);
|
||||
return otp.toString();
|
||||
} catch (InvalidKeyException | NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return ID;
|
||||
|
|
|
@ -619,7 +619,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
DatabaseImporter.State state;
|
||||
try {
|
||||
state = ((AegisImporter.EncryptedState) _importerState).decrypt(creds);
|
||||
} catch (VaultFileException e) {
|
||||
} catch (DatabaseImporterException e) {
|
||||
e.printStackTrace();
|
||||
Dialogs.showErrorDialog(getContext(), R.string.decryption_error, e);
|
||||
return;
|
||||
|
|
|
@ -28,9 +28,13 @@ public class PasswordSlotDecryptTask extends ProgressDialogTask<PasswordSlotDecr
|
|||
setPriority();
|
||||
|
||||
Params params = args[0];
|
||||
for (PasswordSlot slot : params.getSlots()) {
|
||||
return decrypt(params.getSlots(), params.getPassword());
|
||||
}
|
||||
|
||||
public static Result decrypt(List<PasswordSlot> slots, char[] password) {
|
||||
for (PasswordSlot slot : slots) {
|
||||
try {
|
||||
return decryptPasswordSlot(slot, params.getPassword());
|
||||
return decryptPasswordSlot(slot, password);
|
||||
} catch (SlotException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (SlotIntegrityException ignored) {
|
||||
|
@ -41,7 +45,7 @@ public class PasswordSlotDecryptTask extends ProgressDialogTask<PasswordSlotDecr
|
|||
return null;
|
||||
}
|
||||
|
||||
private Result decryptPasswordSlot(PasswordSlot slot, char[] password)
|
||||
public static Result decryptPasswordSlot(PasswordSlot slot, char[] password)
|
||||
throws SlotIntegrityException, SlotException {
|
||||
MasterKey masterKey;
|
||||
SecretKey key = slot.deriveKey(password);
|
||||
|
@ -56,8 +60,6 @@ public class PasswordSlotDecryptTask extends ProgressDialogTask<PasswordSlotDecr
|
|||
throw e;
|
||||
}
|
||||
|
||||
publishProgress(getDialog().getContext().getString(R.string.unlocking_vault_repair));
|
||||
|
||||
// try to decrypt the password slot with the old key
|
||||
SecretKey oldKey = slot.deriveKey(oldPasswordBytes);
|
||||
masterKey = decryptPasswordSlot(slot, oldKey);
|
||||
|
@ -75,7 +77,7 @@ public class PasswordSlotDecryptTask extends ProgressDialogTask<PasswordSlotDecr
|
|||
return new Result(masterKey, slot, repaired);
|
||||
}
|
||||
|
||||
private MasterKey decryptPasswordSlot(PasswordSlot slot, SecretKey key)
|
||||
public static MasterKey decryptPasswordSlot(PasswordSlot slot, SecretKey key)
|
||||
throws SlotException, SlotIntegrityException {
|
||||
Cipher cipher = slot.createDecryptCipher(key);
|
||||
return slot.getKey(cipher);
|
||||
|
|
|
@ -70,6 +70,10 @@ public class PasswordSlot extends RawSlot {
|
|||
return _repaired;
|
||||
}
|
||||
|
||||
public SCryptParameters getSCryptParameters() {
|
||||
return _params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getType() {
|
||||
return TYPE_DERIVED;
|
||||
|
|
|
@ -51,7 +51,7 @@ public abstract class Slot extends UUIDMap.Value {
|
|||
public MasterKey getKey(Cipher cipher) throws SlotException, SlotIntegrityException {
|
||||
try {
|
||||
CryptResult res = CryptoUtils.decrypt(_encryptedMasterKey, cipher, _encryptedMasterKeyParams);
|
||||
SecretKey key = new SecretKeySpec(res.getData(), CryptoUtils.CRYPTO_AEAD);
|
||||
SecretKey key = new SecretKeySpec(res.getData(), "AES");
|
||||
return new MasterKey(key);
|
||||
} catch (BadPaddingException e) {
|
||||
throw new SlotIntegrityException(e);
|
||||
|
@ -97,6 +97,10 @@ public abstract class Slot extends UUIDMap.Value {
|
|||
}
|
||||
}
|
||||
|
||||
protected byte[] getEncryptedMasterKey() {
|
||||
return _encryptedMasterKey;
|
||||
}
|
||||
|
||||
public JSONObject toJson() {
|
||||
try {
|
||||
JSONObject obj = new JSONObject();
|
||||
|
@ -130,10 +134,10 @@ public abstract class Slot extends UUIDMap.Value {
|
|||
break;
|
||||
case Slot.TYPE_DERIVED:
|
||||
SCryptParameters scryptParams = new SCryptParameters(
|
||||
obj.getInt("n"),
|
||||
obj.getInt("r"),
|
||||
obj.getInt("p"),
|
||||
Hex.decode(obj.getString("salt"))
|
||||
obj.getInt("n"),
|
||||
obj.getInt("r"),
|
||||
obj.getInt("p"),
|
||||
Hex.decode(obj.getString("salt"))
|
||||
);
|
||||
boolean repaired = obj.optBoolean("repaired", false);
|
||||
slot = new PasswordSlot(uuid, key, keyParams, scryptParams, repaired);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue