token-import: add entrypoint for blank token setup flow

This commit is contained in:
Vincent Breitmoser
2017-09-11 01:42:04 +02:00
parent 19bb4ff83c
commit e0b5d97356
9 changed files with 116 additions and 214 deletions

View File

@@ -1,6 +1,8 @@
package org.sufficientlysecure.keychain.securitytoken;
import java.util.Arrays;
import android.os.Parcelable;
import android.support.annotation.Nullable;
@@ -12,6 +14,8 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
@AutoValue
public abstract class SecurityTokenInfo implements Parcelable {
private static final byte[] EMPTY_ARRAY = new byte[20];
@Nullable
public abstract byte[] getFingerprintSign();
@Nullable
@@ -36,7 +40,8 @@ public abstract class SecurityTokenInfo implements Parcelable {
}
public boolean isEmpty() {
return getFingerprintSign() == null && getFingerprintDecrypt() == null && getFingerprintAuth() == null;
return Arrays.equals(EMPTY_ARRAY, getFingerprintSign()) && Arrays.equals(EMPTY_ARRAY, getFingerprintDecrypt()) &&
Arrays.equals(EMPTY_ARRAY, getFingerprintAuth());
}
public static SecurityTokenInfo create(byte[] fpSign, byte[] fpDecrypt, byte[] fpAuth,
@@ -45,10 +50,6 @@ public abstract class SecurityTokenInfo implements Parcelable {
userId, url, verifyRetries, verifyAdminRetries);
}
public static SecurityTokenInfo createBlank(byte[] aid) {
return new AutoValue_SecurityTokenInfo(null, null, null, aid, null, null, 0, 0);
}
public static SecurityTokenInfo newInstanceDebugKeyserver() {
if (!BuildConfig.DEBUG) {
throw new UnsupportedOperationException("This operation is only available in debug builds!");

View File

@@ -110,16 +110,9 @@ public class CreateKeyActivity extends BaseSecurityTokenActivity {
if (intent.hasExtra(EXTRA_SECURITY_TOKEN_INFO)) {
SecurityTokenInfo tokenInfo = intent.getParcelableExtra(EXTRA_SECURITY_TOKEN_INFO);
if (!tokenInfo.isEmpty()) {
Fragment frag = ManageSecurityTokenFragment.newInstance(tokenInfo);
loadFragment(frag, FragAction.START);
setTitle(R.string.title_import_keys);
} else {
Fragment frag = CreateSecurityTokenBlankFragment.newInstance();
loadFragment(frag, FragAction.START);
setTitle(R.string.title_manage_my_keys);
}
Fragment frag = ManageSecurityTokenFragment.newInstance(tokenInfo);
loadFragment(frag, FragAction.START);
setTitle(R.string.title_manage_my_keys);
// done
return;
@@ -164,15 +157,10 @@ public class CreateKeyActivity extends BaseSecurityTokenActivity {
CreateSecurityTokenWaitFragment.sDisableFragmentAnimations = false;
}
if (!tokenInfo.isEmpty()) {
Fragment frag = ManageSecurityTokenFragment.newInstance(tokenInfo);
if (mCurrentFragment instanceof ManageSecurityTokenFragment) {
loadFragment(frag, FragAction.REPLACE);
} else {
loadFragment(frag, FragAction.TO_RIGHT);
}
Fragment frag = ManageSecurityTokenFragment.newInstance(tokenInfo);
if (mCurrentFragment instanceof ManageSecurityTokenFragment) {
loadFragment(frag, FragAction.REPLACE);
} else {
Fragment frag = CreateSecurityTokenBlankFragment.newInstance();
loadFragment(frag, FragAction.TO_RIGHT);
}
}
@@ -196,6 +184,14 @@ public class CreateKeyActivity extends BaseSecurityTokenActivity {
setContentView(R.layout.create_key_activity);
}
public void startCreateKeyForSecurityToken(SecurityTokenInfo tokenInfo) {
mCreateSecurityToken = true;
this.tokenInfo = tokenInfo;
CreateKeyNameFragment frag = CreateKeyNameFragment.newInstance();
loadFragment(frag, FragAction.TO_RIGHT);
}
public enum FragAction {
START,
TO_RIGHT,

View File

@@ -1,86 +0,0 @@
/*
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
public class CreateSecurityTokenBlankFragment extends Fragment {
CreateKeyActivity mCreateKeyActivity;
View mBackButton;
View mNextButton;
/**
* Creates new instance of this fragment
*/
public static CreateSecurityTokenBlankFragment newInstance() {
return new CreateSecurityTokenBlankFragment();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.create_yubi_key_blank_fragment, container, false);
mBackButton = view.findViewById(R.id.create_key_back_button);
mNextButton = view.findViewById(R.id.create_key_next_button);
mBackButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (getFragmentManager().getBackStackEntryCount() == 0) {
getActivity().setResult(Activity.RESULT_CANCELED);
getActivity().finish();
} else {
mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT);
}
}
});
mNextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
nextClicked();
}
});
return view;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
mCreateKeyActivity = (CreateKeyActivity) getActivity();
}
private void nextClicked() {
mCreateKeyActivity.mCreateSecurityToken = true;
CreateKeyNameFragment frag = CreateKeyNameFragment.newInstance();
mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
}
}

