- Added Pocket Security Functionality (still in progress...)
This commit is contained in:
parent
5b21c0719e
commit
6117fc0f99
14 changed files with 429 additions and 6 deletions
|
@ -1,4 +1,5 @@
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 27
|
compileSdkVersion 27
|
||||||
|
@ -79,4 +80,6 @@ dependencies {
|
||||||
implementation 'id.zelory:compressor:2.1.0'
|
implementation 'id.zelory:compressor:2.1.0'
|
||||||
implementation 'com.vincent.filepicker:MultiTypeFilePicker:1.0.7'
|
implementation 'com.vincent.filepicker:MultiTypeFilePicker:1.0.7'
|
||||||
implementation 'com.andrognito.patternlockview:patternlockview:1.0.0'
|
implementation 'com.andrognito.patternlockview:patternlockview:1.0.0'
|
||||||
|
implementation 'commons-codec:commons-codec:1.11'
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.NFC" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".application.CrystalApplication"
|
android:name=".application.CrystalApplication"
|
||||||
|
@ -52,6 +53,21 @@
|
||||||
<activity android:name=".activities.PatternRequestActivity"
|
<activity android:name=".activities.PatternRequestActivity"
|
||||||
android:noHistory="true">
|
android:noHistory="true">
|
||||||
</activity>
|
</activity>
|
||||||
|
<activity android:name=".activities.PocketRequestActivity"
|
||||||
|
android:noHistory="true">
|
||||||
|
<!--<meta-data
|
||||||
|
android:name="android.nfc.action.TECH_DISCOVERED"
|
||||||
|
android:resource="@xml/tech" />-->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<data
|
||||||
|
android:scheme="https"
|
||||||
|
android:host="my.yubico.com"
|
||||||
|
android:pathPrefix="/neo"/>
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
</activity>
|
||||||
<activity android:name=".activities.CryptoNetAccountSettingsActivity"
|
<activity android:name=".activities.CryptoNetAccountSettingsActivity"
|
||||||
android:theme="@style/AppTheme.NoActionBar"
|
android:theme="@style/AppTheme.NoActionBar"
|
||||||
android:windowSoftInputMode="adjustPan">
|
android:windowSoftInputMode="adjustPan">
|
||||||
|
@ -60,6 +76,14 @@
|
||||||
android:name=".activities.SettingsActivity"
|
android:name=".activities.SettingsActivity"
|
||||||
android:theme="@style/AppTheme.NoActionBar"
|
android:theme="@style/AppTheme.NoActionBar"
|
||||||
android:windowSoftInputMode="adjustPan">
|
android:windowSoftInputMode="adjustPan">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<data
|
||||||
|
android:scheme="https"
|
||||||
|
android:host="my.yubico.com"
|
||||||
|
android:pathPrefix="/neo"/>
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.AccountsActivity"
|
android:name=".activities.AccountsActivity"
|
||||||
|
|
|
@ -95,6 +95,7 @@ public class IntroActivity extends AppCompatActivity {
|
||||||
} else {
|
} else {
|
||||||
//Intent intent = new Intent(this, CreateSeedActivity.class);
|
//Intent intent = new Intent(this, CreateSeedActivity.class);
|
||||||
Intent intent = new Intent(this, BoardActivity.class);
|
Intent intent = new Intent(this, BoardActivity.class);
|
||||||
|
//Intent intent = new Intent(this, PocketRequestActivity.class);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
package cy.agorise.crystalwallet.activities;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.arch.lifecycle.LiveData;
|
||||||
|
import android.arch.lifecycle.Observer;
|
||||||
|
import android.arch.lifecycle.ViewModelProviders;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.nfc.NfcAdapter;
|
||||||
|
import android.nfc.Tag;
|
||||||
|
import android.nfc.tech.IsoDep;
|
||||||
|
import android.nfc.tech.MifareClassic;
|
||||||
|
import android.nfc.tech.NdefFormatable;
|
||||||
|
import android.nfc.tech.NfcA;
|
||||||
|
import android.nfc.tech.NfcF;
|
||||||
|
import android.nfc.tech.NfcV;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.andrognito.patternlockview.PatternLockView;
|
||||||
|
import com.andrognito.patternlockview.listener.PatternLockViewListener;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base32;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import butterknife.BindView;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
import cy.agorise.crystalwallet.R;
|
||||||
|
import cy.agorise.crystalwallet.models.GeneralSetting;
|
||||||
|
import cy.agorise.crystalwallet.util.PasswordManager;
|
||||||
|
import cy.agorise.crystalwallet.util.yubikey.Algorithm;
|
||||||
|
import cy.agorise.crystalwallet.util.yubikey.OathType;
|
||||||
|
import cy.agorise.crystalwallet.util.yubikey.YkOathApi;
|
||||||
|
import cy.agorise.crystalwallet.viewmodels.GeneralSettingListViewModel;
|
||||||
|
|
||||||
|
public class PocketRequestActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private NfcAdapter mNfcAdapter;
|
||||||
|
private PendingIntent pendingIntent;
|
||||||
|
private IntentFilter ndef;
|
||||||
|
IntentFilter[] intentFiltersArray;
|
||||||
|
String[][] techList;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
//Do nothing to prevent the user to use the back button
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_pocket_request);
|
||||||
|
ButterKnife.bind(this);
|
||||||
|
|
||||||
|
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
|
||||||
|
|
||||||
|
this.configureForegroundDispatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void configureForegroundDispatch(){
|
||||||
|
if (mNfcAdapter != null) {
|
||||||
|
pendingIntent = PendingIntent.getActivity(
|
||||||
|
this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
|
||||||
|
|
||||||
|
ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
|
||||||
|
try {
|
||||||
|
ndef.addDataType("*/*"); /* Handles all MIME based dispatches.
|
||||||
|
You should specify only the ones that you need. */
|
||||||
|
} catch (IntentFilter.MalformedMimeTypeException e) {
|
||||||
|
throw new RuntimeException("fail", e);
|
||||||
|
}
|
||||||
|
intentFiltersArray = new IntentFilter[]{ndef,};
|
||||||
|
techList = new String[][]{ new String[] {IsoDep.class.getName(), NfcA.class.getName(), MifareClassic.class.getName(), NdefFormatable.class.getName()} };
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, "This device doesn't support NFC.", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
if (mNfcAdapter != null) {
|
||||||
|
mNfcAdapter.disableForegroundDispatch(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
if (mNfcAdapter != null) {
|
||||||
|
mNfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onNewIntent(Intent intent) {
|
||||||
|
Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
|
||||||
|
IsoDep tagIsoDep = IsoDep.get(tagFromIntent);
|
||||||
|
Log.i("Tag from nfc","New Intent");
|
||||||
|
try {
|
||||||
|
|
||||||
|
String encodedSecret = "hola";
|
||||||
|
Base32 decoder = new Base32();
|
||||||
|
|
||||||
|
if ((encodedSecret != null) && (!encodedSecret.equals("")) && decoder.isInAlphabet(encodedSecret)) {
|
||||||
|
byte[] secret = decoder.decode(encodedSecret);
|
||||||
|
YkOathApi ykOathApi = new YkOathApi();
|
||||||
|
tagIsoDep.connect();
|
||||||
|
tagIsoDep.setTimeout(15000);
|
||||||
|
|
||||||
|
//byte[] keyBytes = {0x68,0x6f,0x6c,0x61};
|
||||||
|
ykOathApi.putCode(tagIsoDep,"prueba",secret, OathType.TOTP, Algorithm.SHA256,(byte)6,0,false);
|
||||||
|
tagIsoDep.close();
|
||||||
|
|
||||||
|
Toast.makeText(this, "Credential saved!", Toast.LENGTH_LONG).show();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, "Invalid password for credential", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
Toast.makeText(this, "Tag from nfc: "+tagFromIntent, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package cy.agorise.crystalwallet.activities;
|
package cy.agorise.crystalwallet.activities;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
import android.media.MediaPlayer;
|
import android.media.MediaPlayer;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
|
@ -47,6 +48,8 @@ public class SettingsActivity extends AppCompatActivity{
|
||||||
@BindView(R.id.tvBuildVersion)
|
@BindView(R.id.tvBuildVersion)
|
||||||
public TextView tvBuildVersion;
|
public TextView tvBuildVersion;
|
||||||
|
|
||||||
|
private SecuritySettingsFragment securitySettingsFragment;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
@ -97,7 +100,8 @@ public class SettingsActivity extends AppCompatActivity{
|
||||||
case 0:
|
case 0:
|
||||||
return new GeneralSettingsFragment();
|
return new GeneralSettingsFragment();
|
||||||
case 1:
|
case 1:
|
||||||
return new SecuritySettingsFragment();
|
securitySettingsFragment = new SecuritySettingsFragment();
|
||||||
|
return securitySettingsFragment;
|
||||||
case 2:
|
case 2:
|
||||||
return new BackupsSettingsFragment();
|
return new BackupsSettingsFragment();
|
||||||
//case 3:
|
//case 3:
|
||||||
|
@ -123,4 +127,12 @@ public class SettingsActivity extends AppCompatActivity{
|
||||||
public void goBack(){
|
public void goBack(){
|
||||||
onBackPressed();
|
onBackPressed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onNewIntent(Intent intent) {
|
||||||
|
super.onNewIntent(intent);
|
||||||
|
if (this.securitySettingsFragment != null){
|
||||||
|
this.securitySettingsFragment.onNewIntent(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ public class CrystalSecurityMonitor implements Application.ActivityLifecycleCall
|
||||||
private int numStarted = 0;
|
private int numStarted = 0;
|
||||||
private String passwordEncrypted;
|
private String passwordEncrypted;
|
||||||
private String patternEncrypted;
|
private String patternEncrypted;
|
||||||
|
private String yubikeyOathTotpPasswordEncrypted;
|
||||||
|
|
||||||
private static CrystalSecurityMonitor instance;
|
private static CrystalSecurityMonitor instance;
|
||||||
private GeneralSettingListViewModel generalSettingListViewModel;
|
private GeneralSettingListViewModel generalSettingListViewModel;
|
||||||
|
@ -38,8 +39,10 @@ public class CrystalSecurityMonitor implements Application.ActivityLifecycleCall
|
||||||
|
|
||||||
this.passwordEncrypted = "";
|
this.passwordEncrypted = "";
|
||||||
this.patternEncrypted = "";
|
this.patternEncrypted = "";
|
||||||
|
this.yubikeyOathTotpPasswordEncrypted = "";
|
||||||
GeneralSetting passwordGeneralSetting = generalSettingListViewModel.getGeneralSettingByName(GeneralSetting.SETTING_PASSWORD);;
|
GeneralSetting passwordGeneralSetting = generalSettingListViewModel.getGeneralSettingByName(GeneralSetting.SETTING_PASSWORD);;
|
||||||
GeneralSetting patternGeneralSetting = generalSettingListViewModel.getGeneralSettingByName(GeneralSetting.SETTING_PATTERN);;
|
GeneralSetting patternGeneralSetting = generalSettingListViewModel.getGeneralSettingByName(GeneralSetting.SETTING_PATTERN);;
|
||||||
|
GeneralSetting yubikeyOathTotpPasswordSetting = generalSettingListViewModel.getGeneralSettingByName(GeneralSetting.SETTING_YUBIKEY_OATH_TOTP_PASSWORD);;
|
||||||
|
|
||||||
if (passwordGeneralSetting != null){
|
if (passwordGeneralSetting != null){
|
||||||
this.passwordEncrypted = passwordGeneralSetting.getValue();
|
this.passwordEncrypted = passwordGeneralSetting.getValue();
|
||||||
|
@ -47,6 +50,9 @@ public class CrystalSecurityMonitor implements Application.ActivityLifecycleCall
|
||||||
if (patternGeneralSetting != null){
|
if (patternGeneralSetting != null){
|
||||||
this.patternEncrypted = patternGeneralSetting.getValue();
|
this.patternEncrypted = patternGeneralSetting.getValue();
|
||||||
}
|
}
|
||||||
|
if (yubikeyOathTotpPasswordSetting != null){
|
||||||
|
this.yubikeyOathTotpPasswordEncrypted = yubikeyOathTotpPasswordSetting.getValue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CrystalSecurityMonitor getInstance(final FragmentActivity fragmentActivity){
|
public static CrystalSecurityMonitor getInstance(final FragmentActivity fragmentActivity){
|
||||||
|
@ -57,6 +63,10 @@ public class CrystalSecurityMonitor implements Application.ActivityLifecycleCall
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getServiceName(){
|
||||||
|
return "cy.agorise.crystalwallet";
|
||||||
|
}
|
||||||
|
|
||||||
public void clearSecurity(){
|
public void clearSecurity(){
|
||||||
this.patternEncrypted = "";
|
this.patternEncrypted = "";
|
||||||
this.passwordEncrypted = "";
|
this.passwordEncrypted = "";
|
||||||
|
@ -65,6 +75,12 @@ public class CrystalSecurityMonitor implements Application.ActivityLifecycleCall
|
||||||
generalSettingListViewModel.deleteGeneralSettingByName(GeneralSetting.SETTING_PATTERN);
|
generalSettingListViewModel.deleteGeneralSettingByName(GeneralSetting.SETTING_PATTERN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void clearSecurity2ndFactor(){
|
||||||
|
this.yubikeyOathTotpPasswordEncrypted = "";
|
||||||
|
|
||||||
|
generalSettingListViewModel.deleteGeneralSettingByName(GeneralSetting.SETTING_YUBIKEY_OATH_TOTP_PASSWORD);
|
||||||
|
}
|
||||||
|
|
||||||
public void setPasswordSecurity(String password){
|
public void setPasswordSecurity(String password){
|
||||||
clearSecurity();
|
clearSecurity();
|
||||||
this.passwordEncrypted = password;
|
this.passwordEncrypted = password;
|
||||||
|
@ -95,6 +111,15 @@ public class CrystalSecurityMonitor implements Application.ActivityLifecycleCall
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setYubikeyOathTotpSecurity(String name, String password){
|
||||||
|
this.yubikeyOathTotpPasswordEncrypted = password;
|
||||||
|
GeneralSetting yubikeyOathTotpSetting = new GeneralSetting();
|
||||||
|
yubikeyOathTotpSetting.setName(GeneralSetting.SETTING_YUBIKEY_OATH_TOTP_PASSWORD);
|
||||||
|
yubikeyOathTotpSetting.setValue(password);
|
||||||
|
|
||||||
|
generalSettingListViewModel.saveGeneralSetting(yubikeyOathTotpSetting);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityStarted(Activity activity) {
|
public void onActivityStarted(Activity activity) {
|
||||||
if (numStarted == 0) {
|
if (numStarted == 0) {
|
||||||
|
|
|
@ -1,20 +1,42 @@
|
||||||
package cy.agorise.crystalwallet.fragments;
|
package cy.agorise.crystalwallet.fragments;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.nfc.NfcAdapter;
|
||||||
|
import android.nfc.Tag;
|
||||||
|
import android.nfc.tech.IsoDep;
|
||||||
|
import android.nfc.tech.MifareClassic;
|
||||||
|
import android.nfc.tech.NdefFormatable;
|
||||||
|
import android.nfc.tech.NfcA;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.design.widget.TabLayout;
|
import android.support.design.widget.TabLayout;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.app.FragmentManager;
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.support.v4.app.FragmentPagerAdapter;
|
import android.support.v4.app.FragmentPagerAdapter;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.CompoundButton;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.Switch;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base32;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
|
import butterknife.OnCheckedChanged;
|
||||||
import cy.agorise.crystalwallet.R;
|
import cy.agorise.crystalwallet.R;
|
||||||
import cy.agorise.crystalwallet.application.CrystalSecurityMonitor;
|
import cy.agorise.crystalwallet.application.CrystalSecurityMonitor;
|
||||||
import cy.agorise.crystalwallet.models.GeneralSetting;
|
import cy.agorise.crystalwallet.models.GeneralSetting;
|
||||||
import cy.agorise.crystalwallet.util.ChildViewPager;
|
import cy.agorise.crystalwallet.util.ChildViewPager;
|
||||||
|
import cy.agorise.crystalwallet.util.yubikey.Algorithm;
|
||||||
|
import cy.agorise.crystalwallet.util.yubikey.OathType;
|
||||||
|
import cy.agorise.crystalwallet.util.yubikey.YkOathApi;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by xd on 1/17/18.
|
* Created by xd on 1/17/18.
|
||||||
|
@ -34,11 +56,23 @@ public class SecuritySettingsFragment extends Fragment {
|
||||||
return fragment;
|
return fragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private NfcAdapter mNfcAdapter;
|
||||||
|
private PendingIntent pendingIntent;
|
||||||
|
private IntentFilter ndef;
|
||||||
|
IntentFilter[] intentFiltersArray;
|
||||||
|
String[][] techList;
|
||||||
|
|
||||||
|
|
||||||
@BindView(R.id.pager)
|
@BindView(R.id.pager)
|
||||||
public ChildViewPager mPager;
|
public ChildViewPager mPager;
|
||||||
|
|
||||||
public SecurityPagerAdapter securityPagerAdapter;
|
public SecurityPagerAdapter securityPagerAdapter;
|
||||||
|
|
||||||
|
@BindView(R.id.sPocketSecurity)
|
||||||
|
Switch sPocketSecurity;
|
||||||
|
@BindView(R.id.etPocketPassword)
|
||||||
|
EditText etPocketPassword;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
|
@ -66,6 +100,9 @@ public class SecuritySettingsFragment extends Fragment {
|
||||||
mPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
|
mPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
|
||||||
tabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mPager));
|
tabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mPager));
|
||||||
|
|
||||||
|
mNfcAdapter = NfcAdapter.getDefaultAdapter(this.getActivity());
|
||||||
|
this.configureForegroundDispatch();
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,4 +138,87 @@ public class SecuritySettingsFragment extends Fragment {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OnCheckedChanged(R.id.sPocketSecurity)
|
||||||
|
public void onPocketSecurityActivated(CompoundButton button, boolean checked){
|
||||||
|
if (checked) {
|
||||||
|
enableNfc();
|
||||||
|
} else {
|
||||||
|
disableNfc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void configureForegroundDispatch(){
|
||||||
|
if (mNfcAdapter != null) {
|
||||||
|
pendingIntent = PendingIntent.getActivity(
|
||||||
|
this.getActivity(), 0, new Intent(this.getActivity(), getActivity().getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
|
||||||
|
|
||||||
|
ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
|
||||||
|
try {
|
||||||
|
ndef.addDataType("*/*"); /* Handles all MIME based dispatches.
|
||||||
|
You should specify only the ones that you need. */
|
||||||
|
} catch (IntentFilter.MalformedMimeTypeException e) {
|
||||||
|
throw new RuntimeException("fail", e);
|
||||||
|
}
|
||||||
|
intentFiltersArray = new IntentFilter[]{ndef,};
|
||||||
|
techList = new String[][]{ new String[] {IsoDep.class.getName(), NfcA.class.getName(), MifareClassic.class.getName(), NdefFormatable.class.getName()} };
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this.getContext(), "This device doesn't support NFC.", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void enableNfc(){
|
||||||
|
mNfcAdapter.enableForegroundDispatch(this.getActivity(), pendingIntent, intentFiltersArray, techList);
|
||||||
|
Toast.makeText(this.getContext(), "Tap with your yubikey", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disableNfc(){
|
||||||
|
mNfcAdapter.disableForegroundDispatch(this.getActivity());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
disableNfc();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
if (mNfcAdapter != null) {
|
||||||
|
enableNfc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onNewIntent(Intent intent) {
|
||||||
|
Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
|
||||||
|
IsoDep tagIsoDep = IsoDep.get(tagFromIntent);
|
||||||
|
Log.i("Tag from nfc","New Intent");
|
||||||
|
try {
|
||||||
|
|
||||||
|
String serviceName = "cy.agorise.crystalwallet";
|
||||||
|
String encodedSecret = etPocketPassword.getText().toString();
|
||||||
|
Base32 decoder = new Base32();
|
||||||
|
|
||||||
|
if ((encodedSecret != null) && (!encodedSecret.equals("")) && decoder.isInAlphabet(encodedSecret)) {
|
||||||
|
byte[] secret = decoder.decode(encodedSecret);
|
||||||
|
YkOathApi ykOathApi = new YkOathApi();
|
||||||
|
tagIsoDep.connect();
|
||||||
|
tagIsoDep.setTimeout(15000);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ykOathApi.putCode(tagIsoDep, serviceName, secret, OathType.TOTP, Algorithm.SHA256, (byte) 6, 0, false);
|
||||||
|
CrystalSecurityMonitor.getInstance(null).setYubikeyOathTotpSecurity(CrystalSecurityMonitor.getServiceName(),encodedSecret);
|
||||||
|
} catch(IOException e) {
|
||||||
|
Toast.makeText(this.getContext(), "There's no space for new credentials!", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
tagIsoDep.close();
|
||||||
|
Toast.makeText(this.getContext(), "Credential saved!", Toast.LENGTH_LONG).show();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this.getContext(), "Invalid password for credential", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,8 @@ public class GeneralSetting {
|
||||||
public final static String SETTING_PATTERN = "PATTERN";
|
public final static String SETTING_PATTERN = "PATTERN";
|
||||||
public final static String SETTING_NAME_RECEIVED_FUNDS_SOUND_PATH = "RECEIVED_FUNDS_SOUND_PATH";
|
public final static String SETTING_NAME_RECEIVED_FUNDS_SOUND_PATH = "RECEIVED_FUNDS_SOUND_PATH";
|
||||||
public final static String SETTING_LAST_LICENSE_READ = "LAST_LICENSE_READ";
|
public final static String SETTING_LAST_LICENSE_READ = "LAST_LICENSE_READ";
|
||||||
|
public final static String SETTING_YUBIKEY_OATH_TOTP_NAME = "YUBIKEY_OATH_TOTP_NAME";
|
||||||
|
public final static String SETTING_YUBIKEY_OATH_TOTP_PASSWORD = "YUBIKEY_OATH_TOTP_PASSWORD";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The id on the database
|
* The id on the database
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
package cy.agorise.crystalwallet.util.yubikey;
|
||||||
|
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import kotlin.jvm.internal.Intrinsics;
|
||||||
|
|
||||||
|
public enum Algorithm {
|
||||||
|
SHA1((byte)1, "SHA1", 64),
|
||||||
|
SHA256((byte)2, "SHA256", 64),
|
||||||
|
SHA512((byte)3, "SHA512", 128);
|
||||||
|
|
||||||
|
protected Byte byteVal;
|
||||||
|
protected String name;
|
||||||
|
protected int blockSize;
|
||||||
|
protected MessageDigest messageDigest;
|
||||||
|
|
||||||
|
Algorithm(Byte byteVal, String name, int blockSize){
|
||||||
|
this.byteVal = byteVal;
|
||||||
|
this.name = name;
|
||||||
|
this.blockSize = blockSize;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.messageDigest = MessageDigest.getInstance(name);
|
||||||
|
} catch (NoSuchAlgorithmException e){
|
||||||
|
this.messageDigest = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Byte getByteVal(){
|
||||||
|
return this.byteVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName(){
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBlockSize() {
|
||||||
|
return this.blockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] prepareKey(byte[] key){
|
||||||
|
int keyLength = key.length;
|
||||||
|
byte[] keyPrepared;
|
||||||
|
if ((0 <= keyLength) && (keyLength <= 13)) {
|
||||||
|
keyPrepared = Arrays.copyOf(key, 14);
|
||||||
|
return keyPrepared;
|
||||||
|
}
|
||||||
|
if ((14 <= keyLength) && (keyLength <= this.blockSize)){
|
||||||
|
keyPrepared = key;
|
||||||
|
return keyPrepared;
|
||||||
|
}
|
||||||
|
|
||||||
|
keyPrepared = this.messageDigest.digest(key);
|
||||||
|
return keyPrepared;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package cy.agorise.crystalwallet.util.yubikey;
|
||||||
|
|
||||||
|
public enum OathType {
|
||||||
|
HOTP((byte)0x10),
|
||||||
|
TOTP((byte)0x20);
|
||||||
|
|
||||||
|
protected Byte byteVal;
|
||||||
|
|
||||||
|
OathType(Byte byteVal){
|
||||||
|
this.byteVal = byteVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Byte getByteVal(){
|
||||||
|
return this.byteVal;
|
||||||
|
}
|
||||||
|
}
|
18
app/src/main/res/layout/activity_pocket_request.xml
Normal file
18
app/src/main/res/layout/activity_pocket_request.xml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="5dp"
|
||||||
|
android:background="@color/colorPrimary">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Please tap pocket!"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
|
@ -68,7 +68,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="140dp"
|
android:layout_height="140dp"
|
||||||
android:background="@color/lightGray"
|
android:background="@color/lightGray"
|
||||||
android:visibility="gone"
|
android:visibility="visible"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
@ -107,10 +107,9 @@
|
||||||
android:id="@+id/etPocketPassword"
|
android:id="@+id/etPocketPassword"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:inputType="numberPassword"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/tvPocketSecurityUser"
|
|
||||||
app:layout_constraintStart_toStartOf="@id/tvPocketSecurity"
|
|
||||||
app:layout_constraintEnd_toEndOf="@id/sPocketSecurity"
|
app:layout_constraintEnd_toEndOf="@id/sPocketSecurity"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/tvPocketSecurity"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/tvPocketSecurityUser"
|
||||||
tools:ignore="LabelFor" />
|
tools:ignore="LabelFor" />
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
</android.support.constraint.ConstraintLayout>
|
13
app/src/main/res/xml/tech.xml
Normal file
13
app/src/main/res/xml/tech.xml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<tech-list>
|
||||||
|
<tech>android.nfc.tech.MifareUltralight</tech>
|
||||||
|
<tech>android.nfc.tech.Ndef</tech>
|
||||||
|
<tech>android.nfc.tech.NfcA</tech>
|
||||||
|
</tech-list>
|
||||||
|
<tech-list>
|
||||||
|
<tech>android.nfc.tech.MifareClassic</tech>
|
||||||
|
<tech>android.nfc.tech.Ndef</tech>
|
||||||
|
<tech>android.nfc.tech.NfcA</tech>
|
||||||
|
</tech-list>
|
||||||
|
</resources>
|
|
@ -5,9 +5,11 @@ buildscript {
|
||||||
jcenter()
|
jcenter()
|
||||||
google()
|
google()
|
||||||
}
|
}
|
||||||
|
ext.kotlin_version = '1.2.51'
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.1.3'
|
classpath 'com.android.tools.build:gradle:3.1.3'
|
||||||
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
|
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue