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