View File

@@ -56,6 +56,8 @@ class ManageSecurityTokenContract {
void onSecurityTokenResetSuccess(SecurityTokenInfo tokenInfo);
void onSecurityTokenResetCanceled(SecurityTokenInfo tokenInfo);
void onClickSetupToken();
void onClickUnlockToken();
void onMenuClickChangePin();
void onInputAdminPin(String adminPin, String newPin);
@@ -73,6 +75,7 @@ class ManageSecurityTokenContract {
void showActionViewKey();
void showActionRetryOrFromFile();
void showActionLocked(int unlockAttempts);
void showActionEmptyToken();
void hideAction();
void operationImportKey(byte[] importKeyData);
@@ -85,6 +88,7 @@ class ManageSecurityTokenContract {
void showFileSelectDialog();
void showConfirmResetDialog();
void showAdminPinDialog();
void startCreateKeyForToken(SecurityTokenInfo tokenInfo);
void showDisplayLogActivity(OperationResult result);

View File

@@ -130,6 +130,7 @@ public class ManageSecurityTokenFragment extends Fragment implements ManageSecur
view.findViewById(R.id.button_unlock).setOnClickListener(this);
view.findViewById(R.id.button_unlock_impossible).setOnClickListener(this);
view.findViewById(R.id.button_load_file).setOnClickListener(this);
view.findViewById(R.id.button_setup).setOnClickListener(this);
setHasOptionsMenu(true);
@@ -253,6 +254,11 @@ public class ManageSecurityTokenFragment extends Fragment implements ManageSecur
}
}
@Override
public void showActionEmptyToken() {
actionAnimator.setDisplayedChildId(R.id.token_layout_empty);
}
@Override
public void hideAction() {
actionAnimator.setDisplayedChild(0);
@@ -325,6 +331,12 @@ public class ManageSecurityTokenFragment extends Fragment implements ManageSecur
adminPinDialog.show();
}
@Override
public void startCreateKeyForToken(SecurityTokenInfo tokenInfo) {
CreateKeyActivity activity = (CreateKeyActivity) getActivity();
activity.startCreateKeyForSecurityToken(tokenInfo);
}
@Override
public void showErrorCannotUnlock() {
Notify.create(getActivity(), R.string.token_error_locked_indefinitely, Style.ERROR).show();
@@ -430,6 +442,11 @@ public class ManageSecurityTokenFragment extends Fragment implements ManageSecur
presenter.onClickUnlockTokenImpossible();
break;
}
case R.id.button_setup: {
presenter.onClickSetupToken();
break;
}
}
}

View File

@@ -107,7 +107,8 @@ class ManageSecurityTokenPresenter implements ManageSecurityTokenMvpPresenter {
private void continueSearch() {
if (!checkedKeyStatus) {
boolean keyIsLocked = tokenInfo.getVerifyRetries() == 0;
if (keyIsLocked) {
boolean keyIsEmpty = tokenInfo.isEmpty();
if (keyIsLocked || keyIsEmpty) {
// the "checking key status" is fake: we only do it if we already know the key is locked
view.statusLineAdd(StatusLine.CHECK_KEY);
delayPerformKeyCheck();
@@ -148,19 +149,27 @@ class ManageSecurityTokenPresenter implements ManageSecurityTokenMvpPresenter {
}
private void performKeyCheck() {
boolean isLocked = tokenInfo.getVerifyRetries() == 0;
if (!isLocked) {
boolean keyIsEmpty = tokenInfo.isEmpty();
if (keyIsEmpty) {
view.statusLineOk();
checkedKeyStatus = true;
continueSearch();
view.showActionEmptyToken();
return;
}
view.statusLineError();
boolean keyIsLocked = tokenInfo.getVerifyRetries() == 0;
if (keyIsLocked) {
view.statusLineError();
int unlockAttemptsLeft = tokenInfo.getVerifyAdminRetries();
view.showActionLocked(unlockAttemptsLeft);
int unlockAttemptsLeft = tokenInfo.getVerifyAdminRetries();
view.showActionLocked(unlockAttemptsLeft);
return;
}
view.statusLineOk();
checkedKeyStatus = true;
continueSearch();
}
@Override
@@ -346,6 +355,11 @@ class ManageSecurityTokenPresenter implements ManageSecurityTokenMvpPresenter {
}
}
@Override
public void onClickSetupToken() {
view.startCreateKeyForToken(tokenInfo);
}
@Override
public void onSecurityTokenChangePinSuccess(SecurityTokenInfo tokenInfo) {
this.tokenInfo = tokenInfo;

View File

@@ -50,7 +50,7 @@
android:inAnimation="@anim/fade_in_delayed"
android:outAnimation="@anim/fade_out"
android:clipChildren="false"
custom:initialView="05">
custom:initialView="06">
<Space
android:layout_width="wrap_content"
@@ -67,7 +67,7 @@
android:layout_height="wrap_content"
android:minHeight="?listPreferredItemHeight"
android:gravity="center_vertical"
android:text="@string/token_status_not_found"
android:text="@string/token_result_not_found"
style="?android:textAppearanceLarge"
/>
@@ -79,7 +79,7 @@
android:drawableStart="@drawable/ic_repeat_grey_24dp"
android:drawablePadding="12dp"
android:textColor="@color/card_view_button"
android:text="@string/token_status_retry"
android:text="@string/token_action_retry"
style="?borderlessButtonStyle"
/>
@@ -91,7 +91,7 @@
android:drawableStart="@drawable/ic_folder_grey_24dp"
android:drawablePadding="12dp"
android:textColor="@color/card_view_button"
android:text="@string/token_status_load_from_file"
android:text="@string/token_action_load_from_file"
style="?borderlessButtonStyle"
/>
@@ -103,7 +103,7 @@
android:drawableStart="@drawable/ic_bomb_24dp"
android:drawablePadding="12dp"
android:textColor="@color/android_red_dark"
android:text="@string/token_status_reset"
android:text="@string/token_action_reset"
style="?borderlessButtonStyle"
/>
@@ -120,7 +120,7 @@
android:layout_height="wrap_content"
android:minHeight="?listPreferredItemHeight"
android:gravity="center_vertical"
android:text="@string/token_status_key_found"
android:text="@string/token_result_key_found"
style="?android:textAppearanceLarge"
/>
@@ -132,7 +132,7 @@
android:drawableStart="@drawable/ic_key_plus_grey600_24dp"
android:drawablePadding="12dp"
android:textColor="@color/card_view_button"
android:text="@string/token_status_import"
android:text="@string/token_action_import"
style="?borderlessButtonStyle"
/>
@@ -144,7 +144,7 @@
android:drawableStart="@drawable/ic_bomb_24dp"
android:drawablePadding="12dp"
android:textColor="@color/android_red_dark"
android:text="@string/token_status_reset"
android:text="@string/token_action_reset"
style="?borderlessButtonStyle"
/>
@@ -161,7 +161,7 @@
android:layout_height="wrap_content"
android:minHeight="?listPreferredItemHeight"
android:gravity="center_vertical"
android:text="@string/token_status_token_ok"
android:text="@string/token_result_token_ok"
style="?android:textAppearanceLarge"
/>
@@ -173,7 +173,7 @@
android:drawableStart="@drawable/ic_vpn_key_grey_24dp"
android:drawablePadding="12dp"
android:textColor="@color/card_view_button"
android:text="@string/token_status_view_key"
android:text="@string/token_action_view_key"
style="?borderlessButtonStyle"
/>
@@ -185,7 +185,7 @@
android:drawableStart="@drawable/ic_bomb_24dp"
android:drawablePadding="12dp"
android:textColor="@color/android_red_dark"
android:text="@string/token_status_reset"
android:text="@string/token_action_reset"
style="?borderlessButtonStyle"
/>
@@ -259,7 +259,7 @@
android:drawableStart="@drawable/ic_bomb_24dp"
android:drawablePadding="12dp"
android:textColor="@color/android_red_dark"
android:text="@string/token_status_reset"
android:text="@string/token_action_reset"
style="?borderlessButtonStyle"
/>
@@ -334,7 +334,37 @@
android:drawableStart="@drawable/ic_bomb_24dp"
android:drawablePadding="12dp"
android:textColor="@color/android_red_dark"
android:text="@string/token_status_reset"
android:text="@string/token_action_reset"
style="?borderlessButtonStyle"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/token_layout_empty">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?listPreferredItemHeight"
android:gravity="center_vertical"
android:text="@string/token_result_empty"
style="?android:textAppearanceLarge"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/button_setup"
android:drawableLeft="@drawable/ic_person_add_grey_24dp"
android:drawableStart="@drawable/ic_person_add_grey_24dp"
android:drawableTint="@color/md_black_1000"
android:drawablePadding="12dp"
android:text="@string/token_action_setup"
style="?borderlessButtonStyle"
/>

View File

@@ -1,76 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:layout_above="@+id/create_key_buttons">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginLeft="8dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/first_time_blank_security_token" />
</LinearLayout>
</ScrollView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:background="?attr/colorButtonRow"
android:id="@+id/create_key_buttons">
<TextView
android:id="@+id/create_key_back_button"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:text="@string/btn_back"
android:textAllCaps="true"
android:minHeight="?android:attr/listPreferredItemHeight"
android:drawableLeft="@drawable/ic_chevron_left_grey_24dp"
android:drawablePadding="8dp"
android:gravity="left|center_vertical"
android:clickable="true"
style="?android:attr/borderlessButtonStyle" />
<TextView
android:id="@+id/create_key_next_button"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:text="@string/first_time_blank_security_token_yes"
android:textAllCaps="true"
android:minHeight="?android:attr/listPreferredItemHeight"
android:drawableRight="@drawable/ic_chevron_right_grey_24dp"
android:drawablePadding="8dp"
android:gravity="center_vertical|right"
android:clickable="true"
style="?android:attr/borderlessButtonStyle" />
</LinearLayout>
</RelativeLayout>

View File

@@ -1931,14 +1931,16 @@
<string name="token_reset_confirm_message">This will irrecoverably delete the key stored on this Security Token. You will no longer be able to use this key for decryption! Are you sure?</string>
<string name="token_reset_confirm_ok">Reset</string>
<string name="token_status_title">Gathering information for Security Token…</string>
<string name="token_status_not_found">Key not found!</string>
<string name="token_status_retry">Retry Search</string>
<string name="token_status_load_from_file">Load from File</string>
<string name="token_status_reset">Reset Security Token</string>
<string name="token_status_key_found">Key found!</string>
<string name="token_status_import">Import</string>
<string name="token_status_token_ok">Ready for use!</string>
<string name="token_status_view_key">View Key</string>
<string name="token_result_not_found">Key not found!</string>
<string name="token_result_key_found">Key found!</string>
<string name="token_result_token_ok">Ready for use!</string>
<string name="token_result_empty">Token is empty</string>
<string name="token_action_retry">Retry Search</string>
<string name="token_action_load_from_file">Load from File</string>
<string name="token_action_reset">Reset Security Token</string>
<string name="token_action_import">Import</string>
<string name="token_action_view_key">View Key</string>
<string name="token_action_setup">"Set up with new key"</string>
<string name="token_action_unlock">Unlock using admin pin</string>
<string name="token_unlock_attempts_none">"No unlock attempts left"</string>
<plurals name="token_unlock_attempts">