diff --git a/app/src/main/java/me/impy/aegis/AddProfileActivity.java b/app/src/main/java/me/impy/aegis/AddProfileActivity.java index e5c1ce28..0806c3c3 100644 --- a/app/src/main/java/me/impy/aegis/AddProfileActivity.java +++ b/app/src/main/java/me/impy/aegis/AddProfileActivity.java @@ -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,16 +90,12 @@ public class AddProfileActivity extends AppCompatActivity { } } - - private void setPreferredTheme() - { + private void setPreferredTheme() { SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - if(sharedPreferences.getBoolean("pref_night_mode", false)) - { - setTheme(R.style.AppTheme_Dark_TransparentActionBar); - } else - { - setTheme(R.style.AppTheme_Default_TransparentActionBar); + if (sharedPreferences.getBoolean("pref_night_mode", false)) { + setTheme(R.style.AppTheme_Dark_TransparentActionBar); + } else { + setTheme(R.style.AppTheme_Default_TransparentActionBar); } } } diff --git a/app/src/main/java/me/impy/aegis/AuthActivity.java b/app/src/main/java/me/impy/aegis/AuthActivity.java index df77325d..da629e28 100644 --- a/app/src/main/java/me/impy/aegis/AuthActivity.java +++ b/app/src/main/java/me/impy/aegis/AuthActivity.java @@ -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 void trySlots(Class 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(); } } diff --git a/app/src/main/java/me/impy/aegis/CustomAuthenticatedSlide.java b/app/src/main/java/me/impy/aegis/CustomAuthenticatedSlide.java index f22f06f6..f50b40d6 100644 --- a/app/src/main/java/me/impy/aegis/CustomAuthenticatedSlide.java +++ b/app/src/main/java/me/impy/aegis/CustomAuthenticatedSlide.java @@ -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 diff --git a/app/src/main/java/me/impy/aegis/CustomAuthenticationSlide.java b/app/src/main/java/me/impy/aegis/CustomAuthenticationSlide.java index 3e59ebe0..1a8ef83e 100644 --- a/app/src/main/java/me/impy/aegis/CustomAuthenticationSlide.java +++ b/app/src/main/java/me/impy/aegis/CustomAuthenticationSlide.java @@ -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; - }*/ } diff --git a/app/src/main/java/me/impy/aegis/EditProfileBottomSheetdialog.java b/app/src/main/java/me/impy/aegis/EditProfileBottomSheetdialog.java index 2599d0ef..e9556799 100644 --- a/app/src/main/java/me/impy/aegis/EditProfileBottomSheetdialog.java +++ b/app/src/main/java/me/impy/aegis/EditProfileBottomSheetdialog.java @@ -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 diff --git a/app/src/main/java/me/impy/aegis/IntroActivity.java b/app/src/main/java/me/impy/aegis/IntroActivity.java index 269d87db..d4236836 100644 --- a/app/src/main/java/me/impy/aegis/IntroActivity.java +++ b/app/src/main/java/me/impy/aegis/IntroActivity.java @@ -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) { diff --git a/app/src/main/java/me/impy/aegis/KeyProfile.java b/app/src/main/java/me/impy/aegis/KeyProfile.java index 82b0aff6..a95972eb 100644 --- a/app/src/main/java/me/impy/aegis/KeyProfile.java +++ b/app/src/main/java/me/impy/aegis/KeyProfile.java @@ -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; } } diff --git a/app/src/main/java/me/impy/aegis/KeyProfileAdapter.java b/app/src/main/java/me/impy/aegis/KeyProfileAdapter.java index 7dde1f6a..9d4cebfa 100644 --- a/app/src/main/java/me/impy/aegis/KeyProfileAdapter.java +++ b/app/src/main/java/me/impy/aegis/KeyProfileAdapter.java @@ -26,34 +26,34 @@ import me.impy.aegis.crypto.otp.OTP; import me.impy.aegis.helpers.ItemTouchHelperAdapter; public class KeyProfileAdapter extends RecyclerView.Adapter implements ItemTouchHelperAdapter { - private final List lstHolders; - private ArrayList mKeyProfiles; - private Handler uiHandler; - private static ItemClickListener itemClickListener; - private static LongItemClickListener longItemClickListener; + private final List _holders; + private ArrayList _keyProfiles; + private Handler _uiHandler; + private static ItemClickListener _itemClickListener; + private static LongItemClickListener _longItemClickListener; public KeyProfileAdapter(ArrayList 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 comparator = new Comparator() { @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 mKeyProfiles = new ArrayList<>(); - private DatabaseManager db; + private KeyProfileAdapter _keyProfileAdapter; + private ArrayList _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 comparator = new Comparator() { @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); } @@ -384,7 +380,7 @@ public class MainActivity extends AppCompatActivity { ShortcutManager shortcutManager = null; if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { shortcutManager = getSystemService(ShortcutManager.class); - if(shortcutManager != null) { + if (shortcutManager != null) { //TODO: Remove this line shortcutManager.removeAllDynamicShortcuts(); if (shortcutManager.getDynamicShortcuts().size() == 0) { @@ -394,48 +390,47 @@ public class MainActivity extends AppCompatActivity { intent1.putExtra("Action", "Scan"); intent1.setAction(Intent.ACTION_MAIN); - ShortcutInfo shortcut = new ShortcutInfo.Builder(this, "id1") - .setShortLabel("New profile") - .setLongLabel("Add new profile") - .setIcon(Icon.createWithResource(this.getApplicationContext(), R.drawable.intro_scanner)) - .setIntent(intent1) - .build(); + ShortcutInfo shortcut = new ShortcutInfo.Builder(this, "id1") + .setShortLabel("New profile") + .setLongLabel("Add new profile") + .setIcon(Icon.createWithResource(this.getApplicationContext(), R.drawable.intro_scanner)) + .setIntent(intent1) + .build(); - shortcutManager.setDynamicShortcuts(Arrays.asList(shortcut)); + shortcutManager.setDynamicShortcuts(Arrays.asList(shortcut)); } } } } - private void setPreferredTheme() - { + private void setPreferredTheme() { boolean restart = false; SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - if(sharedPreferences.getBoolean("pref_night_mode", false)) { - if(!nightMode) { + if (sharedPreferences.getBoolean("pref_night_mode", false)) { + if (!_nightMode) { setTheme(R.style.AppTheme_Dark_NoActionBar); restart = true; } } else { - if(nightMode) { + if (_nightMode) { setTheme(R.style.AppTheme_Default_NoActionBar); restart = true; } } - if(restart){ + if (restart) { finish(); startActivity(new Intent(this, this.getClass())); } } 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()); + } + } } diff --git a/app/src/main/java/me/impy/aegis/PreferencesActivity.java b/app/src/main/java/me/impy/aegis/PreferencesActivity.java index 8ee541cc..071bd72d 100644 --- a/app/src/main/java/me/impy/aegis/PreferencesActivity.java +++ b/app/src/main/java/me/impy/aegis/PreferencesActivity.java @@ -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"); diff --git a/app/src/main/java/me/impy/aegis/ScannerActivity.java b/app/src/main/java/me/impy/aegis/ScannerActivity.java index 622f8b07..5cea9d6f 100644 --- a/app/src/main/java/me/impy/aegis/ScannerActivity.java +++ b/app/src/main/java/me/impy/aegis/ScannerActivity.java @@ -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 getSupportedFormats() { - ArrayList supportedFormats = new ArrayList<>(); - supportedFormats.add(BarcodeFormat.QR_CODE); - - return supportedFormats; - } } diff --git a/app/src/main/java/me/impy/aegis/crypto/CryptoUtils.java b/app/src/main/java/me/impy/aegis/crypto/CryptoUtils.java index 697f6af3..629bf178 100644 --- a/app/src/main/java/me/impy/aegis/crypto/CryptoUtils.java +++ b/app/src/main/java/me/impy/aegis/crypto/CryptoUtils.java @@ -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; diff --git a/app/src/main/java/me/impy/aegis/crypto/KeyInfo.java b/app/src/main/java/me/impy/aegis/crypto/KeyInfo.java index a6b0b637..bac02c8c 100644 --- a/app/src/main/java/me/impy/aegis/crypto/KeyInfo.java +++ b/app/src/main/java/me/impy/aegis/crypto/KeyInfo.java @@ -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; } } diff --git a/app/src/main/java/me/impy/aegis/crypto/KeyStoreHandle.java b/app/src/main/java/me/impy/aegis/crypto/KeyStoreHandle.java index 4bf64156..91c5ae95 100644 --- a/app/src/main/java/me/impy/aegis/crypto/KeyStoreHandle.java +++ b/app/src/main/java/me/impy/aegis/crypto/KeyStoreHandle.java @@ -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); } } diff --git a/app/src/main/java/me/impy/aegis/crypto/slots/SlotCollection.java b/app/src/main/java/me/impy/aegis/crypto/slots/SlotCollection.java index 26a75cb4..9ff26ed9 100644 --- a/app/src/main/java/me/impy/aegis/crypto/slots/SlotCollection.java +++ b/app/src/main/java/me/impy/aegis/crypto/slots/SlotCollection.java @@ -82,6 +82,10 @@ public class SlotCollection implements Iterable, Serializable { return _slots.size(); } + public boolean isEmpty() { + return _slots.size() == 0; + } + public T find(Class type) { for (Slot slot : this) { if (slot.getClass() == type) { diff --git a/app/src/main/java/me/impy/aegis/db/Database.java b/app/src/main/java/me/impy/aegis/db/Database.java index 046f0806..a54a4d74 100644 --- a/app/src/main/java/me/impy/aegis/db/Database.java +++ b/app/src/main/java/me/impy/aegis/db/Database.java @@ -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 entries = new ArrayList<>(); + private static final int VERSION = 1; + private List _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 getKeys() throws Exception { - List 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 getKeys() throws Exception { + return _entries; } } diff --git a/app/src/main/java/me/impy/aegis/db/DatabaseEntry.java b/app/src/main/java/me/impy/aegis/db/DatabaseEntry.java index 19d1616d..300eb00e 100644 --- a/app/src/main/java/me/impy/aegis/db/DatabaseEntry.java +++ b/app/src/main/java/me/impy/aegis/db/DatabaseEntry.java @@ -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; } } diff --git a/app/src/main/java/me/impy/aegis/db/DatabaseFile.java b/app/src/main/java/me/impy/aegis/db/DatabaseFile.java index 8c974dcf..a127c5f5 100644 --- a/app/src/main/java/me/impy/aegis/db/DatabaseFile.java +++ b/app/src/main/java/me/impy/aegis/db/DatabaseFile.java @@ -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 { diff --git a/app/src/main/java/me/impy/aegis/db/DatabaseManager.java b/app/src/main/java/me/impy/aegis/db/DatabaseManager.java index 6f9dca22..4dc31bb3 100644 --- a/app/src/main/java/me/impy/aegis/db/DatabaseManager.java +++ b/app/src/main/java/me/impy/aegis/db/DatabaseManager.java @@ -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 getKeys() throws Exception { + public List getKeys() throws Exception { assertDecrypted(); return _db.getKeys(); } diff --git a/app/src/main/java/me/impy/aegis/ext/FreeOTPImporter.java b/app/src/main/java/me/impy/aegis/ext/FreeOTPImporter.java index 9b7d3676..bcad669c 100644 --- a/app/src/main/java/me/impy/aegis/ext/FreeOTPImporter.java +++ b/app/src/main/java/me/impy/aegis/ext/FreeOTPImporter.java @@ -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 convert() throws Exception { - List keys = new ArrayList<>(); - + public List 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 parse(XmlPullParser parser) throws IOException, XmlPullParserException, JSONException { + private static List parse(XmlPullParser parser) throws IOException, XmlPullParserException, JSONException { List entries = new ArrayList<>(); parser.require(XmlPullParser.START_TAG, null, "map"); @@ -54,7 +52,7 @@ public class FreeOTPImporter extends KeyConverter { entries.add(parseEntry(parser)); } - List profiles = new ArrayList<>(); + List 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); diff --git a/app/src/main/java/me/impy/aegis/ext/KeyConverter.java b/app/src/main/java/me/impy/aegis/ext/KeyConverter.java index 2366c9f3..d2d603db 100644 --- a/app/src/main/java/me/impy/aegis/ext/KeyConverter.java +++ b/app/src/main/java/me/impy/aegis/ext/KeyConverter.java @@ -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 convert() throws Exception; + public abstract List convert() throws Exception; } diff --git a/app/src/main/java/me/impy/aegis/helpers/SimpleItemTouchHelperCallback.java b/app/src/main/java/me/impy/aegis/helpers/SimpleItemTouchHelperCallback.java index 551c8049..0378f07c 100644 --- a/app/src/main/java/me/impy/aegis/helpers/SimpleItemTouchHelperCallback.java +++ b/app/src/main/java/me/impy/aegis/helpers/SimpleItemTouchHelperCallback.java @@ -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()); } } \ No newline at end of file diff --git a/app/src/test/java/me/impy/aegis/HOTPTest.java b/app/src/test/java/me/impy/aegis/HOTPTest.java index 51435c3a..e9c233ed 100644 --- a/app/src/test/java/me/impy/aegis/HOTPTest.java +++ b/app/src/test/java/me/impy/aegis/HOTPTest.java @@ -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); } } } diff --git a/app/src/test/java/me/impy/aegis/TOTPTest.java b/app/src/test/java/me/impy/aegis/TOTPTest.java index 10def313..cb2ee73a 100644 --- a/app/src/test/java/me/impy/aegis/TOTPTest.java +++ b/app/src/test/java/me/impy/aegis/TOTPTest.java @@ -15,42 +15,47 @@ public class TOTPTest { // https://tools.ietf.org/html/rfc6238#appendix-B private final testVector[] _vectors = { - new testVector(){{ Time = "0000000000000001"; OTP = "94287082"; Mode = "HmacSHA1"; }}, - new testVector(){{ Time = "0000000000000001"; OTP = "46119246"; Mode = "HmacSHA256"; }}, - new testVector(){{ Time = "0000000000000001"; OTP = "90693936"; Mode = "HmacSHA512"; }}, - new testVector(){{ Time = "00000000023523EC"; OTP = "07081804"; Mode = "HmacSHA1"; }}, - new testVector(){{ Time = "00000000023523EC"; OTP = "68084774"; Mode = "HmacSHA256"; }}, - new testVector(){{ Time = "00000000023523EC"; OTP = "25091201"; Mode = "HmacSHA512"; }}, - new testVector(){{ Time = "00000000023523ED"; OTP = "14050471"; Mode = "HmacSHA1"; }}, - new testVector(){{ Time = "00000000023523ED"; OTP = "67062674"; Mode = "HmacSHA256"; }}, - new testVector(){{ Time = "00000000023523ED"; OTP = "99943326"; Mode = "HmacSHA512"; }}, - new testVector(){{ Time = "000000000273EF07"; OTP = "89005924"; Mode = "HmacSHA1"; }}, - new testVector(){{ Time = "000000000273EF07"; OTP = "91819424"; Mode = "HmacSHA256"; }}, - new testVector(){{ Time = "000000000273EF07"; OTP = "93441116"; Mode = "HmacSHA512"; }}, - new testVector(){{ Time = "0000000003F940AA"; OTP = "69279037"; Mode = "HmacSHA1"; }}, - new testVector(){{ Time = "0000000003F940AA"; OTP = "90698825"; Mode = "HmacSHA256"; }}, - new testVector(){{ Time = "0000000003F940AA"; OTP = "38618901"; Mode = "HmacSHA512"; }}, - new testVector(){{ Time = "0000000027BC86AA"; OTP = "65353130"; Mode = "HmacSHA1"; }}, - new testVector(){{ Time = "0000000027BC86AA"; OTP = "77737706"; Mode = "HmacSHA256"; }}, - new testVector(){{ Time = "0000000027BC86AA"; OTP = "47863826"; Mode = "HmacSHA512"; }} + new testVector(){{ Time = "0000000000000001"; OTP = "94287082"; Mode = "HmacSHA1"; }}, + new testVector(){{ Time = "0000000000000001"; OTP = "46119246"; Mode = "HmacSHA256"; }}, + new testVector(){{ Time = "0000000000000001"; OTP = "90693936"; Mode = "HmacSHA512"; }}, + new testVector(){{ Time = "00000000023523EC"; OTP = "07081804"; Mode = "HmacSHA1"; }}, + new testVector(){{ Time = "00000000023523EC"; OTP = "68084774"; Mode = "HmacSHA256"; }}, + new testVector(){{ Time = "00000000023523EC"; OTP = "25091201"; Mode = "HmacSHA512"; }}, + new testVector(){{ Time = "00000000023523ED"; OTP = "14050471"; Mode = "HmacSHA1"; }}, + new testVector(){{ Time = "00000000023523ED"; OTP = "67062674"; Mode = "HmacSHA256"; }}, + new testVector(){{ Time = "00000000023523ED"; OTP = "99943326"; Mode = "HmacSHA512"; }}, + new testVector(){{ Time = "000000000273EF07"; OTP = "89005924"; Mode = "HmacSHA1"; }}, + new testVector(){{ Time = "000000000273EF07"; OTP = "91819424"; Mode = "HmacSHA256"; }}, + new testVector(){{ Time = "000000000273EF07"; OTP = "93441116"; Mode = "HmacSHA512"; }}, + new testVector(){{ Time = "0000000003F940AA"; OTP = "69279037"; Mode = "HmacSHA1"; }}, + new testVector(){{ Time = "0000000003F940AA"; OTP = "90698825"; Mode = "HmacSHA256"; }}, + new testVector(){{ Time = "0000000003F940AA"; OTP = "38618901"; Mode = "HmacSHA512"; }}, + new testVector(){{ Time = "0000000027BC86AA"; OTP = "65353130"; Mode = "HmacSHA1"; }}, + new testVector(){{ Time = "0000000027BC86AA"; OTP = "77737706"; Mode = "HmacSHA256"; }}, + 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;