mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-06-07 23:27:50 +00:00
Clean things up a bit
This commit is contained in:
parent
37e303626f
commit
c0bdd261b5
24 changed files with 528 additions and 573 deletions
|
@ -12,18 +12,18 @@ import android.view.View;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import me.impy.aegis.crypto.KeyInfo;
|
||||||
import me.impy.aegis.crypto.otp.OTP;
|
import me.impy.aegis.crypto.otp.OTP;
|
||||||
|
|
||||||
public class AddProfileActivity extends AppCompatActivity {
|
public class AddProfileActivity extends AppCompatActivity {
|
||||||
|
|
||||||
KeyProfile keyProfile;
|
KeyProfile _keyProfile;
|
||||||
|
|
||||||
EditText profileName;
|
|
||||||
TextView tvAlgorithm;
|
|
||||||
TextView tvIssuer;
|
|
||||||
TextView tvPeriod;
|
|
||||||
TextView tvOtp;
|
|
||||||
|
|
||||||
|
EditText _profileName;
|
||||||
|
TextView _textAlgorithm;
|
||||||
|
TextView _textIssuer;
|
||||||
|
TextView _textPeriod;
|
||||||
|
TextView _textOtp;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
@ -31,16 +31,16 @@ public class AddProfileActivity extends AppCompatActivity {
|
||||||
setPreferredTheme();
|
setPreferredTheme();
|
||||||
setContentView(R.layout.activity_add_profile);
|
setContentView(R.layout.activity_add_profile);
|
||||||
|
|
||||||
profileName = (EditText) findViewById(R.id.addProfileName);
|
_profileName = (EditText) findViewById(R.id.addProfileName);
|
||||||
tvAlgorithm = (TextView) findViewById(R.id.tvAlgorithm);
|
_textAlgorithm = (TextView) findViewById(R.id.tvAlgorithm);
|
||||||
tvIssuer = (TextView) findViewById(R.id.tvIssuer);
|
_textIssuer = (TextView) findViewById(R.id.tvIssuer);
|
||||||
tvPeriod = (TextView) findViewById(R.id.tvPeriod);
|
_textPeriod = (TextView) findViewById(R.id.tvPeriod);
|
||||||
tvOtp = (TextView) findViewById(R.id.tvOtp);
|
_textOtp = (TextView) findViewById(R.id.tvOtp);
|
||||||
|
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
getSupportActionBar().setDisplayShowTitleEnabled(false);
|
getSupportActionBar().setDisplayShowTitleEnabled(false);
|
||||||
|
|
||||||
keyProfile = (KeyProfile)getIntent().getSerializableExtra("KeyProfile");
|
_keyProfile = (KeyProfile) getIntent().getSerializableExtra("KeyProfile");
|
||||||
|
|
||||||
initializeForm();
|
initializeForm();
|
||||||
|
|
||||||
|
@ -50,33 +50,33 @@ public class AddProfileActivity extends AppCompatActivity {
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
Intent resultIntent = new Intent();
|
Intent resultIntent = new Intent();
|
||||||
|
|
||||||
keyProfile.Name = profileName.getText().toString();
|
_keyProfile.getEntry().setName(_profileName.getText().toString());
|
||||||
resultIntent.putExtra("KeyProfile", keyProfile);
|
resultIntent.putExtra("KeyProfile", _keyProfile);
|
||||||
|
|
||||||
setResult(Activity.RESULT_OK, resultIntent);
|
setResult(Activity.RESULT_OK, resultIntent);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
//profileName.setText(keyProfile.Info.getAccountName());
|
//_profileName.setText(_keyProfile.Info.getAccountName());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeForm()
|
private void initializeForm() {
|
||||||
{
|
KeyInfo info = _keyProfile.getEntry().getInfo();
|
||||||
profileName.setText(keyProfile.Info.getAccountName());
|
_profileName.setText(info.getAccountName());
|
||||||
tvAlgorithm.setText(keyProfile.Info.getAlgorithm());
|
_textAlgorithm.setText(info.getAlgorithm());
|
||||||
tvIssuer.setText(keyProfile.Info.getIssuer());
|
_textIssuer.setText(info.getIssuer());
|
||||||
tvPeriod.setText(keyProfile.Info.getPeriod() + " seconds");
|
_textPeriod.setText(info.getPeriod() + " seconds");
|
||||||
|
|
||||||
String otp;
|
String otp;
|
||||||
try {
|
try {
|
||||||
otp = OTP.generateOTP(keyProfile.Info);
|
otp = OTP.generateOTP(info);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
keyProfile.Code = otp;
|
_keyProfile.setCode(otp);
|
||||||
tvOtp.setText(otp.substring(0, 3) + " " + otp.substring(3));
|
_textOtp.setText(otp.substring(0, 3) + " " + otp.substring(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -90,16 +90,12 @@ public class AddProfileActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setPreferredTheme() {
|
||||||
private void setPreferredTheme()
|
|
||||||
{
|
|
||||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
if(sharedPreferences.getBoolean("pref_night_mode", false))
|
if (sharedPreferences.getBoolean("pref_night_mode", false)) {
|
||||||
{
|
setTheme(R.style.AppTheme_Dark_TransparentActionBar);
|
||||||
setTheme(R.style.AppTheme_Dark_TransparentActionBar);
|
} else {
|
||||||
} else
|
setTheme(R.style.AppTheme_Default_TransparentActionBar);
|
||||||
{
|
|
||||||
setTheme(R.style.AppTheme_Default_TransparentActionBar);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,39 +39,36 @@ public class AuthActivity extends AppCompatActivity implements FingerprintUiHelp
|
||||||
public static final int RESULT_OK = 0;
|
public static final int RESULT_OK = 0;
|
||||||
public static final int RESULT_EXCEPTION = 1;
|
public static final int RESULT_EXCEPTION = 1;
|
||||||
|
|
||||||
private EditText textPassword;
|
private EditText _textPassword;
|
||||||
|
|
||||||
private SlotCollection slots;
|
private SlotCollection _slots;
|
||||||
private LinearLayout boxFingerprint;
|
private FingerprintUiHelper _fingerHelper;
|
||||||
private SwirlView imgFingerprint;
|
private Cipher _fingerCipher;
|
||||||
private TextView textFingerprint;
|
|
||||||
private FingerprintUiHelper fingerHelper;
|
|
||||||
private Cipher fingerCipher;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_auth);
|
setContentView(R.layout.activity_auth);
|
||||||
textPassword = (EditText) findViewById(R.id.text_password);
|
_textPassword = (EditText) findViewById(R.id.text_password);
|
||||||
boxFingerprint = (LinearLayout) findViewById(R.id.box_fingerprint);
|
LinearLayout boxFingerprint = (LinearLayout) findViewById(R.id.box_fingerprint);
|
||||||
imgFingerprint = (SwirlView) findViewById(R.id.img_fingerprint);
|
SwirlView imgFingerprint = (SwirlView) findViewById(R.id.img_fingerprint);
|
||||||
textFingerprint = (TextView) findViewById(R.id.text_fingerprint);
|
TextView textFingerprint = (TextView) findViewById(R.id.text_fingerprint);
|
||||||
|
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
slots = (SlotCollection) intent.getSerializableExtra("slots");
|
_slots = (SlotCollection) intent.getSerializableExtra("slots");
|
||||||
|
|
||||||
// only show the fingerprint controls if the api version is new enough, permission is granted, a scanner is found and a fingerprint slot is found
|
// only show the fingerprint controls if the api version is new enough, permission is granted, a scanner is found and a fingerprint slot is found
|
||||||
Context context = getApplicationContext();
|
Context context = getApplicationContext();
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
FingerprintManager manager = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
|
FingerprintManager manager = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
|
||||||
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.USE_FINGERPRINT) == PackageManager.PERMISSION_GRANTED && manager.isHardwareDetected()) {
|
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.USE_FINGERPRINT) == PackageManager.PERMISSION_GRANTED && manager.isHardwareDetected()) {
|
||||||
if (slots.has(FingerprintSlot.class)) {
|
if (_slots.has(FingerprintSlot.class)) {
|
||||||
try {
|
try {
|
||||||
KeyStoreHandle handle = new KeyStoreHandle();
|
KeyStoreHandle handle = new KeyStoreHandle();
|
||||||
if (handle.keyExists()) {
|
if (handle.keyExists()) {
|
||||||
SecretKey key = handle.getKey();
|
SecretKey key = handle.getKey();
|
||||||
fingerCipher = Slot.createCipher(key, Cipher.DECRYPT_MODE);
|
_fingerCipher = Slot.createCipher(key, Cipher.DECRYPT_MODE);
|
||||||
fingerHelper = new FingerprintUiHelper(manager, imgFingerprint, textFingerprint, this);
|
_fingerHelper = new FingerprintUiHelper(manager, imgFingerprint, textFingerprint, this);
|
||||||
boxFingerprint.setVisibility(View.VISIBLE);
|
boxFingerprint.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -104,25 +101,25 @@ public class AuthActivity extends AppCompatActivity implements FingerprintUiHelp
|
||||||
}
|
}
|
||||||
|
|
||||||
private MasterKey decryptPasswordSlot(PasswordSlot slot) throws Exception {
|
private MasterKey decryptPasswordSlot(PasswordSlot slot) throws Exception {
|
||||||
char[] password = AuthHelper.getPassword(textPassword, true);
|
char[] password = AuthHelper.getPassword(_textPassword, true);
|
||||||
SecretKey key = slot.deriveKey(password);
|
SecretKey key = slot.deriveKey(password);
|
||||||
CryptoUtils.zero(password);
|
CryptoUtils.zero(password);
|
||||||
Cipher cipher = Slot.createCipher(key, Cipher.DECRYPT_MODE);
|
Cipher cipher = Slot.createCipher(key, Cipher.DECRYPT_MODE);
|
||||||
return slots.decrypt(slot, cipher);
|
return _slots.decrypt(slot, cipher);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MasterKey decryptFingerSlot(FingerprintSlot slot) throws Exception {
|
private MasterKey decryptFingerSlot(FingerprintSlot slot) throws Exception {
|
||||||
return slots.decrypt(slot, fingerCipher);
|
return _slots.decrypt(slot, _fingerCipher);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends Slot> void trySlots(Class<T> type) {
|
private <T extends Slot> void trySlots(Class<T> type) {
|
||||||
try {
|
try {
|
||||||
if (!slots.has(type)) {
|
if (!_slots.has(type)) {
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
}
|
}
|
||||||
|
|
||||||
MasterKey masterKey = null;
|
MasterKey masterKey = null;
|
||||||
for (Slot slot : slots.findAll(type)) {
|
for (Slot slot : _slots.findAll(type)) {
|
||||||
try {
|
try {
|
||||||
if (slot instanceof PasswordSlot) {
|
if (slot instanceof PasswordSlot) {
|
||||||
masterKey = decryptPasswordSlot((PasswordSlot) slot);
|
masterKey = decryptPasswordSlot((PasswordSlot) slot);
|
||||||
|
@ -132,8 +129,7 @@ public class AuthActivity extends AppCompatActivity implements FingerprintUiHelp
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
} catch (SlotIntegrityException e) {
|
} catch (SlotIntegrityException e) { }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (masterKey == null) {
|
if (masterKey == null) {
|
||||||
|
@ -165,8 +161,8 @@ public class AuthActivity extends AppCompatActivity implements FingerprintUiHelp
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
||||||
if (fingerHelper != null) {
|
if (_fingerHelper != null) {
|
||||||
fingerHelper.startListening(new FingerprintManager.CryptoObject(fingerCipher));
|
_fingerHelper.startListening(new FingerprintManager.CryptoObject(_fingerCipher));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,8 +170,8 @@ public class AuthActivity extends AppCompatActivity implements FingerprintUiHelp
|
||||||
public void onPause() {
|
public void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
|
|
||||||
if (fingerHelper != null) {
|
if (_fingerHelper != null) {
|
||||||
fingerHelper.stopListening();
|
_fingerHelper.stopListening();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,96 +31,91 @@ import me.impy.aegis.finger.FingerprintUiHelper;
|
||||||
import me.impy.aegis.helpers.AuthHelper;
|
import me.impy.aegis.helpers.AuthHelper;
|
||||||
|
|
||||||
public class CustomAuthenticatedSlide extends Fragment implements FingerprintUiHelper.Callback, ISlidePolicy, ISlideSelectionListener {
|
public class CustomAuthenticatedSlide extends Fragment implements FingerprintUiHelper.Callback, ISlidePolicy, ISlideSelectionListener {
|
||||||
private int cryptType;
|
private int _cryptType;
|
||||||
private EditText textPassword;
|
private EditText _textPassword;
|
||||||
private EditText textPasswordConfirm;
|
private EditText _textPasswordConfirm;
|
||||||
private int bgColor;
|
private int _bgColor;
|
||||||
|
|
||||||
private LinearLayout boxFingerprint;
|
private LinearLayout _boxFingerprint;
|
||||||
private SwirlView imgFingerprint;
|
private SwirlView _imgFingerprint;
|
||||||
private TextView textFingerprint;
|
private TextView _textFingerprint;
|
||||||
private FingerprintUiHelper fingerHelper;
|
private FingerprintUiHelper _fingerHelper;
|
||||||
private KeyStoreHandle storeHandle;
|
private KeyStoreHandle _storeHandle;
|
||||||
private Cipher fingerCipher;
|
private Cipher _fingerCipher;
|
||||||
private boolean fingerAuthenticated;
|
private boolean _fingerAuthenticated;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
final View view = inflater.inflate(R.layout.fragment_authenticated_slide, container, false);
|
final View view = inflater.inflate(R.layout.fragment_authenticated_slide, container, false);
|
||||||
textPassword = (EditText) view.findViewById(R.id.text_password);
|
_textPassword = (EditText) view.findViewById(R.id.text_password);
|
||||||
textPasswordConfirm = (EditText) view.findViewById(R.id.text_password_confirm);
|
_textPasswordConfirm = (EditText) view.findViewById(R.id.text_password_confirm);
|
||||||
boxFingerprint = (LinearLayout) view.findViewById(R.id.box_fingerprint);
|
_boxFingerprint = (LinearLayout) view.findViewById(R.id.box_fingerprint);
|
||||||
imgFingerprint = (SwirlView) view.findViewById(R.id.img_fingerprint);
|
_imgFingerprint = (SwirlView) view.findViewById(R.id.img_fingerprint);
|
||||||
textFingerprint = (TextView) view.findViewById(R.id.text_fingerprint);
|
_textFingerprint = (TextView) view.findViewById(R.id.text_fingerprint);
|
||||||
view.findViewById(R.id.main).setBackgroundColor(bgColor);
|
view.findViewById(R.id.main).setBackgroundColor(_bgColor);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*@Override
|
|
||||||
public int buttonsColor() {
|
|
||||||
return R.color.colorAccent;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
public int getCryptType() {
|
public int getCryptType() {
|
||||||
return cryptType;
|
return _cryptType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cipher getCipher(Slot slot) throws Exception {
|
public Cipher getCipher(Slot slot) throws Exception {
|
||||||
if (slot instanceof PasswordSlot) {
|
if (slot instanceof PasswordSlot) {
|
||||||
char[] password = AuthHelper.getPassword(textPassword, true);
|
char[] password = AuthHelper.getPassword(_textPassword, true);
|
||||||
byte[] salt = CryptoUtils.generateSalt();
|
byte[] salt = CryptoUtils.generateSalt();
|
||||||
SecretKey key = ((PasswordSlot)slot).deriveKey(password, salt, CryptoUtils.CRYPTO_SCRYPT_N, CryptoUtils.CRYPTO_SCRYPT_r, CryptoUtils.CRYPTO_SCRYPT_p);
|
SecretKey key = ((PasswordSlot)slot).deriveKey(password, salt, CryptoUtils.CRYPTO_SCRYPT_N, CryptoUtils.CRYPTO_SCRYPT_r, CryptoUtils.CRYPTO_SCRYPT_p);
|
||||||
CryptoUtils.zero(password);
|
CryptoUtils.zero(password);
|
||||||
return Slot.createCipher(key, Cipher.ENCRYPT_MODE);
|
return Slot.createCipher(key, Cipher.ENCRYPT_MODE);
|
||||||
} else if (slot instanceof FingerprintSlot) {
|
} else if (slot instanceof FingerprintSlot) {
|
||||||
return fingerCipher;
|
return _fingerCipher;
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBgColor(int color) {
|
public void setBgColor(int color) {
|
||||||
bgColor = color;
|
_bgColor = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSlideSelected() {
|
public void onSlideSelected() {
|
||||||
Intent intent = getActivity().getIntent();
|
Intent intent = getActivity().getIntent();
|
||||||
cryptType = intent.getIntExtra("cryptType", CustomAuthenticationSlide.CRYPT_TYPE_INVALID);
|
_cryptType = intent.getIntExtra("cryptType", CustomAuthenticationSlide.CRYPT_TYPE_INVALID);
|
||||||
|
|
||||||
switch(cryptType) {
|
switch(_cryptType) {
|
||||||
case CustomAuthenticationSlide.CRYPT_TYPE_NONE:
|
case CustomAuthenticationSlide.CRYPT_TYPE_NONE:
|
||||||
case CustomAuthenticationSlide.CRYPT_TYPE_PASS:
|
case CustomAuthenticationSlide.CRYPT_TYPE_PASS:
|
||||||
break;
|
break;
|
||||||
case CustomAuthenticationSlide.CRYPT_TYPE_FINGER:
|
case CustomAuthenticationSlide.CRYPT_TYPE_FINGER:
|
||||||
boxFingerprint.setVisibility(View.VISIBLE);
|
_boxFingerprint.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
SecretKey key;
|
SecretKey key;
|
||||||
try {
|
try {
|
||||||
if (storeHandle == null) {
|
if (_storeHandle == null) {
|
||||||
storeHandle = new KeyStoreHandle();
|
_storeHandle = new KeyStoreHandle();
|
||||||
}
|
}
|
||||||
// TODO: consider regenerating the key if it exists
|
// TODO: consider regenerating the key if it exists
|
||||||
if (!storeHandle.keyExists()) {
|
if (!_storeHandle.keyExists()) {
|
||||||
key = storeHandle.generateKey(true);
|
key = _storeHandle.generateKey(true);
|
||||||
} else {
|
} else {
|
||||||
key = storeHandle.getKey();
|
key = _storeHandle.getKey();
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new UndeclaredThrowableException(e);
|
throw new UndeclaredThrowableException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fingerHelper == null) {
|
if (_fingerHelper == null) {
|
||||||
FingerprintManager fingerManager = (FingerprintManager) getContext().getSystemService(Context.FINGERPRINT_SERVICE);
|
FingerprintManager fingerManager = (FingerprintManager) getContext().getSystemService(Context.FINGERPRINT_SERVICE);
|
||||||
fingerHelper = new FingerprintUiHelper(fingerManager, imgFingerprint, textFingerprint, this);
|
_fingerHelper = new FingerprintUiHelper(fingerManager, _imgFingerprint, _textFingerprint, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fingerCipher = Slot.createCipher(key, Cipher.ENCRYPT_MODE);
|
_fingerCipher = Slot.createCipher(key, Cipher.ENCRYPT_MODE);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new UndeclaredThrowableException(e);
|
throw new UndeclaredThrowableException(e);
|
||||||
}
|
}
|
||||||
fingerHelper.startListening(new FingerprintManager.CryptoObject(fingerCipher));
|
_fingerHelper.startListening(new FingerprintManager.CryptoObject(_fingerCipher));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
|
@ -129,25 +124,25 @@ public class CustomAuthenticatedSlide extends Fragment implements FingerprintUiH
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSlideDeselected() {
|
public void onSlideDeselected() {
|
||||||
if (fingerHelper != null) {
|
if (_fingerHelper != null) {
|
||||||
fingerAuthenticated = false;
|
_fingerAuthenticated = false;
|
||||||
boxFingerprint.setVisibility(View.INVISIBLE);
|
_boxFingerprint.setVisibility(View.INVISIBLE);
|
||||||
fingerHelper.stopListening();
|
_fingerHelper.stopListening();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isPolicyRespected() {
|
public boolean isPolicyRespected() {
|
||||||
switch(cryptType) {
|
switch(_cryptType) {
|
||||||
case CustomAuthenticationSlide.CRYPT_TYPE_NONE:
|
case CustomAuthenticationSlide.CRYPT_TYPE_NONE:
|
||||||
return true;
|
return true;
|
||||||
case CustomAuthenticationSlide.CRYPT_TYPE_FINGER:
|
case CustomAuthenticationSlide.CRYPT_TYPE_FINGER:
|
||||||
if (!fingerAuthenticated) {
|
if (!_fingerAuthenticated) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// intentional fallthrough
|
// intentional fallthrough
|
||||||
case CustomAuthenticationSlide.CRYPT_TYPE_PASS:
|
case CustomAuthenticationSlide.CRYPT_TYPE_PASS:
|
||||||
return AuthHelper.arePasswordsEqual(textPassword, textPasswordConfirm);
|
return AuthHelper.arePasswordsEqual(_textPassword, _textPasswordConfirm);
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
}
|
}
|
||||||
|
@ -156,9 +151,9 @@ public class CustomAuthenticatedSlide extends Fragment implements FingerprintUiH
|
||||||
@Override
|
@Override
|
||||||
public void onUserIllegallyRequestedNextPage() {
|
public void onUserIllegallyRequestedNextPage() {
|
||||||
String message;
|
String message;
|
||||||
if (!AuthHelper.arePasswordsEqual(textPassword, textPasswordConfirm)) {
|
if (!AuthHelper.arePasswordsEqual(_textPassword, _textPasswordConfirm)) {
|
||||||
message = "Passwords should be equal and non-empty";
|
message = "Passwords should be equal and non-empty";
|
||||||
} else if (!fingerAuthenticated) {
|
} else if (!_fingerAuthenticated) {
|
||||||
message = "Register your fingerprint";
|
message = "Register your fingerprint";
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
|
@ -173,7 +168,7 @@ public class CustomAuthenticatedSlide extends Fragment implements FingerprintUiH
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAuthenticated() {
|
public void onAuthenticated() {
|
||||||
fingerAuthenticated = true;
|
_fingerAuthenticated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -25,16 +25,16 @@ public class CustomAuthenticationSlide extends Fragment implements ISlidePolicy
|
||||||
public static final int CRYPT_TYPE_PASS = 2;
|
public static final int CRYPT_TYPE_PASS = 2;
|
||||||
public static final int CRYPT_TYPE_FINGER = 3;
|
public static final int CRYPT_TYPE_FINGER = 3;
|
||||||
|
|
||||||
private RadioGroup buttonGroup;
|
private RadioGroup _buttonGroup;
|
||||||
private int bgColor;
|
private int _bgColor;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
final View view = inflater.inflate(R.layout.fragment_authentication_slide, container, false);
|
final View view = inflater.inflate(R.layout.fragment_authentication_slide, container, false);
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
|
|
||||||
buttonGroup = (RadioGroup) view.findViewById(R.id.rg_authenticationMethod);
|
_buttonGroup = (RadioGroup) view.findViewById(R.id.rg_authenticationMethod);
|
||||||
buttonGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
|
_buttonGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onCheckedChanged(RadioGroup group, int checkedId) {
|
public void onCheckedChanged(RadioGroup group, int checkedId) {
|
||||||
if (checkedId == -1) {
|
if (checkedId == -1) {
|
||||||
|
@ -71,17 +71,17 @@ public class CustomAuthenticationSlide extends Fragment implements ISlidePolicy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
view.findViewById(R.id.main).setBackgroundColor(bgColor);
|
view.findViewById(R.id.main).setBackgroundColor(_bgColor);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBgColor(int color) {
|
public void setBgColor(int color) {
|
||||||
bgColor = color;
|
_bgColor = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isPolicyRespected() {
|
public boolean isPolicyRespected() {
|
||||||
return buttonGroup.getCheckedRadioButtonId() != -1;
|
return _buttonGroup.getCheckedRadioButtonId() != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -89,9 +89,4 @@ public class CustomAuthenticationSlide extends Fragment implements ISlidePolicy
|
||||||
Snackbar snackbar = Snackbar.make(getView(), "Please select an authentication method", Snackbar.LENGTH_LONG);
|
Snackbar snackbar = Snackbar.make(getView(), "Please select an authentication method", Snackbar.LENGTH_LONG);
|
||||||
snackbar.show();
|
snackbar.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*@Override
|
|
||||||
public int buttonsColor() {
|
|
||||||
return R.color.colorAccent;
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,24 +9,15 @@ import android.view.ViewGroup;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
public class EditProfileBottomSheetdialog extends BottomSheetDialogFragment {
|
public class EditProfileBottomSheetdialog extends BottomSheetDialogFragment {
|
||||||
LinearLayout copyLayout;
|
LinearLayout _copyLayout;
|
||||||
|
|
||||||
public static EditProfileBottomSheetdialog getInstance() {
|
public static EditProfileBottomSheetdialog getInstance() {
|
||||||
return new EditProfileBottomSheetdialog();
|
return new EditProfileBottomSheetdialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @Override
|
|
||||||
public void setupDialog(Dialog dialog, int style) {
|
|
||||||
super.setupDialog(dialog, style);
|
|
||||||
View contentView = View.inflate(getContext(), R.layout.bottom_sheet_edit_profile, null);
|
|
||||||
dialog.setContentView(contentView);
|
|
||||||
|
|
||||||
copyLayout = (LinearLayout)contentView.findViewById(R.id.copy_button);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
public LinearLayout GetCopyLayout()
|
public LinearLayout GetCopyLayout()
|
||||||
{
|
{
|
||||||
return copyLayout;
|
return _copyLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|
|
@ -25,9 +25,9 @@ public class IntroActivity extends AppIntro {
|
||||||
public static final int RESULT_OK = 0;
|
public static final int RESULT_OK = 0;
|
||||||
public static final int RESULT_EXCEPTION = 1;
|
public static final int RESULT_EXCEPTION = 1;
|
||||||
|
|
||||||
private CustomAuthenticatedSlide authenticatedSlide;
|
private CustomAuthenticatedSlide _authenticatedSlide;
|
||||||
private CustomAuthenticationSlide authenticationSlide;
|
private CustomAuthenticationSlide _authenticationSlide;
|
||||||
private Fragment endSlide;
|
private Fragment _endSlide;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
@ -51,20 +51,20 @@ public class IntroActivity extends AppIntro {
|
||||||
addSlide(AppIntroFragment.newInstance(permSliderPage));
|
addSlide(AppIntroFragment.newInstance(permSliderPage));
|
||||||
askForPermissions(new String[]{Manifest.permission.CAMERA}, 2);
|
askForPermissions(new String[]{Manifest.permission.CAMERA}, 2);
|
||||||
|
|
||||||
authenticationSlide = new CustomAuthenticationSlide();
|
_authenticationSlide = new CustomAuthenticationSlide();
|
||||||
authenticationSlide.setBgColor(getResources().getColor(R.color.colorHeaderSuccess));
|
_authenticationSlide.setBgColor(getResources().getColor(R.color.colorHeaderSuccess));
|
||||||
addSlide(authenticationSlide);
|
addSlide(_authenticationSlide);
|
||||||
authenticatedSlide = new CustomAuthenticatedSlide();
|
_authenticatedSlide = new CustomAuthenticatedSlide();
|
||||||
authenticatedSlide.setBgColor(getResources().getColor(R.color.colorPrimary));
|
_authenticatedSlide.setBgColor(getResources().getColor(R.color.colorPrimary));
|
||||||
addSlide(authenticatedSlide);
|
addSlide(_authenticatedSlide);
|
||||||
|
|
||||||
SliderPage endSliderPage = new SliderPage();
|
SliderPage endSliderPage = new SliderPage();
|
||||||
endSliderPage.setTitle("All done!");
|
endSliderPage.setTitle("All done!");
|
||||||
endSliderPage.setDescription("Aegis has been set up and is ready to go.");
|
endSliderPage.setDescription("Aegis has been set up and is ready to go.");
|
||||||
endSliderPage.setImageDrawable(R.drawable.intro_shield);
|
endSliderPage.setImageDrawable(R.drawable.intro_shield);
|
||||||
endSliderPage.setBgColor(getResources().getColor(R.color.colorPrimary));
|
endSliderPage.setBgColor(getResources().getColor(R.color.colorPrimary));
|
||||||
endSlide = AppIntroFragment.newInstance(endSliderPage);
|
_endSlide = AppIntroFragment.newInstance(endSliderPage);
|
||||||
addSlide(endSlide);
|
addSlide(_endSlide);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setException(Exception e) {
|
private void setException(Exception e) {
|
||||||
|
@ -77,7 +77,7 @@ public class IntroActivity extends AppIntro {
|
||||||
@Override
|
@Override
|
||||||
public void onSlideChanged(Fragment oldFragment, Fragment newFragment) {
|
public void onSlideChanged(Fragment oldFragment, Fragment newFragment) {
|
||||||
// skip to the last slide if no encryption will be used
|
// skip to the last slide if no encryption will be used
|
||||||
if (oldFragment == authenticationSlide && newFragment != endSlide) {
|
if (oldFragment == _authenticationSlide && newFragment != _endSlide) {
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
int cryptType = intent.getIntExtra("cryptType", CustomAuthenticationSlide.CRYPT_TYPE_INVALID);
|
int cryptType = intent.getIntExtra("cryptType", CustomAuthenticationSlide.CRYPT_TYPE_INVALID);
|
||||||
if (cryptType == CustomAuthenticationSlide.CRYPT_TYPE_NONE) {
|
if (cryptType == CustomAuthenticationSlide.CRYPT_TYPE_NONE) {
|
||||||
|
@ -95,7 +95,7 @@ public class IntroActivity extends AppIntro {
|
||||||
Database database = new Database();
|
Database database = new Database();
|
||||||
DatabaseFile databaseFile = new DatabaseFile();
|
DatabaseFile databaseFile = new DatabaseFile();
|
||||||
|
|
||||||
int cryptType = authenticatedSlide.getCryptType();
|
int cryptType = _authenticatedSlide.getCryptType();
|
||||||
|
|
||||||
// generate the master key
|
// generate the master key
|
||||||
MasterKey masterKey = null;
|
MasterKey masterKey = null;
|
||||||
|
@ -114,7 +114,7 @@ public class IntroActivity extends AppIntro {
|
||||||
// encrypt the master key with a key derived from the user's password
|
// encrypt the master key with a key derived from the user's password
|
||||||
// and add it to the list of slots
|
// and add it to the list of slots
|
||||||
PasswordSlot slot = new PasswordSlot();
|
PasswordSlot slot = new PasswordSlot();
|
||||||
Cipher cipher = authenticatedSlide.getCipher(slot);
|
Cipher cipher = _authenticatedSlide.getCipher(slot);
|
||||||
slots.encrypt(slot, masterKey, cipher);
|
slots.encrypt(slot, masterKey, cipher);
|
||||||
slots.add(slot);
|
slots.add(slot);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -128,7 +128,7 @@ public class IntroActivity extends AppIntro {
|
||||||
// encrypt the master key with the fingerprint key
|
// encrypt the master key with the fingerprint key
|
||||||
// and add it to the list of slots
|
// and add it to the list of slots
|
||||||
FingerprintSlot slot = new FingerprintSlot();
|
FingerprintSlot slot = new FingerprintSlot();
|
||||||
Cipher cipher = authenticatedSlide.getCipher(slot);
|
Cipher cipher = _authenticatedSlide.getCipher(slot);
|
||||||
slots.encrypt(slot, masterKey, cipher);
|
slots.encrypt(slot, masterKey, cipher);
|
||||||
slots.add(slot);
|
slots.add(slot);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
|
@ -2,22 +2,24 @@ package me.impy.aegis;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import me.impy.aegis.crypto.KeyInfo;
|
import me.impy.aegis.db.DatabaseEntry;
|
||||||
|
|
||||||
public class KeyProfile implements Serializable {
|
public class KeyProfile implements Serializable {
|
||||||
public String Name;
|
private String _code;
|
||||||
public String Icon;
|
private DatabaseEntry _entry;
|
||||||
public String Code;
|
|
||||||
public String Issuer;
|
|
||||||
public KeyInfo Info;
|
|
||||||
public int Order;
|
|
||||||
public int ID;
|
|
||||||
|
|
||||||
public int compareTo(KeyProfile another) {
|
public KeyProfile(DatabaseEntry entry) {
|
||||||
if (this.Order>another.Order){
|
_entry = entry;
|
||||||
return -1;
|
}
|
||||||
}else{
|
|
||||||
return 1;
|
public DatabaseEntry getEntry() {
|
||||||
}
|
return _entry;
|
||||||
|
}
|
||||||
|
public String getCode() {
|
||||||
|
return _code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCode(String code) {
|
||||||
|
_code = code;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,34 +26,34 @@ import me.impy.aegis.crypto.otp.OTP;
|
||||||
import me.impy.aegis.helpers.ItemTouchHelperAdapter;
|
import me.impy.aegis.helpers.ItemTouchHelperAdapter;
|
||||||
|
|
||||||
public class KeyProfileAdapter extends RecyclerView.Adapter<KeyProfileAdapter.KeyProfileHolder> implements ItemTouchHelperAdapter {
|
public class KeyProfileAdapter extends RecyclerView.Adapter<KeyProfileAdapter.KeyProfileHolder> implements ItemTouchHelperAdapter {
|
||||||
private final List<KeyProfileHolder> lstHolders;
|
private final List<KeyProfileHolder> _holders;
|
||||||
private ArrayList<KeyProfile> mKeyProfiles;
|
private ArrayList<KeyProfile> _keyProfiles;
|
||||||
private Handler uiHandler;
|
private Handler _uiHandler;
|
||||||
private static ItemClickListener itemClickListener;
|
private static ItemClickListener _itemClickListener;
|
||||||
private static LongItemClickListener longItemClickListener;
|
private static LongItemClickListener _longItemClickListener;
|
||||||
|
|
||||||
public KeyProfileAdapter(ArrayList<KeyProfile> keyProfiles) {
|
public KeyProfileAdapter(ArrayList<KeyProfile> keyProfiles) {
|
||||||
mKeyProfiles = keyProfiles;
|
_keyProfiles = keyProfiles;
|
||||||
lstHolders = new ArrayList<>();
|
_holders = new ArrayList<>();
|
||||||
uiHandler = new Handler();
|
_uiHandler = new Handler();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onItemDismiss(int position) {
|
public void onItemDismiss(int position) {
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void remove(int position) {
|
private void remove(int position) {
|
||||||
mKeyProfiles.remove(position);
|
_keyProfiles.remove(position);
|
||||||
notifyItemRemoved(position);
|
notifyItemRemoved(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onItemMove(int firstPosition, int secondPosition) {
|
public void onItemMove(int firstPosition, int secondPosition) {
|
||||||
Collections.swap(mKeyProfiles, firstPosition, secondPosition);
|
Collections.swap(_keyProfiles, firstPosition, secondPosition);
|
||||||
notifyItemMoved(firstPosition, secondPosition);
|
notifyItemMoved(firstPosition, secondPosition);
|
||||||
|
|
||||||
mKeyProfiles.get(firstPosition).Order = secondPosition;
|
_keyProfiles.get(firstPosition).getEntry().setOrder(secondPosition);
|
||||||
adjustOrder(secondPosition);
|
adjustOrder(secondPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,12 +61,12 @@ public class KeyProfileAdapter extends RecyclerView.Adapter<KeyProfileAdapter.Ke
|
||||||
Comparator<KeyProfile> comparator = new Comparator<KeyProfile>() {
|
Comparator<KeyProfile> comparator = new Comparator<KeyProfile>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(KeyProfile keyProfile, KeyProfile t1) {
|
public int compare(KeyProfile keyProfile, KeyProfile t1) {
|
||||||
return keyProfile.Order - t1.Order;
|
return keyProfile.getEntry().getOrder() - t1.getEntry().getOrder();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (int i = startPosition; i < mKeyProfiles.size(); i++) {
|
for (int i = startPosition; i < _keyProfiles.size(); i++) {
|
||||||
mKeyProfiles.get(i).Order = i + 1;
|
_keyProfiles.get(i).getEntry().setOrder(i + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,130 +79,127 @@ public class KeyProfileAdapter extends RecyclerView.Adapter<KeyProfileAdapter.Ke
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(final KeyProfileHolder holder, int position) {
|
public void onBindViewHolder(final KeyProfileHolder holder, int position) {
|
||||||
holder.setData(mKeyProfiles.get(position));
|
holder.setData(_keyProfiles.get(position));
|
||||||
holder.updateCode();
|
holder.updateCode();
|
||||||
lstHolders.add(holder);
|
_holders.add(holder);
|
||||||
|
|
||||||
Runnable runnable = new Runnable() {
|
Runnable runnable = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// check if this key profile still exists
|
// check if this key profile still exists
|
||||||
if (lstHolders.contains(holder)) {
|
if (_holders.contains(holder)) {
|
||||||
holder.updateCode();
|
holder.updateCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
uiHandler.postDelayed(this, holder.keyProfile.Info.getPeriod() * 1000);
|
_uiHandler.postDelayed(this, holder._keyProfile.getEntry().getInfo().getPeriod() * 1000);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
uiHandler.postDelayed(runnable, holder.keyProfile.Info.getMillisTillNextRotation());
|
_uiHandler.postDelayed(runnable, holder._keyProfile.getEntry().getInfo().getMillisTillNextRotation());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
return mKeyProfiles.size();
|
return _keyProfiles.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class KeyProfileHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
public static class KeyProfileHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
||||||
TextView profileName;
|
TextView _profileName;
|
||||||
TextView profileCode;
|
TextView _profileCode;
|
||||||
TextView profileIssuer;
|
TextView _profileIssuer;
|
||||||
ImageView profileDrawable;
|
ImageView _profileDrawable;
|
||||||
KeyProfile keyProfile;
|
KeyProfile _keyProfile;
|
||||||
ProgressBar progressBar;
|
ProgressBar _progressBar;
|
||||||
View _itemView;
|
View _itemView;
|
||||||
|
|
||||||
KeyProfileHolder(final View itemView) {
|
KeyProfileHolder(final View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
_itemView = itemView;
|
_itemView = itemView;
|
||||||
profileName = (TextView) itemView.findViewById(R.id.profile_name);
|
_profileName = (TextView) itemView.findViewById(R.id.profile_name);
|
||||||
profileCode = (TextView) itemView.findViewById(R.id.profile_code);
|
_profileCode = (TextView) itemView.findViewById(R.id.profile_code);
|
||||||
profileIssuer = (TextView) itemView.findViewById(R.id.profile_issuer);
|
_profileIssuer = (TextView) itemView.findViewById(R.id.profile_issuer);
|
||||||
profileDrawable = (ImageView) itemView.findViewById(R.id.ivTextDrawable);
|
_profileDrawable = (ImageView) itemView.findViewById(R.id.ivTextDrawable);
|
||||||
progressBar = (ProgressBar) itemView.findViewById(R.id.progressBar);
|
_progressBar = (ProgressBar) itemView.findViewById(R.id.progressBar);
|
||||||
|
|
||||||
itemView.setOnClickListener(this);
|
itemView.setOnClickListener(this);
|
||||||
itemView.setOnLongClickListener(this);
|
itemView.setOnLongClickListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setData(KeyProfile profile) {
|
public void setData(KeyProfile profile) {
|
||||||
this.keyProfile = profile;
|
_keyProfile = profile;
|
||||||
profileName.setText(profile.Name);
|
_profileName.setText(profile.getEntry().getName());
|
||||||
profileCode.setText(profile.Code);
|
_profileCode.setText(profile.getCode());
|
||||||
|
|
||||||
// So that we can have text in the designer without showing it to our user
|
// So that we can have text in the designer without showing it to our user
|
||||||
profileIssuer.setText("");
|
_profileIssuer.setText("");
|
||||||
|
|
||||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(_itemView.getContext());
|
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(_itemView.getContext());
|
||||||
if(sharedPreferences.getBoolean("pref_issuer", false))
|
if(sharedPreferences.getBoolean("pref_issuer", false))
|
||||||
{
|
{
|
||||||
profileIssuer.setText(" - " + profile.Info.getIssuer());
|
_profileIssuer.setText(" - " + profile.getEntry().getInfo().getIssuer());
|
||||||
}
|
}
|
||||||
|
|
||||||
profileDrawable.setImageDrawable(generateTextDrawable(profile));
|
_profileDrawable.setImageDrawable(generateTextDrawable(profile));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateCode() {
|
public void updateCode() {
|
||||||
progressBar.setProgress(1000);
|
_progressBar.setProgress(1000);
|
||||||
if (this.keyProfile == null) {
|
if (_keyProfile == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String otp = "";
|
String otp = "";
|
||||||
try {
|
try {
|
||||||
otp = OTP.generateOTP(this.keyProfile.Info);
|
otp = OTP.generateOTP(_keyProfile.getEntry().getInfo());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new UndeclaredThrowableException(e);
|
throw new UndeclaredThrowableException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.keyProfile.Code = otp;
|
_keyProfile.setCode(otp);
|
||||||
profileCode.setText(otp.substring(0, 3) + " " + otp.substring(3));
|
_profileCode.setText(otp.substring(0, 3) + " " + otp.substring(3));
|
||||||
|
|
||||||
long millisTillRotation = keyProfile.Info.getMillisTillNextRotation();
|
long millisTillRotation = _keyProfile.getEntry().getInfo().getMillisTillNextRotation();
|
||||||
long period = keyProfile.Info.getPeriod() * 1000;
|
long period = _keyProfile.getEntry().getInfo().getPeriod() * 1000;
|
||||||
int currentProgress = 1000 - (int) ((((double) period - millisTillRotation) / period) * 1000);
|
int currentProgress = 1000 - (int) ((((double) period - millisTillRotation) / period) * 1000);
|
||||||
ObjectAnimator animation = ObjectAnimator.ofInt(progressBar, "progress", currentProgress, 0);
|
ObjectAnimator animation = ObjectAnimator.ofInt(_progressBar, "progress", currentProgress, 0);
|
||||||
animation.setDuration(millisTillRotation);
|
animation.setDuration(millisTillRotation);
|
||||||
animation.setInterpolator(new LinearInterpolator());
|
animation.setInterpolator(new LinearInterpolator());
|
||||||
animation.start();
|
animation.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private TextDrawable generateTextDrawable(KeyProfile profile) {
|
private TextDrawable generateTextDrawable(KeyProfile profile) {
|
||||||
if (profileName == null)
|
if (_profileName == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
ColorGenerator generator = ColorGenerator.MATERIAL;
|
ColorGenerator generator = ColorGenerator.MATERIAL;
|
||||||
int profileKeyColor = generator.getColor(profile.Name);
|
int profileKeyColor = generator.getColor(profile.getEntry().getName());
|
||||||
|
|
||||||
TextDrawable newDrawable = TextDrawable.builder().buildRound(profile.Name.substring(0, 1).toUpperCase(), profileKeyColor);
|
return TextDrawable.builder().buildRound(profile.getEntry().getName().substring(0, 1).toUpperCase(), profileKeyColor);
|
||||||
return newDrawable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
itemClickListener.onItemClick(getAdapterPosition(), view);
|
_itemClickListener.onItemClick(getAdapterPosition(), view);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onLongClick(View view) {
|
public boolean onLongClick(View view) {
|
||||||
longItemClickListener.onLongItemClick(getAdapterPosition(), view);
|
_longItemClickListener.onLongItemClick(getAdapterPosition(), view);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOnItemClickListener(ItemClickListener clickListener) {
|
public void setOnItemClickListener(ItemClickListener clickListener) {
|
||||||
KeyProfileAdapter.itemClickListener = clickListener;
|
KeyProfileAdapter._itemClickListener = clickListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOnLongItemClickListener(LongItemClickListener clickListener) {
|
public void setOnLongItemClickListener(LongItemClickListener clickListener) {
|
||||||
KeyProfileAdapter.longItemClickListener = clickListener;
|
KeyProfileAdapter._longItemClickListener = clickListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ItemClickListener
|
public interface ItemClickListener {
|
||||||
{
|
|
||||||
void onItemClick(int position, View v);
|
void onItemClick(int position, View v);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface LongItemClickListener
|
public interface LongItemClickListener {
|
||||||
{
|
|
||||||
void onLongItemClick(int position, View v);
|
void onLongItemClick(int position, View v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,19 +26,17 @@ import android.view.View;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.reflect.UndeclaredThrowableException;
|
import java.lang.reflect.UndeclaredThrowableException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import me.impy.aegis.crypto.MasterKey;
|
import me.impy.aegis.crypto.MasterKey;
|
||||||
import me.impy.aegis.crypto.otp.OTP;
|
import me.impy.aegis.crypto.otp.OTP;
|
||||||
|
import me.impy.aegis.db.DatabaseEntry;
|
||||||
import me.impy.aegis.db.DatabaseManager;
|
import me.impy.aegis.db.DatabaseManager;
|
||||||
import me.impy.aegis.ext.FreeOTPImporter;
|
import me.impy.aegis.ext.FreeOTPImporter;
|
||||||
import me.impy.aegis.helpers.SimpleItemTouchHelperCallback;
|
import me.impy.aegis.helpers.SimpleItemTouchHelperCallback;
|
||||||
|
@ -50,18 +48,19 @@ public class MainActivity extends AppCompatActivity {
|
||||||
private static final int CODE_DECRYPT = 3;
|
private static final int CODE_DECRYPT = 3;
|
||||||
private static final int CODE_IMPORT = 4;
|
private static final int CODE_IMPORT = 4;
|
||||||
|
|
||||||
RecyclerView rvKeyProfiles;
|
private KeyProfileAdapter _keyProfileAdapter;
|
||||||
KeyProfileAdapter mKeyProfileAdapter;
|
private ArrayList<KeyProfile> _keyProfiles = new ArrayList<>();
|
||||||
ArrayList<KeyProfile> mKeyProfiles = new ArrayList<>();
|
private DatabaseManager _db;
|
||||||
private DatabaseManager db;
|
|
||||||
|
|
||||||
boolean nightMode = false;
|
private boolean _nightMode = false;
|
||||||
int clickedItemPosition = -1;
|
private int _clickedItemPosition = -1;
|
||||||
|
|
||||||
|
private Menu _menu;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
db = new DatabaseManager(getApplicationContext());
|
_db = new DatabaseManager(getApplicationContext());
|
||||||
|
|
||||||
SharedPreferences prefs = this.getSharedPreferences("me.impy.aegis", Context.MODE_PRIVATE);
|
SharedPreferences prefs = this.getSharedPreferences("me.impy.aegis", Context.MODE_PRIVATE);
|
||||||
if (!prefs.getBoolean("passedIntro", false)) {
|
if (!prefs.getBoolean("passedIntro", false)) {
|
||||||
|
@ -69,14 +68,14 @@ public class MainActivity extends AppCompatActivity {
|
||||||
startActivityForResult(intro, CODE_DO_INTRO);
|
startActivityForResult(intro, CODE_DO_INTRO);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
db.load();
|
_db.load();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// TODO: feedback
|
// TODO: feedback
|
||||||
throw new UndeclaredThrowableException(e);
|
throw new UndeclaredThrowableException(e);
|
||||||
}
|
}
|
||||||
if (!db.isDecrypted()) {
|
if (!_db.isDecrypted()) {
|
||||||
Intent intent = new Intent(this, AuthActivity.class);
|
Intent intent = new Intent(this, AuthActivity.class);
|
||||||
intent.putExtra("slots", db.getFile().getSlots());
|
intent.putExtra("slots", _db.getFile().getSlots());
|
||||||
startActivityForResult(intent, CODE_DECRYPT);
|
startActivityForResult(intent, CODE_DECRYPT);
|
||||||
} else {
|
} else {
|
||||||
loadKeyProfiles();
|
loadKeyProfiles();
|
||||||
|
@ -85,7 +84,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
if (sharedPreferences.getBoolean("pref_night_mode", false)) {
|
if (sharedPreferences.getBoolean("pref_night_mode", false)) {
|
||||||
nightMode = true;
|
_nightMode = true;
|
||||||
setTheme(R.style.AppTheme_Dark_NoActionBar);
|
setTheme(R.style.AppTheme_Dark_NoActionBar);
|
||||||
} else {
|
} else {
|
||||||
setPreferredTheme();
|
setPreferredTheme();
|
||||||
|
@ -102,31 +101,31 @@ public class MainActivity extends AppCompatActivity {
|
||||||
startActivityForResult(scannerActivity, CODE_GET_KEYINFO);
|
startActivityForResult(scannerActivity, CODE_GET_KEYINFO);
|
||||||
});
|
});
|
||||||
|
|
||||||
rvKeyProfiles = (RecyclerView) findViewById(R.id.rvKeyProfiles);
|
RecyclerView rvKeyProfiles = (RecyclerView) findViewById(R.id.rvKeyProfiles);
|
||||||
LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
|
LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
|
||||||
rvKeyProfiles.setLayoutManager(mLayoutManager);
|
rvKeyProfiles.setLayoutManager(mLayoutManager);
|
||||||
|
|
||||||
mKeyProfileAdapter = new KeyProfileAdapter(mKeyProfiles);
|
_keyProfileAdapter = new KeyProfileAdapter(_keyProfiles);
|
||||||
mKeyProfileAdapter.setOnItemClickListener((position, v) -> {
|
_keyProfileAdapter.setOnItemClickListener((position, v) -> {
|
||||||
clickedItemPosition = position;
|
_clickedItemPosition = position;
|
||||||
InitializeBottomSheet().show();
|
InitializeBottomSheet().show();
|
||||||
});
|
});
|
||||||
mKeyProfileAdapter.setOnLongItemClickListener((position, v) -> {
|
_keyProfileAdapter.setOnLongItemClickListener((position, v) -> {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(mKeyProfileAdapter);
|
ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(_keyProfileAdapter);
|
||||||
ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
|
ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
|
||||||
touchHelper.attachToRecyclerView(rvKeyProfiles);
|
touchHelper.attachToRecyclerView(rvKeyProfiles);
|
||||||
|
|
||||||
rvKeyProfiles.setAdapter(mKeyProfileAdapter);
|
rvKeyProfiles.setAdapter(_keyProfileAdapter);
|
||||||
Comparator<KeyProfile> comparator = new Comparator<KeyProfile>() {
|
Comparator<KeyProfile> comparator = new Comparator<KeyProfile>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(KeyProfile keyProfile, KeyProfile t1) {
|
public int compare(KeyProfile keyProfile, KeyProfile t1) {
|
||||||
return keyProfile.Order - t1.Order;
|
return keyProfile.getEntry().getOrder() - t1.getEntry().getOrder();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Collections.sort(mKeyProfiles, comparator);
|
Collections.sort(_keyProfiles, comparator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -151,6 +150,10 @@ public class MainActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onImportResult(int resultCode, Intent data) {
|
private void onImportResult(int resultCode, Intent data) {
|
||||||
|
if (resultCode != RESULT_OK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
InputStream stream = null;
|
InputStream stream = null;
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
|
@ -162,8 +165,8 @@ public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
FreeOTPImporter importer = new FreeOTPImporter(stream);
|
FreeOTPImporter importer = new FreeOTPImporter(stream);
|
||||||
try {
|
try {
|
||||||
for (KeyProfile profile : importer.convert()) {
|
for (DatabaseEntry profile : importer.convert()) {
|
||||||
addKey(profile);
|
addKey(new KeyProfile(profile));
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Toast.makeText(this, "An error occurred while trying to parse the file", Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, "An error occurred while trying to parse the file", Toast.LENGTH_SHORT).show();
|
||||||
|
@ -171,8 +174,10 @@ public class MainActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (stream != null) {
|
if (stream != null) {
|
||||||
try { stream.close(); }
|
try {
|
||||||
catch (Exception e) { }
|
stream.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,27 +208,29 @@ public class MainActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addKey(KeyProfile profile) {
|
private void addKey(KeyProfile profile) {
|
||||||
|
DatabaseEntry entry = profile.getEntry();
|
||||||
|
|
||||||
String otp;
|
String otp;
|
||||||
try {
|
try {
|
||||||
otp = OTP.generateOTP(profile.Info);
|
otp = OTP.generateOTP(entry.getInfo());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
profile.Name = profile.Info.getAccountName();
|
entry.setName(entry.getInfo().getAccountName());
|
||||||
profile.Order = mKeyProfiles.size() + 1;
|
entry.setOrder(_keyProfiles.size() + 1);
|
||||||
profile.Code = otp;
|
profile.setCode(otp);
|
||||||
try {
|
try {
|
||||||
db.addKey(profile);
|
_db.addKey(entry);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
// TODO: feedback
|
// TODO: feedback
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mKeyProfiles.add(profile);
|
_keyProfiles.add(profile);
|
||||||
mKeyProfileAdapter.notifyDataSetChanged();
|
_keyProfileAdapter.notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDoIntroResult(int resultCode, Intent data) {
|
private void onDoIntroResult(int resultCode, Intent data) {
|
||||||
|
@ -235,9 +242,9 @@ public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
MasterKey key = (MasterKey) data.getSerializableExtra("key");
|
MasterKey key = (MasterKey) data.getSerializableExtra("key");
|
||||||
try {
|
try {
|
||||||
db.load();
|
_db.load();
|
||||||
if (!db.isDecrypted()) {
|
if (!_db.isDecrypted()) {
|
||||||
db.setMasterKey(key);
|
_db.setMasterKey(key);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// TODO: feedback
|
// TODO: feedback
|
||||||
|
@ -250,7 +257,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
private void onDecryptResult(int resultCode, Intent data) {
|
private void onDecryptResult(int resultCode, Intent data) {
|
||||||
MasterKey key = (MasterKey) data.getSerializableExtra("key");
|
MasterKey key = (MasterKey) data.getSerializableExtra("key");
|
||||||
try {
|
try {
|
||||||
db.setMasterKey(key);
|
_db.setMasterKey(key);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// TODO: feedback
|
// TODO: feedback
|
||||||
throw new UndeclaredThrowableException(e);
|
throw new UndeclaredThrowableException(e);
|
||||||
|
@ -262,21 +269,12 @@ public class MainActivity extends AppCompatActivity {
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
mKeyProfileAdapter.notifyDataSetChanged();
|
_keyProfileAdapter.notifyDataSetChanged();
|
||||||
setPreferredTheme();
|
setPreferredTheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
// update order of keys
|
|
||||||
for (int i = 0; i < mKeyProfiles.size(); i++) {
|
|
||||||
try {
|
|
||||||
db.updateKey(mKeyProfiles.get(i));
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
saveDatabase();
|
saveDatabase();
|
||||||
super.onPause();
|
super.onPause();
|
||||||
}
|
}
|
||||||
|
@ -298,7 +296,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
copyLayout.setOnClickListener(view -> {
|
copyLayout.setOnClickListener(view -> {
|
||||||
bottomDialog.dismiss();
|
bottomDialog.dismiss();
|
||||||
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
ClipData clip = ClipData.newPlainText("text/plain", mKeyProfiles.get(clickedItemPosition).Code);
|
ClipData clip = ClipData.newPlainText("text/plain", _keyProfiles.get(_clickedItemPosition).getCode());
|
||||||
clipboard.setPrimaryClip(clip);
|
clipboard.setPrimaryClip(clip);
|
||||||
|
|
||||||
Toast.makeText(this.getApplicationContext(), "Code successfully copied to the clipboard", Toast.LENGTH_SHORT).show();
|
Toast.makeText(this.getApplicationContext(), "Code successfully copied to the clipboard", Toast.LENGTH_SHORT).show();
|
||||||
|
@ -307,7 +305,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
deleteLayout.setOnClickListener(view -> {
|
deleteLayout.setOnClickListener(view -> {
|
||||||
bottomDialog.dismiss();
|
bottomDialog.dismiss();
|
||||||
|
|
||||||
KeyProfile keyProfile = mKeyProfiles.get(clickedItemPosition);
|
KeyProfile keyProfile = _keyProfiles.get(_clickedItemPosition);
|
||||||
deleteProfile(keyProfile);
|
deleteProfile(keyProfile);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -326,14 +324,14 @@ public class MainActivity extends AppCompatActivity {
|
||||||
.setMessage("Are you sure you want to delete this profile?")
|
.setMessage("Are you sure you want to delete this profile?")
|
||||||
.setPositiveButton(android.R.string.yes, (dialog, which) -> {
|
.setPositiveButton(android.R.string.yes, (dialog, which) -> {
|
||||||
try {
|
try {
|
||||||
db.removeKey(profile);
|
_db.removeKey(profile.getEntry());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
//TODO: feedback
|
//TODO: feedback
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mKeyProfiles.remove(clickedItemPosition);
|
_keyProfiles.remove(_clickedItemPosition);
|
||||||
mKeyProfileAdapter.notifyItemRemoved(clickedItemPosition);
|
_keyProfileAdapter.notifyItemRemoved(_clickedItemPosition);
|
||||||
})
|
})
|
||||||
.setNegativeButton(android.R.string.no, (dialog, which) -> {
|
.setNegativeButton(android.R.string.no, (dialog, which) -> {
|
||||||
})
|
})
|
||||||
|
@ -342,7 +340,9 @@ public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
_menu = menu;
|
||||||
getMenuInflater().inflate(R.menu.menu_main, menu);
|
getMenuInflater().inflate(R.menu.menu_main, menu);
|
||||||
|
updateLockIcon();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,15 +367,11 @@ public class MainActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeAppShortcuts()
|
private void initializeAppShortcuts() {
|
||||||
{
|
|
||||||
String mode = getIntent().getStringExtra("Action");
|
String mode = getIntent().getStringExtra("Action");
|
||||||
if(mode != null)
|
if (mode != null) {
|
||||||
{
|
|
||||||
Log.println(Log.DEBUG, "MODE: ", mode);
|
Log.println(Log.DEBUG, "MODE: ", mode);
|
||||||
if(Objects.equals(mode, "Scan"))
|
if (Objects.equals(mode, "Scan")) {
|
||||||
{
|
|
||||||
Log.println(Log.DEBUG, "OKK ", "OKKK");
|
|
||||||
Intent scannerActivity = new Intent(getApplicationContext(), ScannerActivity.class);
|
Intent scannerActivity = new Intent(getApplicationContext(), ScannerActivity.class);
|
||||||
startActivityForResult(scannerActivity, CODE_GET_KEYINFO);
|
startActivityForResult(scannerActivity, CODE_GET_KEYINFO);
|
||||||
}
|
}
|
||||||
|
@ -384,7 +380,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
ShortcutManager shortcutManager = null;
|
ShortcutManager shortcutManager = null;
|
||||||
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
||||||
shortcutManager = getSystemService(ShortcutManager.class);
|
shortcutManager = getSystemService(ShortcutManager.class);
|
||||||
if(shortcutManager != null) {
|
if (shortcutManager != null) {
|
||||||
//TODO: Remove this line
|
//TODO: Remove this line
|
||||||
shortcutManager.removeAllDynamicShortcuts();
|
shortcutManager.removeAllDynamicShortcuts();
|
||||||
if (shortcutManager.getDynamicShortcuts().size() == 0) {
|
if (shortcutManager.getDynamicShortcuts().size() == 0) {
|
||||||
|
@ -394,48 +390,47 @@ public class MainActivity extends AppCompatActivity {
|
||||||
intent1.putExtra("Action", "Scan");
|
intent1.putExtra("Action", "Scan");
|
||||||
intent1.setAction(Intent.ACTION_MAIN);
|
intent1.setAction(Intent.ACTION_MAIN);
|
||||||
|
|
||||||
ShortcutInfo shortcut = new ShortcutInfo.Builder(this, "id1")
|
ShortcutInfo shortcut = new ShortcutInfo.Builder(this, "id1")
|
||||||
.setShortLabel("New profile")
|
.setShortLabel("New profile")
|
||||||
.setLongLabel("Add new profile")
|
.setLongLabel("Add new profile")
|
||||||
.setIcon(Icon.createWithResource(this.getApplicationContext(), R.drawable.intro_scanner))
|
.setIcon(Icon.createWithResource(this.getApplicationContext(), R.drawable.intro_scanner))
|
||||||
.setIntent(intent1)
|
.setIntent(intent1)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
shortcutManager.setDynamicShortcuts(Arrays.asList(shortcut));
|
shortcutManager.setDynamicShortcuts(Arrays.asList(shortcut));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setPreferredTheme()
|
private void setPreferredTheme() {
|
||||||
{
|
|
||||||
boolean restart = false;
|
boolean restart = false;
|
||||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
if(sharedPreferences.getBoolean("pref_night_mode", false)) {
|
if (sharedPreferences.getBoolean("pref_night_mode", false)) {
|
||||||
if(!nightMode) {
|
if (!_nightMode) {
|
||||||
setTheme(R.style.AppTheme_Dark_NoActionBar);
|
setTheme(R.style.AppTheme_Dark_NoActionBar);
|
||||||
restart = true;
|
restart = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(nightMode) {
|
if (_nightMode) {
|
||||||
setTheme(R.style.AppTheme_Default_NoActionBar);
|
setTheme(R.style.AppTheme_Default_NoActionBar);
|
||||||
restart = true;
|
restart = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(restart){
|
if (restart) {
|
||||||
finish();
|
finish();
|
||||||
startActivity(new Intent(this, this.getClass()));
|
startActivity(new Intent(this, this.getClass()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveDatabase() {
|
private void saveDatabase() {
|
||||||
if (!db.isDecrypted()) {
|
if (!_db.isDecrypted()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.save();
|
_db.save();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
//TODO: feedback
|
//TODO: feedback
|
||||||
throw new UndeclaredThrowableException(e);
|
throw new UndeclaredThrowableException(e);
|
||||||
|
@ -443,11 +438,23 @@ public class MainActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadKeyProfiles() {
|
private void loadKeyProfiles() {
|
||||||
|
updateLockIcon();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mKeyProfiles.addAll(db.getKeys());
|
for (DatabaseEntry entry : _db.getKeys()) {
|
||||||
mKeyProfileAdapter.notifyDataSetChanged();
|
_keyProfiles.add(new KeyProfile(entry));
|
||||||
|
}
|
||||||
|
_keyProfileAdapter.notifyDataSetChanged();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateLockIcon() {
|
||||||
|
// hide the lock icon if the database is not encrypted
|
||||||
|
if (_menu != null && _db.isDecrypted()) {
|
||||||
|
MenuItem item = _menu.findItem(R.id.action_lock);
|
||||||
|
item.setVisible(_db.getFile().isEncrypted());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
package me.impy.aegis;
|
package me.impy.aegis;
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
import android.preference.PreferenceActivity;
|
|
||||||
import android.preference.PreferenceFragment;
|
import android.preference.PreferenceFragment;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.support.v7.app.AppCompatDelegate;
|
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
public class PreferencesActivity extends AppCompatActivity {
|
public class PreferencesActivity extends AppCompatActivity {
|
||||||
|
@ -16,24 +13,23 @@ public class PreferencesActivity extends AppCompatActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
SharedPreferences mySharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
SharedPreferences mySharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
if(mySharedPreferences.getBoolean("pref_night_mode", false))
|
if (mySharedPreferences.getBoolean("pref_night_mode", false)) {
|
||||||
{
|
|
||||||
setTheme(R.style.AppTheme_Dark);
|
setTheme(R.style.AppTheme_Dark);
|
||||||
} else
|
} else {
|
||||||
{
|
|
||||||
setTheme(R.style.AppTheme_Default);
|
setTheme(R.style.AppTheme_Default);
|
||||||
}
|
}
|
||||||
getFragmentManager().beginTransaction().replace(android.R.id.content, new PreferencesFragment()).commit();
|
|
||||||
|
|
||||||
|
getFragmentManager().beginTransaction().replace(android.R.id.content, new PreferencesFragment()).commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class PreferencesFragment extends PreferenceFragment {
|
public static class PreferencesFragment extends PreferenceFragment {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// Load the preferences from an XML resource
|
|
||||||
addPreferencesFromResource(R.xml.preferences);
|
addPreferencesFromResource(R.xml.preferences);
|
||||||
|
|
||||||
final Preference nightModePreference = findPreference("pref_night_mode");
|
final Preference nightModePreference = findPreference("pref_night_mode");
|
||||||
|
|
|
@ -12,32 +12,34 @@ import android.widget.Toast;
|
||||||
import com.google.zxing.BarcodeFormat;
|
import com.google.zxing.BarcodeFormat;
|
||||||
import com.google.zxing.Result;
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import me.dm7.barcodescanner.core.IViewFinder;
|
import me.dm7.barcodescanner.core.IViewFinder;
|
||||||
import me.dm7.barcodescanner.zxing.ZXingScannerView;
|
import me.dm7.barcodescanner.zxing.ZXingScannerView;
|
||||||
import me.impy.aegis.crypto.KeyInfo;
|
import me.impy.aegis.crypto.KeyInfo;
|
||||||
|
import me.impy.aegis.db.DatabaseEntry;
|
||||||
import me.impy.aegis.helpers.SquareFinderView;
|
import me.impy.aegis.helpers.SquareFinderView;
|
||||||
|
|
||||||
public class ScannerActivity extends Activity implements ZXingScannerView.ResultHandler {
|
public class ScannerActivity extends Activity implements ZXingScannerView.ResultHandler {
|
||||||
private ZXingScannerView mScannerView;
|
private static final int CODE_ASK_PERMS = 0;
|
||||||
|
|
||||||
|
private ZXingScannerView _scannerView;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle state) {
|
public void onCreate(Bundle state) {
|
||||||
super.onCreate(state);
|
super.onCreate(state);
|
||||||
|
|
||||||
mScannerView = new ZXingScannerView(this) {
|
_scannerView = new ZXingScannerView(this) {
|
||||||
@Override
|
@Override
|
||||||
protected IViewFinder createViewFinderView(Context context) {
|
protected IViewFinder createViewFinderView(Context context) {
|
||||||
return new SquareFinderView(context);
|
return new SquareFinderView(context);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
setContentView(mScannerView); // Set the scanner view as the content view
|
setContentView(_scannerView);
|
||||||
mScannerView.setFormats(getSupportedFormats());
|
_scannerView.setFormats(Collections.singletonList(BarcodeFormat.QR_CODE));
|
||||||
|
|
||||||
ActivityCompat.requestPermissions(ScannerActivity.this, new String[]{Manifest.permission.CAMERA}, 1);
|
ActivityCompat.requestPermissions(ScannerActivity.this, new String[]{Manifest.permission.CAMERA}, CODE_ASK_PERMS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -48,54 +50,39 @@ public class ScannerActivity extends Activity implements ZXingScannerView.Result
|
||||||
@Override
|
@Override
|
||||||
public void onPause() {
|
public void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
mScannerView.stopCamera(); // Stop camera on pause
|
_scannerView.stopCamera();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleResult(Result rawResult) {
|
public void handleResult(Result rawResult) {
|
||||||
// Do something with the result here
|
|
||||||
Toast.makeText(this, rawResult.getText(), Toast.LENGTH_SHORT).show();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//TODO: Handle non TOTP / HOTP qr codes.
|
|
||||||
KeyInfo info = KeyInfo.fromURL(rawResult.getText());
|
KeyInfo info = KeyInfo.fromURL(rawResult.getText());
|
||||||
KeyProfile keyProfile = new KeyProfile();
|
KeyProfile profile = new KeyProfile(new DatabaseEntry(info));
|
||||||
keyProfile.Info = info;
|
|
||||||
|
|
||||||
Intent resultIntent = new Intent();
|
Intent resultIntent = new Intent();
|
||||||
resultIntent.putExtra("KeyProfile", keyProfile);
|
resultIntent.putExtra("KeyProfile", profile);
|
||||||
|
|
||||||
setResult(Activity.RESULT_OK, resultIntent);
|
setResult(Activity.RESULT_OK, resultIntent);
|
||||||
finish();
|
finish();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
Toast.makeText(this, "An error occurred while trying to parse the QR code contents", Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If you would like to resume scanning, call this method below:
|
_scannerView.resumeCameraPreview(this);
|
||||||
mScannerView.resumeCameraPreview(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
|
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
|
||||||
switch (requestCode) {
|
switch (requestCode) {
|
||||||
case 1: {
|
case CODE_ASK_PERMS: {
|
||||||
// If request is cancelled, the result arrays are empty.
|
|
||||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
_scannerView.setResultHandler(this);
|
||||||
mScannerView.setResultHandler(this); // Register ourselves as a handler for scan results.
|
_scannerView.startCamera();
|
||||||
mScannerView.startCamera(); // Start camera on resume
|
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(ScannerActivity.this, "Permission denied to get access to the camera", Toast.LENGTH_SHORT).show();
|
Toast.makeText(ScannerActivity.this, "Permission denied to get access to the camera", Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<BarcodeFormat> getSupportedFormats() {
|
|
||||||
ArrayList<BarcodeFormat> supportedFormats = new ArrayList<>();
|
|
||||||
supportedFormats.add(BarcodeFormat.QR_CODE);
|
|
||||||
|
|
||||||
return supportedFormats;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ public class CryptoUtils {
|
||||||
public static final byte CRYPTO_NONCE_SIZE = 12;
|
public static final byte CRYPTO_NONCE_SIZE = 12;
|
||||||
public static final byte CRYPTO_SALT_SIZE = 32;
|
public static final byte CRYPTO_SALT_SIZE = 32;
|
||||||
|
|
||||||
public static final int CRYPTO_SCRYPT_N = 2 << 14;
|
public static final int CRYPTO_SCRYPT_N = 1 << 15;
|
||||||
public static final int CRYPTO_SCRYPT_r = 8;
|
public static final int CRYPTO_SCRYPT_r = 8;
|
||||||
public static final int CRYPTO_SCRYPT_p = 1;
|
public static final int CRYPTO_SCRYPT_p = 1;
|
||||||
|
|
||||||
|
|
|
@ -7,39 +7,39 @@ import java.io.Serializable;
|
||||||
import me.impy.aegis.encoding.Base32;
|
import me.impy.aegis.encoding.Base32;
|
||||||
|
|
||||||
public class KeyInfo implements Serializable {
|
public class KeyInfo implements Serializable {
|
||||||
private String type;
|
private String _type;
|
||||||
private byte[] secret;
|
private byte[] _secret;
|
||||||
private String accountName;
|
private String _accountName;
|
||||||
private String issuer;
|
private String _issuer;
|
||||||
private long counter;
|
private long _counter;
|
||||||
private String algorithm = "SHA1";
|
private String _algorithm = "SHA1";
|
||||||
private int digits = 6;
|
private int _digits = 6;
|
||||||
private int period = 30;
|
private int _period = 30;
|
||||||
|
|
||||||
public String getURL() throws Exception {
|
public String getURL() throws Exception {
|
||||||
Uri.Builder builder = new Uri.Builder();
|
Uri.Builder builder = new Uri.Builder();
|
||||||
builder.scheme("otpauth");
|
builder.scheme("otpauth");
|
||||||
builder.authority(type);
|
builder.authority(_type);
|
||||||
|
|
||||||
builder.appendQueryParameter("period", Integer.toString(period));
|
builder.appendQueryParameter("period", Integer.toString(_period));
|
||||||
builder.appendQueryParameter("algorithm", algorithm);
|
builder.appendQueryParameter("algorithm", _algorithm);
|
||||||
builder.appendQueryParameter("secret", Base32.encodeOriginal(secret));
|
builder.appendQueryParameter("secret", Base32.encodeOriginal(_secret));
|
||||||
if (type.equals("hotp")) {
|
if (_type.equals("hotp")) {
|
||||||
builder.appendQueryParameter("counter", Long.toString(counter));
|
builder.appendQueryParameter("counter", Long.toString(_counter));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!issuer.equals("")) {
|
if (!_issuer.equals("")) {
|
||||||
builder.path(String.format("%s:%s", issuer, accountName));
|
builder.path(String.format("%s:%s", _issuer, _accountName));
|
||||||
builder.appendQueryParameter("issuer", issuer);
|
builder.appendQueryParameter("issuer", _issuer);
|
||||||
} else {
|
} else {
|
||||||
builder.path(accountName);
|
builder.path(_accountName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.build().toString();
|
return builder.build().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getMillisTillNextRotation() {
|
public long getMillisTillNextRotation() {
|
||||||
long p = period * 1000;
|
long p = _period * 1000;
|
||||||
return p - (System.currentTimeMillis() % p);
|
return p - (System.currentTimeMillis() % p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,8 +52,8 @@ public class KeyInfo implements Serializable {
|
||||||
KeyInfo info = new KeyInfo();
|
KeyInfo info = new KeyInfo();
|
||||||
|
|
||||||
// only 'totp' and 'hotp' are supported
|
// only 'totp' and 'hotp' are supported
|
||||||
info.type = url.getHost();
|
info._type = url.getHost();
|
||||||
if (info.type.equals("totp") && info.type.equals("hotp")) {
|
if (info._type.equals("totp") && info._type.equals("hotp")) {
|
||||||
throw new Exception("unsupported type");
|
throw new Exception("unsupported type");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ public class KeyInfo implements Serializable {
|
||||||
if (secret == null) {
|
if (secret == null) {
|
||||||
throw new Exception("'secret' is not set");
|
throw new Exception("'secret' is not set");
|
||||||
}
|
}
|
||||||
info.secret = Base32.decode(secret);
|
info._secret = Base32.decode(secret);
|
||||||
|
|
||||||
// provider info used to disambiguate accounts
|
// provider info used to disambiguate accounts
|
||||||
String path = url.getPath();
|
String path = url.getPath();
|
||||||
|
@ -74,39 +74,39 @@ public class KeyInfo implements Serializable {
|
||||||
String[] strings = label.split(":");
|
String[] strings = label.split(":");
|
||||||
|
|
||||||
if (strings.length == 2) {
|
if (strings.length == 2) {
|
||||||
info.issuer = strings[0];
|
info._issuer = strings[0];
|
||||||
info.accountName = strings[1];
|
info._accountName = strings[1];
|
||||||
} else {
|
} else {
|
||||||
// at this point, just dump the whole thing into the accountName
|
// at this point, just dump the whole thing into the accountName
|
||||||
info.accountName = label;
|
info._accountName = label;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// label only contains the account name
|
// label only contains the account name
|
||||||
// grab the issuer's info from the 'issuer' parameter if it's present
|
// grab the issuer's info from the 'issuer' parameter if it's present
|
||||||
String issuer = url.getQueryParameter("issuer");
|
String issuer = url.getQueryParameter("issuer");
|
||||||
info.issuer = issuer != null ? issuer : "";
|
info._issuer = issuer != null ? issuer : "";
|
||||||
info.accountName = label;
|
info._accountName = label;
|
||||||
}
|
}
|
||||||
|
|
||||||
// just use the defaults if these parameters aren't set
|
// just use the defaults if these parameters aren't set
|
||||||
String algorithm = url.getQueryParameter("algorithm");
|
String algorithm = url.getQueryParameter("algorithm");
|
||||||
if (algorithm != null) {
|
if (algorithm != null) {
|
||||||
info.algorithm = algorithm;
|
info._algorithm = algorithm;
|
||||||
}
|
}
|
||||||
String period = url.getQueryParameter("period");
|
String period = url.getQueryParameter("period");
|
||||||
if (period != null) {
|
if (period != null) {
|
||||||
info.period = Integer.parseInt(period);
|
info._period = Integer.parseInt(period);
|
||||||
}
|
}
|
||||||
String digits = url.getQueryParameter("digits");
|
String digits = url.getQueryParameter("digits");
|
||||||
if (digits != null) {
|
if (digits != null) {
|
||||||
info.digits = Integer.parseInt(digits);
|
info._digits = Integer.parseInt(digits);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 'counter' is required if the type is 'hotp'
|
// 'counter' is required if the type is 'hotp'
|
||||||
String counter = url.getQueryParameter("counter");
|
String counter = url.getQueryParameter("counter");
|
||||||
if (counter != null) {
|
if (counter != null) {
|
||||||
info.counter = Long.parseLong(counter);
|
info._counter = Long.parseLong(counter);
|
||||||
} else if (info.type.equals("hotp")) {
|
} else if (info._type.equals("hotp")) {
|
||||||
throw new Exception("'counter' was not set which is required for 'hotp'");
|
throw new Exception("'counter' was not set which is required for 'hotp'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,55 +114,55 @@ public class KeyInfo implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getType() {
|
public String getType() {
|
||||||
return type;
|
return _type;
|
||||||
}
|
}
|
||||||
public byte[] getSecret() {
|
public byte[] getSecret() {
|
||||||
return secret;
|
return _secret;
|
||||||
}
|
}
|
||||||
public String getAccountName() {
|
public String getAccountName() {
|
||||||
return accountName;
|
return _accountName;
|
||||||
}
|
}
|
||||||
public String getIssuer() {
|
public String getIssuer() {
|
||||||
return issuer;
|
return _issuer;
|
||||||
}
|
}
|
||||||
public String getAlgorithm() {
|
public String getAlgorithm() {
|
||||||
return "Hmac" + algorithm;
|
return "Hmac" + _algorithm;
|
||||||
}
|
}
|
||||||
public int getDigits() {
|
public int getDigits() {
|
||||||
return digits;
|
return _digits;
|
||||||
}
|
}
|
||||||
public long getCounter() {
|
public long getCounter() {
|
||||||
return counter;
|
return _counter;
|
||||||
}
|
}
|
||||||
public int getPeriod() {
|
public int getPeriod() {
|
||||||
return period;
|
return _period;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setType(String type) {
|
public void setType(String type) {
|
||||||
this.type = type.toLowerCase();
|
_type = type.toLowerCase();
|
||||||
}
|
}
|
||||||
public void setSecret(byte[] secret) {
|
public void setSecret(byte[] secret) {
|
||||||
this.secret = secret;
|
_secret = secret;
|
||||||
}
|
}
|
||||||
public void setAccountName(String accountName) {
|
public void setAccountName(String accountName) {
|
||||||
this.accountName = accountName;
|
_accountName = accountName;
|
||||||
}
|
}
|
||||||
public void setIssuer(String issuer) {
|
public void setIssuer(String issuer) {
|
||||||
this.issuer = issuer;
|
_issuer = issuer;
|
||||||
}
|
}
|
||||||
public void setAlgorithm(String algorithm) {
|
public void setAlgorithm(String algorithm) {
|
||||||
if (algorithm.startsWith("Hmac")) {
|
if (algorithm.startsWith("Hmac")) {
|
||||||
algorithm = algorithm.substring(4);
|
algorithm = algorithm.substring(4);
|
||||||
}
|
}
|
||||||
this.algorithm = algorithm.toUpperCase();
|
_algorithm = algorithm.toUpperCase();
|
||||||
}
|
}
|
||||||
public void setDigits(int digits) {
|
public void setDigits(int digits) {
|
||||||
this.digits = digits;
|
_digits = digits;
|
||||||
}
|
}
|
||||||
public void setCounter(long count) {
|
public void setCounter(long count) {
|
||||||
counter = count;
|
_counter = count;
|
||||||
}
|
}
|
||||||
public void setPeriod(int period) {
|
public void setPeriod(int period) {
|
||||||
this.period = period;
|
_period = period;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,17 +15,17 @@ import javax.crypto.KeyGenerator;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
|
|
||||||
public class KeyStoreHandle {
|
public class KeyStoreHandle {
|
||||||
private final KeyStore keyStore;
|
private final KeyStore _keyStore;
|
||||||
private static final String KEY_NAME = "AegisKey";
|
private static final String KEY_NAME = "AegisKey";
|
||||||
private static final String STORE_NAME = "AndroidKeyStore";
|
private static final String STORE_NAME = "AndroidKeyStore";
|
||||||
|
|
||||||
public KeyStoreHandle() throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
|
public KeyStoreHandle() throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
|
||||||
keyStore = KeyStore.getInstance(STORE_NAME);
|
_keyStore = KeyStore.getInstance(STORE_NAME);
|
||||||
keyStore.load(null);
|
_keyStore.load(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean keyExists() throws KeyStoreException {
|
public boolean keyExists() throws KeyStoreException {
|
||||||
return keyStore.containsAlias(KEY_NAME);
|
return _keyStore.containsAlias(KEY_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SecretKey generateKey(boolean authRequired) throws Exception {
|
public SecretKey generateKey(boolean authRequired) throws Exception {
|
||||||
|
@ -47,6 +47,6 @@ public class KeyStoreHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
public SecretKey getKey() throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException {
|
public SecretKey getKey() throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException {
|
||||||
return (SecretKey) keyStore.getKey(KEY_NAME, null);
|
return (SecretKey) _keyStore.getKey(KEY_NAME, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,10 @@ public class SlotCollection implements Iterable<Slot>, Serializable {
|
||||||
return _slots.size();
|
return _slots.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return _slots.size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
public <T extends Slot> T find(Class<T> type) {
|
public <T extends Slot> T find(Class<T> type) {
|
||||||
for (Slot slot : this) {
|
for (Slot slot : this) {
|
||||||
if (slot.getClass() == type) {
|
if (slot.getClass() == type) {
|
||||||
|
|
|
@ -1,28 +1,23 @@
|
||||||
package me.impy.aegis.db;
|
package me.impy.aegis.db;
|
||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import me.impy.aegis.KeyProfile;
|
|
||||||
import me.impy.aegis.crypto.KeyInfo;
|
|
||||||
|
|
||||||
public class Database {
|
public class Database {
|
||||||
private static final int version = 1;
|
private static final int VERSION = 1;
|
||||||
private List<DatabaseEntry> entries = new ArrayList<>();
|
private List<DatabaseEntry> _entries = new ArrayList<>();
|
||||||
|
|
||||||
public byte[] serialize() throws JSONException, UnsupportedEncodingException {
|
public byte[] serialize() throws Exception {
|
||||||
JSONArray array = new JSONArray();
|
JSONArray array = new JSONArray();
|
||||||
for (DatabaseEntry e : entries) {
|
for (DatabaseEntry e : _entries) {
|
||||||
array.put(e.serialize());
|
array.put(e.serialize());
|
||||||
}
|
}
|
||||||
|
|
||||||
JSONObject obj = new JSONObject();
|
JSONObject obj = new JSONObject();
|
||||||
obj.put("version", version);
|
obj.put("version", VERSION);
|
||||||
obj.put("entries", array);
|
obj.put("entries", array);
|
||||||
|
|
||||||
return obj.toString().getBytes("UTF-8");
|
return obj.toString().getBytes("UTF-8");
|
||||||
|
@ -31,64 +26,30 @@ public class Database {
|
||||||
public void deserialize(byte[] data) throws Exception {
|
public void deserialize(byte[] data) throws Exception {
|
||||||
JSONObject obj = new JSONObject(new String(data, "UTF-8"));
|
JSONObject obj = new JSONObject(new String(data, "UTF-8"));
|
||||||
|
|
||||||
// TODO: support different version deserialization providers
|
// TODO: support different VERSION deserialization providers
|
||||||
int ver = obj.getInt("version");
|
int ver = obj.getInt("version");
|
||||||
if (ver != version) {
|
if (ver != VERSION) {
|
||||||
throw new Exception("Unsupported version");
|
throw new Exception("Unsupported version");
|
||||||
}
|
}
|
||||||
|
|
||||||
JSONArray array = obj.getJSONArray("entries");
|
JSONArray array = obj.getJSONArray("entries");
|
||||||
for (int i = 0; i < array.length(); i++) {
|
for (int i = 0; i < array.length(); i++) {
|
||||||
DatabaseEntry e = new DatabaseEntry();
|
DatabaseEntry e = new DatabaseEntry(null);
|
||||||
e.deserialize(array.getJSONObject(i));
|
e.deserialize(array.getJSONObject(i));
|
||||||
entries.add(e);
|
_entries.add(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addKey(KeyProfile profile) throws Exception {
|
public void addKey(DatabaseEntry entry) throws Exception {
|
||||||
DatabaseEntry e = new DatabaseEntry();
|
entry.setID(_entries.size() + 1);
|
||||||
e.Name = profile.Name;
|
_entries.add(entry);
|
||||||
e.URL = profile.Info.getURL();
|
|
||||||
e.Order = profile.Order;
|
|
||||||
e.ID = entries.size() + 1;
|
|
||||||
profile.ID = e.ID;
|
|
||||||
entries.add(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateKey(KeyProfile profile) throws Exception {
|
public void removeKey(DatabaseEntry entry) throws Exception {
|
||||||
DatabaseEntry e = findEntry(profile);
|
_entries.remove(entry);
|
||||||
e.Name = profile.Name;
|
|
||||||
e.URL = profile.Info.getURL();
|
|
||||||
e.Order = profile.Order;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeKey(KeyProfile profile) throws Exception {
|
public List<DatabaseEntry> getKeys() throws Exception {
|
||||||
DatabaseEntry e = findEntry(profile);
|
return _entries;
|
||||||
entries.remove(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<KeyProfile> getKeys() throws Exception {
|
|
||||||
List<KeyProfile> list = new ArrayList<>();
|
|
||||||
|
|
||||||
for (DatabaseEntry e : entries) {
|
|
||||||
KeyProfile profile = new KeyProfile();
|
|
||||||
profile.Name = e.Name;
|
|
||||||
profile.Info = KeyInfo.fromURL(e.URL);
|
|
||||||
profile.Order = e.Order;
|
|
||||||
profile.ID = e.ID;
|
|
||||||
list.add(profile);
|
|
||||||
}
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
private DatabaseEntry findEntry(KeyProfile profile) throws Exception {
|
|
||||||
for (DatabaseEntry e : entries) {
|
|
||||||
if (e.ID == profile.ID) {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Exception("Key doesn't exist");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,67 @@
|
||||||
package me.impy.aegis.db;
|
package me.impy.aegis.db;
|
||||||
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
public class DatabaseEntry {
|
import java.io.Serializable;
|
||||||
public int ID;
|
|
||||||
public String Name;
|
|
||||||
public String URL;
|
|
||||||
public int Order;
|
|
||||||
|
|
||||||
public JSONObject serialize() throws JSONException {
|
import me.impy.aegis.crypto.KeyInfo;
|
||||||
|
|
||||||
|
public class DatabaseEntry implements Serializable {
|
||||||
|
public int _id;
|
||||||
|
public String _name;
|
||||||
|
public String _icon;
|
||||||
|
public KeyInfo _info;
|
||||||
|
public int _order;
|
||||||
|
|
||||||
|
public DatabaseEntry(KeyInfo info) {
|
||||||
|
_info = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSONObject serialize() throws Exception {
|
||||||
JSONObject obj = new JSONObject();
|
JSONObject obj = new JSONObject();
|
||||||
obj.put("id", ID);
|
obj.put("id", _id);
|
||||||
obj.put("name", Name);
|
obj.put("name", _name);
|
||||||
obj.put("url", URL);
|
obj.put("url", _info.getURL());
|
||||||
obj.put("order", Order);
|
obj.put("order", _order);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deserialize(JSONObject obj) throws JSONException {
|
public void deserialize(JSONObject obj) throws Exception {
|
||||||
ID = obj.getInt("id");
|
_id = obj.getInt("id");
|
||||||
Name = obj.getString("name");
|
_name = obj.getString("name");
|
||||||
URL = obj.getString("url");
|
_info = KeyInfo.fromURL(obj.getString("url"));
|
||||||
Order = obj.getInt("order");
|
_order = obj.getInt("order");
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getID() {
|
||||||
|
return _id;
|
||||||
|
}
|
||||||
|
public String getName() {
|
||||||
|
return _name;
|
||||||
|
}
|
||||||
|
public String getIcon() {
|
||||||
|
return _icon;
|
||||||
|
}
|
||||||
|
public KeyInfo getInfo() {
|
||||||
|
return _info;
|
||||||
|
}
|
||||||
|
public int getOrder() {
|
||||||
|
return _order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setID(int id) {
|
||||||
|
_id = id;
|
||||||
|
}
|
||||||
|
public void setName(String name) {
|
||||||
|
_name = name;
|
||||||
|
}
|
||||||
|
public void setIcon(String icon) {
|
||||||
|
_icon = icon;
|
||||||
|
}
|
||||||
|
public void setInfo(KeyInfo info) {
|
||||||
|
_info = info;
|
||||||
|
}
|
||||||
|
public void setOrder(int order) {
|
||||||
|
_order = order;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,50 +16,50 @@ import me.impy.aegis.crypto.CryptoUtils;
|
||||||
import me.impy.aegis.util.LittleByteBuffer;
|
import me.impy.aegis.util.LittleByteBuffer;
|
||||||
|
|
||||||
public class DatabaseFile {
|
public class DatabaseFile {
|
||||||
private static final byte bSectionEncryptionParameters = 0x00;
|
private static final byte SECTION_ENCRYPTION_PARAMETERS = 0x00;
|
||||||
private static final byte bSectionSlots = 0x01;
|
private static final byte SECTION_SLOTS = 0x01;
|
||||||
private static final byte bSectionEnd = (byte) 0xFF;
|
private static final byte SECTION_END = (byte) 0xFF;
|
||||||
private static final byte bVersion = 1;
|
private static final byte VERSION = 1;
|
||||||
private static final String dbFilename = "aegis.db";
|
private static final String FILENAME = "aegis.db";
|
||||||
|
|
||||||
private final byte[] bHeader;
|
private final byte[] HEADER;
|
||||||
|
|
||||||
private byte[] content;
|
private byte[] _content;
|
||||||
private CryptParameters cryptParameters;
|
private CryptParameters _cryptParameters;
|
||||||
private SlotCollection slots;
|
private SlotCollection _slots;
|
||||||
|
|
||||||
public DatabaseFile() {
|
public DatabaseFile() {
|
||||||
try {
|
try {
|
||||||
bHeader = "AEGIS".getBytes("US_ASCII");
|
HEADER = "AEGIS".getBytes("US_ASCII");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new UndeclaredThrowableException(e);
|
throw new UndeclaredThrowableException(e);
|
||||||
}
|
}
|
||||||
slots = new SlotCollection();
|
_slots = new SlotCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] serialize() throws IOException {
|
public byte[] serialize() throws IOException {
|
||||||
CryptParameters cryptParams = getCryptParameters();
|
|
||||||
byte[] content = getContent();
|
byte[] content = getContent();
|
||||||
|
CryptParameters cryptParams = getCryptParameters();
|
||||||
|
|
||||||
// this is dumb, java doesn't provide an endianness-aware data stream
|
// this is dumb, java doesn't provide an endianness-aware data stream
|
||||||
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
||||||
DataOutputStream stream = new DataOutputStream(byteStream);
|
DataOutputStream stream = new DataOutputStream(byteStream);
|
||||||
stream.write(bHeader);
|
stream.write(HEADER);
|
||||||
stream.write(bVersion);
|
stream.write(VERSION);
|
||||||
|
|
||||||
if (cryptParams != null) {
|
if (cryptParams != null) {
|
||||||
LittleByteBuffer paramBuffer = LittleByteBuffer.allocate(CryptoUtils.CRYPTO_NONCE_SIZE + CryptoUtils.CRYPTO_TAG_SIZE);
|
LittleByteBuffer paramBuffer = LittleByteBuffer.allocate(CryptoUtils.CRYPTO_NONCE_SIZE + CryptoUtils.CRYPTO_TAG_SIZE);
|
||||||
paramBuffer.put(cryptParams.Nonce);
|
paramBuffer.put(cryptParams.Nonce);
|
||||||
paramBuffer.put(cryptParams.Tag);
|
paramBuffer.put(cryptParams.Tag);
|
||||||
writeSection(stream, bSectionEncryptionParameters, paramBuffer.array());
|
writeSection(stream, SECTION_ENCRYPTION_PARAMETERS, paramBuffer.array());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (slots != null) {
|
if (!_slots.isEmpty()) {
|
||||||
byte[] bytes = SlotCollection.serialize(slots);
|
byte[] bytes = SlotCollection.serialize(_slots);
|
||||||
writeSection(stream, bSectionSlots, bytes);
|
writeSection(stream, SECTION_SLOTS, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeSection(stream, bSectionEnd, null);
|
writeSection(stream, SECTION_END, null);
|
||||||
stream.write(content);
|
stream.write(content);
|
||||||
return byteStream.toByteArray();
|
return byteStream.toByteArray();
|
||||||
}
|
}
|
||||||
|
@ -67,25 +67,25 @@ public class DatabaseFile {
|
||||||
public void deserialize(byte[] data) throws Exception {
|
public void deserialize(byte[] data) throws Exception {
|
||||||
LittleByteBuffer buffer = LittleByteBuffer.wrap(data);
|
LittleByteBuffer buffer = LittleByteBuffer.wrap(data);
|
||||||
|
|
||||||
byte[] header = new byte[bHeader.length];
|
byte[] header = new byte[HEADER.length];
|
||||||
buffer.get(header);
|
buffer.get(header);
|
||||||
if (!Arrays.equals(header, bHeader)) {
|
if (!Arrays.equals(header, HEADER)) {
|
||||||
throw new Exception("Bad header");
|
throw new Exception("Bad header");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: support different version deserialization providers
|
// TODO: support different version deserialization providers
|
||||||
byte version = buffer.get();
|
byte version = buffer.get();
|
||||||
if (version != bVersion) {
|
if (version != VERSION) {
|
||||||
throw new Exception("Unsupported version");
|
throw new Exception("Unsupported version");
|
||||||
}
|
}
|
||||||
|
|
||||||
CryptParameters cryptParams = null;
|
CryptParameters cryptParams = null;
|
||||||
SlotCollection slots = null;
|
SlotCollection slots = new SlotCollection();
|
||||||
|
|
||||||
for (section s = readSection(buffer); s.ID != bSectionEnd; s = readSection(buffer)) {
|
for (section s = readSection(buffer); s.ID != SECTION_END; s = readSection(buffer)) {
|
||||||
LittleByteBuffer sBuff = LittleByteBuffer.wrap(s.Data);
|
LittleByteBuffer sBuff = LittleByteBuffer.wrap(s.Data);
|
||||||
switch (s.ID) {
|
switch (s.ID) {
|
||||||
case bSectionEncryptionParameters:
|
case SECTION_ENCRYPTION_PARAMETERS:
|
||||||
assertLength(s.Data, CryptoUtils.CRYPTO_NONCE_SIZE + CryptoUtils.CRYPTO_TAG_SIZE);
|
assertLength(s.Data, CryptoUtils.CRYPTO_NONCE_SIZE + CryptoUtils.CRYPTO_TAG_SIZE);
|
||||||
|
|
||||||
byte[] nonce = new byte[CryptoUtils.CRYPTO_NONCE_SIZE];
|
byte[] nonce = new byte[CryptoUtils.CRYPTO_NONCE_SIZE];
|
||||||
|
@ -98,7 +98,7 @@ public class DatabaseFile {
|
||||||
Tag = tag;
|
Tag = tag;
|
||||||
}};
|
}};
|
||||||
break;
|
break;
|
||||||
case bSectionSlots:
|
case SECTION_SLOTS:
|
||||||
slots = SlotCollection.deserialize(s.Data);
|
slots = SlotCollection.deserialize(s.Data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -113,19 +113,19 @@ public class DatabaseFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEncrypted() {
|
public boolean isEncrypted() {
|
||||||
return slots != null && cryptParameters != null;
|
return !_slots.isEmpty() && _cryptParameters != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void save(Context context) throws IOException {
|
public void save(Context context) throws IOException {
|
||||||
byte[] data = serialize();
|
byte[] data = serialize();
|
||||||
|
|
||||||
FileOutputStream file = context.openFileOutput(dbFilename, Context.MODE_PRIVATE);
|
FileOutputStream file = context.openFileOutput(FILENAME, Context.MODE_PRIVATE);
|
||||||
file.write(data);
|
file.write(data);
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DatabaseFile load(Context context) throws Exception {
|
public static DatabaseFile load(Context context) throws Exception {
|
||||||
FileInputStream file = context.openFileInput(dbFilename);
|
FileInputStream file = context.openFileInput(FILENAME);
|
||||||
byte[] data = new byte[(int) file.getChannel().size()];
|
byte[] data = new byte[(int) file.getChannel().size()];
|
||||||
file.read(data);
|
file.read(data);
|
||||||
file.close();
|
file.close();
|
||||||
|
@ -169,27 +169,27 @@ public class DatabaseFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getContent() {
|
public byte[] getContent() {
|
||||||
return content;
|
return _content;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setContent(byte[] content) {
|
public void setContent(byte[] content) {
|
||||||
this.content = content;
|
_content = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CryptParameters getCryptParameters() {
|
public CryptParameters getCryptParameters() {
|
||||||
return cryptParameters;
|
return _cryptParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCryptParameters(CryptParameters parameters) {
|
public void setCryptParameters(CryptParameters parameters) {
|
||||||
this.cryptParameters = parameters;
|
_cryptParameters = parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SlotCollection getSlots() {
|
public SlotCollection getSlots() {
|
||||||
return slots;
|
return _slots;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSlots(SlotCollection slots) {
|
public void setSlots(SlotCollection slots) {
|
||||||
this.slots = slots;
|
_slots = slots;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class section {
|
private static class section {
|
||||||
|
|
|
@ -51,22 +51,17 @@ public class DatabaseManager {
|
||||||
_file.save(_context);
|
_file.save(_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addKey(KeyProfile profile) throws Exception {
|
public void addKey(DatabaseEntry entry) throws Exception {
|
||||||
assertDecrypted();
|
assertDecrypted();
|
||||||
_db.addKey(profile);
|
_db.addKey(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateKey(KeyProfile profile) throws Exception {
|
public void removeKey(DatabaseEntry entry) throws Exception {
|
||||||
assertDecrypted();
|
assertDecrypted();
|
||||||
_db.updateKey(profile);
|
_db.removeKey(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeKey(KeyProfile profile) throws Exception {
|
public List<DatabaseEntry> getKeys() throws Exception {
|
||||||
assertDecrypted();
|
|
||||||
_db.removeKey(profile);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<KeyProfile> getKeys() throws Exception {
|
|
||||||
assertDecrypted();
|
assertDecrypted();
|
||||||
return _db.getKeys();
|
return _db.getKeys();
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,8 @@ import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import me.impy.aegis.KeyProfile;
|
|
||||||
import me.impy.aegis.crypto.KeyInfo;
|
import me.impy.aegis.crypto.KeyInfo;
|
||||||
|
import me.impy.aegis.db.DatabaseEntry;
|
||||||
|
|
||||||
public class FreeOTPImporter extends KeyConverter {
|
public class FreeOTPImporter extends KeyConverter {
|
||||||
public FreeOTPImporter(InputStream stream) {
|
public FreeOTPImporter(InputStream stream) {
|
||||||
|
@ -27,9 +27,7 @@ public class FreeOTPImporter extends KeyConverter {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<KeyProfile> convert() throws Exception {
|
public List<DatabaseEntry> convert() throws Exception {
|
||||||
List<KeyProfile> keys = new ArrayList<>();
|
|
||||||
|
|
||||||
XmlPullParser parser = Xml.newPullParser();
|
XmlPullParser parser = Xml.newPullParser();
|
||||||
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
|
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
|
||||||
parser.setInput(_stream, null);
|
parser.setInput(_stream, null);
|
||||||
|
@ -37,7 +35,7 @@ public class FreeOTPImporter extends KeyConverter {
|
||||||
return parse(parser);
|
return parse(parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<KeyProfile> parse(XmlPullParser parser) throws IOException, XmlPullParserException, JSONException {
|
private static List<DatabaseEntry> parse(XmlPullParser parser) throws IOException, XmlPullParserException, JSONException {
|
||||||
List<Entry> entries = new ArrayList<>();
|
List<Entry> entries = new ArrayList<>();
|
||||||
|
|
||||||
parser.require(XmlPullParser.START_TAG, null, "map");
|
parser.require(XmlPullParser.START_TAG, null, "map");
|
||||||
|
@ -54,7 +52,7 @@ public class FreeOTPImporter extends KeyConverter {
|
||||||
entries.add(parseEntry(parser));
|
entries.add(parseEntry(parser));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<KeyProfile> profiles = new ArrayList<>();
|
List<DatabaseEntry> profiles = new ArrayList<>();
|
||||||
|
|
||||||
for (Entry entry : entries) {
|
for (Entry entry : entries) {
|
||||||
if (entry.Name.equals("tokenOrder")) {
|
if (entry.Name.equals("tokenOrder")) {
|
||||||
|
@ -74,8 +72,8 @@ public class FreeOTPImporter extends KeyConverter {
|
||||||
byte[] secret = toBytes(obj.getJSONArray("secret"));
|
byte[] secret = toBytes(obj.getJSONArray("secret"));
|
||||||
key.setSecret(secret);
|
key.setSecret(secret);
|
||||||
|
|
||||||
KeyProfile profile = new KeyProfile();
|
DatabaseEntry profile = new DatabaseEntry(null);
|
||||||
profile.Info = key;
|
profile.setInfo(key);
|
||||||
profiles.add(profile);
|
profiles.add(profile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,7 +90,6 @@ public class FreeOTPImporter extends KeyConverter {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Entry parseEntry(XmlPullParser parser) throws IOException, XmlPullParserException {
|
private static Entry parseEntry(XmlPullParser parser) throws IOException, XmlPullParserException {
|
||||||
KeyProfile profile = new KeyProfile();
|
|
||||||
parser.require(XmlPullParser.START_TAG, null, "string");
|
parser.require(XmlPullParser.START_TAG, null, "string");
|
||||||
String name = parser.getAttributeValue(null, "name");
|
String name = parser.getAttributeValue(null, "name");
|
||||||
String value = parseText(parser);
|
String value = parseText(parser);
|
||||||
|
|
|
@ -3,7 +3,7 @@ package me.impy.aegis.ext;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import me.impy.aegis.KeyProfile;
|
import me.impy.aegis.db.DatabaseEntry;
|
||||||
|
|
||||||
public abstract class KeyConverter {
|
public abstract class KeyConverter {
|
||||||
protected InputStream _stream;
|
protected InputStream _stream;
|
||||||
|
@ -12,5 +12,5 @@ public abstract class KeyConverter {
|
||||||
_stream = stream;
|
_stream = stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract List<KeyProfile> convert() throws Exception;
|
public abstract List<DatabaseEntry> convert() throws Exception;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,10 @@ import android.support.v7.widget.helper.ItemTouchHelper;
|
||||||
|
|
||||||
public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
|
public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
|
||||||
|
|
||||||
private final ItemTouchHelperAdapter mAdapter;
|
private final ItemTouchHelperAdapter _adapter;
|
||||||
|
|
||||||
public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter) {
|
public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter) {
|
||||||
mAdapter = adapter;
|
_adapter = adapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -31,13 +31,13 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
|
||||||
@Override
|
@Override
|
||||||
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
|
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
|
||||||
RecyclerView.ViewHolder target) {
|
RecyclerView.ViewHolder target) {
|
||||||
mAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
|
_adapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
|
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
|
||||||
mAdapter.onItemDismiss(viewHolder.getAdapterPosition());
|
_adapter.onItemDismiss(viewHolder.getAdapterPosition());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -7,34 +7,25 @@ import me.impy.aegis.crypto.otp.HOTP;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class HOTPTest {
|
public class HOTPTest {
|
||||||
private class testVector {
|
|
||||||
public long Count;
|
|
||||||
public String OTP;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://tools.ietf.org/html/rfc4226#page-32
|
// https://tools.ietf.org/html/rfc4226#page-32
|
||||||
private final testVector[] _vectors = {
|
private final String[] _vectors = {
|
||||||
new testVector(){{ Count = 0; OTP = "755224"; }},
|
"755224", "287082",
|
||||||
new testVector(){{ Count = 1; OTP = "287082"; }},
|
"359152", "969429",
|
||||||
new testVector(){{ Count = 2; OTP = "359152"; }},
|
"338314", "254676",
|
||||||
new testVector(){{ Count = 3; OTP = "969429"; }},
|
"287922", "162583",
|
||||||
new testVector(){{ Count = 4; OTP = "338314"; }},
|
"399871", "520489"
|
||||||
new testVector(){{ Count = 5; OTP = "254676"; }},
|
|
||||||
new testVector(){{ Count = 6; OTP = "287922"; }},
|
|
||||||
new testVector(){{ Count = 7; OTP = "162583"; }},
|
|
||||||
new testVector(){{ Count = 8; OTP = "399871"; }},
|
|
||||||
new testVector(){{ Count = 9; OTP = "520489"; }},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private final byte[] _secret = new byte[] { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
|
private final byte[] _secret = new byte[] {
|
||||||
0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30
|
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30,
|
||||||
|
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30
|
||||||
};
|
};
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void vectors_match() throws Exception {
|
public void vectorsMatch() throws Exception {
|
||||||
for (testVector v : _vectors) {
|
for (int i = 0; i < _vectors.length; i++) {
|
||||||
String otp = HOTP.generateOTP(_secret, v.Count, 6, false, -1);
|
String otp = HOTP.generateOTP(_secret, i, 6, false, -1);
|
||||||
assertEquals(v.OTP, otp);
|
assertEquals(_vectors[i], otp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,42 +15,47 @@ public class TOTPTest {
|
||||||
|
|
||||||
// https://tools.ietf.org/html/rfc6238#appendix-B
|
// https://tools.ietf.org/html/rfc6238#appendix-B
|
||||||
private final testVector[] _vectors = {
|
private final testVector[] _vectors = {
|
||||||
new testVector(){{ Time = "0000000000000001"; OTP = "94287082"; Mode = "HmacSHA1"; }},
|
new testVector(){{ Time = "0000000000000001"; OTP = "94287082"; Mode = "HmacSHA1"; }},
|
||||||
new testVector(){{ Time = "0000000000000001"; OTP = "46119246"; Mode = "HmacSHA256"; }},
|
new testVector(){{ Time = "0000000000000001"; OTP = "46119246"; Mode = "HmacSHA256"; }},
|
||||||
new testVector(){{ Time = "0000000000000001"; OTP = "90693936"; Mode = "HmacSHA512"; }},
|
new testVector(){{ Time = "0000000000000001"; OTP = "90693936"; Mode = "HmacSHA512"; }},
|
||||||
new testVector(){{ Time = "00000000023523EC"; OTP = "07081804"; Mode = "HmacSHA1"; }},
|
new testVector(){{ Time = "00000000023523EC"; OTP = "07081804"; Mode = "HmacSHA1"; }},
|
||||||
new testVector(){{ Time = "00000000023523EC"; OTP = "68084774"; Mode = "HmacSHA256"; }},
|
new testVector(){{ Time = "00000000023523EC"; OTP = "68084774"; Mode = "HmacSHA256"; }},
|
||||||
new testVector(){{ Time = "00000000023523EC"; OTP = "25091201"; Mode = "HmacSHA512"; }},
|
new testVector(){{ Time = "00000000023523EC"; OTP = "25091201"; Mode = "HmacSHA512"; }},
|
||||||
new testVector(){{ Time = "00000000023523ED"; OTP = "14050471"; Mode = "HmacSHA1"; }},
|
new testVector(){{ Time = "00000000023523ED"; OTP = "14050471"; Mode = "HmacSHA1"; }},
|
||||||
new testVector(){{ Time = "00000000023523ED"; OTP = "67062674"; Mode = "HmacSHA256"; }},
|
new testVector(){{ Time = "00000000023523ED"; OTP = "67062674"; Mode = "HmacSHA256"; }},
|
||||||
new testVector(){{ Time = "00000000023523ED"; OTP = "99943326"; Mode = "HmacSHA512"; }},
|
new testVector(){{ Time = "00000000023523ED"; OTP = "99943326"; Mode = "HmacSHA512"; }},
|
||||||
new testVector(){{ Time = "000000000273EF07"; OTP = "89005924"; Mode = "HmacSHA1"; }},
|
new testVector(){{ Time = "000000000273EF07"; OTP = "89005924"; Mode = "HmacSHA1"; }},
|
||||||
new testVector(){{ Time = "000000000273EF07"; OTP = "91819424"; Mode = "HmacSHA256"; }},
|
new testVector(){{ Time = "000000000273EF07"; OTP = "91819424"; Mode = "HmacSHA256"; }},
|
||||||
new testVector(){{ Time = "000000000273EF07"; OTP = "93441116"; Mode = "HmacSHA512"; }},
|
new testVector(){{ Time = "000000000273EF07"; OTP = "93441116"; Mode = "HmacSHA512"; }},
|
||||||
new testVector(){{ Time = "0000000003F940AA"; OTP = "69279037"; Mode = "HmacSHA1"; }},
|
new testVector(){{ Time = "0000000003F940AA"; OTP = "69279037"; Mode = "HmacSHA1"; }},
|
||||||
new testVector(){{ Time = "0000000003F940AA"; OTP = "90698825"; Mode = "HmacSHA256"; }},
|
new testVector(){{ Time = "0000000003F940AA"; OTP = "90698825"; Mode = "HmacSHA256"; }},
|
||||||
new testVector(){{ Time = "0000000003F940AA"; OTP = "38618901"; Mode = "HmacSHA512"; }},
|
new testVector(){{ Time = "0000000003F940AA"; OTP = "38618901"; Mode = "HmacSHA512"; }},
|
||||||
new testVector(){{ Time = "0000000027BC86AA"; OTP = "65353130"; Mode = "HmacSHA1"; }},
|
new testVector(){{ Time = "0000000027BC86AA"; OTP = "65353130"; Mode = "HmacSHA1"; }},
|
||||||
new testVector(){{ Time = "0000000027BC86AA"; OTP = "77737706"; Mode = "HmacSHA256"; }},
|
new testVector(){{ Time = "0000000027BC86AA"; OTP = "77737706"; Mode = "HmacSHA256"; }},
|
||||||
new testVector(){{ Time = "0000000027BC86AA"; OTP = "47863826"; Mode = "HmacSHA512"; }}
|
new testVector(){{ Time = "0000000027BC86AA"; OTP = "47863826"; Mode = "HmacSHA512"; }}
|
||||||
};
|
};
|
||||||
|
|
||||||
private final byte[] _seed = new byte[] { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
|
private final byte[] _seed = new byte[] {
|
||||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30
|
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30,
|
||||||
|
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30
|
||||||
};
|
};
|
||||||
private final byte[] _seed32 = new byte[] { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
|
|
||||||
0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32,
|
private final byte[] _seed32 = new byte[] {
|
||||||
0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32
|
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
|
||||||
|
0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
|
||||||
|
0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34,
|
||||||
|
0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32
|
||||||
};
|
};
|
||||||
private final byte[] _seed64 = new byte[] { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
|
|
||||||
0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32,
|
private final byte[] _seed64 = new byte[] {
|
||||||
0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
|
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
|
||||||
0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30,
|
0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32,
|
||||||
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34
|
0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
|
||||||
|
0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34
|
||||||
};
|
};
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void vectors_match() throws Exception {
|
public void vectorsMatch() throws Exception {
|
||||||
for (testVector v : _vectors) {
|
for (testVector v : _vectors) {
|
||||||
byte[] seed;
|
byte[] seed;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue