Create key wizard for blank YubiKey

This commit is contained in:
Dominik Schürmann
2015-06-29 20:48:11 +02:00
parent cf59a8fc30
commit a9c606d49b
19 changed files with 313 additions and 74 deletions

View File

@@ -95,6 +95,15 @@
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.MainActivity" />
<!-- Connect with YubiKeys. This Activity will automatically show/import/create key -->
<!--<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=".ui.EditKeyActivity"
@@ -687,17 +696,6 @@
android:launchMode="singleTop"
android:taskAffinity=":Nfc" />
<!--<activity-->
<!--android:name=".ui.NfcIntentActivity"-->
<!--android:launchMode="singleTop">-->
<!--<intent-filter>-->
<!--<action android:name="android.nfc.action.NDEF_DISCOVERED" />-->
<!--<category android:name="android.intent.category.DEFAULT" />-->
<!--<data android:host="my.yubico.com" android:scheme="https"/>-->
<!--</intent-filter>-->
<!--</activity>-->
<activity
android:name=".ui.HelpActivity"
android:label="@string/title_help" />

View File

@@ -167,7 +167,7 @@ public class OpenPgpService extends RemoteService {
Intent data, RequiredInputParcel requiredInput) {
switch (requiredInput.mType) {
case NFC_KEYTOCARD:
case NFC_MOVE_KEY_TO_CARD:
case NFC_DECRYPT:
case NFC_SIGN: {
// build PendingIntent for YubiKey NFC operations

View File

@@ -12,7 +12,7 @@ import android.os.Parcelable;
public class RequiredInputParcel implements Parcelable {
public enum RequiredInputType {
PASSPHRASE, PASSPHRASE_SYMMETRIC, NFC_SIGN, NFC_DECRYPT, NFC_KEYTOCARD
PASSPHRASE, PASSPHRASE_SYMMETRIC, NFC_SIGN, NFC_DECRYPT, NFC_MOVE_KEY_TO_CARD
}
public Date mSignatureTime;
@@ -226,7 +226,7 @@ public class RequiredInputParcel implements Parcelable {
ByteBuffer buf = ByteBuffer.wrap(mSubkeysToExport.get(0));
// We need to pass in a subkey here...
return new RequiredInputParcel(RequiredInputType.NFC_KEYTOCARD,
return new RequiredInputParcel(RequiredInputType.NFC_MOVE_KEY_TO_CARD,
inputHashes, null, null, mMasterKeyId, buf.getLong());
}
@@ -241,7 +241,7 @@ public class RequiredInputParcel implements Parcelable {
if (!mMasterKeyId.equals(input.mMasterKeyId)) {
throw new AssertionError("Master keys must match, this is a programming error!");
}
if (input.mType != RequiredInputType.NFC_KEYTOCARD) {
if (input.mType != RequiredInputType.NFC_MOVE_KEY_TO_CARD) {
throw new AssertionError("Operation types must match, this is a programming error!");
}

View File

@@ -17,8 +17,6 @@
package org.sufficientlysecure.keychain.ui;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
@@ -89,13 +87,19 @@ public class CreateKeyActivity extends BaseNfcActivity {
String nfcUserId = intent.getStringExtra(EXTRA_NFC_USER_ID);
byte[] nfcAid = intent.getByteArrayExtra(EXTRA_NFC_AID);
Fragment frag2 = CreateKeyYubiKeyImportFragment.createInstance(
nfcFingerprints, nfcAid, nfcUserId);
loadFragment(frag2, FragAction.START);
if (containsKeys(nfcFingerprints)) {
Fragment frag = CreateKeyYubiKeyImportFragment.newInstance(
nfcFingerprints, nfcAid, nfcUserId);
loadFragment(frag, FragAction.START);
setTitle(R.string.title_import_keys);
setTitle(R.string.title_import_keys);
} else {
Fragment frag = CreateKeyYubiKeyBlankFragment.newInstance();
loadFragment(frag, FragAction.START);
}
return;
} else {
// normal key creation
CreateKeyStartFragment frag = CreateKeyStartFragment.newInstance();
loadFragment(frag, FragAction.START);
}
@@ -122,16 +126,7 @@ public class CreateKeyActivity extends BaseNfcActivity {
byte[] nfcAid = nfcGetAid();
String userId = nfcGetUserId();
// If all fingerprint bytes are 0, the card contains no keys.
boolean cardContainsKeys = false;
for (byte b : scannedFingerprints) {
if (b != 0) {
cardContainsKeys = true;
break;
}
}
if (cardContainsKeys) {
if (containsKeys(scannedFingerprints)) {
try {
long masterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(scannedFingerprints);
CachedPublicKeyRing ring = new ProviderHelper(this).getCachedPublicKeyRing(masterKeyId);
@@ -146,25 +141,29 @@ public class CreateKeyActivity extends BaseNfcActivity {
finish();
} catch (PgpKeyNotFoundException e) {
Fragment frag = CreateKeyYubiKeyImportFragment.createInstance(
Fragment frag = CreateKeyYubiKeyImportFragment.newInstance(
scannedFingerprints, nfcAid, userId);
loadFragment(frag, FragAction.TO_RIGHT);
}
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.first_time_blank_smartcard_title)
.setMessage(R.string.first_time_blank_smartcard_message)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int button) {
CreateKeyActivity.this.mUseSmartCardSettings = true;
}
})
.setNegativeButton(android.R.string.no, null).show();
Fragment frag = CreateKeyYubiKeyBlankFragment.newInstance();
loadFragment(frag, FragAction.TO_RIGHT);
}
}
private boolean containsKeys(byte[] scannedFingerprints) {
// If all fingerprint bytes are 0, the card contains no keys.
boolean cardContainsKeys = false;
for (byte b : scannedFingerprints) {
if (b != 0) {
cardContainsKeys = true;
break;
}
}
return cardContainsKeys;
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
@@ -182,7 +181,7 @@ public class CreateKeyActivity extends BaseNfcActivity {
setContentView(R.layout.create_key_activity);
}
public static enum FragAction {
public enum FragAction {
START,
TO_RIGHT,
TO_LEFT

View File

@@ -20,11 +20,11 @@ package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -37,19 +37,24 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
import java.util.Iterator;
public class CreateKeyFinalFragment extends Fragment {
public class CreateKeyFinalFragment extends CryptoOperationFragment<SaveKeyringParcel, OperationResult> {
public static final int REQUEST_EDIT_KEY = 0x00008007;
@@ -129,6 +134,11 @@ public class CreateKeyFinalFragment extends Fragment {
}
});
// If this is a debug build, don't upload by default
if (Constants.DEBUG) {
mUploadCheckbox.setChecked(false);
}
return view;
}
@@ -164,6 +174,7 @@ public class CreateKeyFinalFragment extends Fragment {
mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA,
2048, null, KeyFlags.AUTHENTICATION, 0L));
mEditText.setText(R.string.create_key_custom);
mEditButton.setEnabled(false);
} else {
mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA,
4096, null, KeyFlags.CERTIFY_OTHER, 0L));
@@ -192,8 +203,9 @@ public class CreateKeyFinalFragment extends Fragment {
}
}
private void createKey() {
final CreateKeyActivity createKeyActivity = (CreateKeyActivity) getActivity();
Intent intent = new Intent(getActivity(), KeychainService.class);
intent.setAction(KeychainService.ACTION_EDIT_KEYRING);
@@ -216,25 +228,29 @@ public class CreateKeyFinalFragment extends Fragment {
return;
}
if (createKeyActivity.mUseSmartCardSettings) {
// save key id in between
mSaveKeyringParcel.mMasterKeyId = result.mMasterKeyId;
cryptoOperation(new CryptoInputParcel());
return;
}
if (result.mMasterKeyId != null && mUploadCheckbox.isChecked()) {
// result will be displayed after upload
uploadKey(result);
} else {
Intent data = new Intent();
data.putExtra(OperationResult.EXTRA_RESULT, result);
getActivity().setResult(Activity.RESULT_OK, data);
getActivity().finish();
return;
}
Intent data = new Intent();
data.putExtra(OperationResult.EXTRA_RESULT, result);
getActivity().setResult(Activity.RESULT_OK, data);
getActivity().finish();
}
}
};
// fill values for this action
Bundle data = new Bundle();
// get selected key entries
data.putParcelable(KeychainService.EDIT_KEYRING_PARCEL, mSaveKeyringParcel);
intent.putExtra(KeychainService.EXTRA_DATA, data);
// Create a new Messenger for the communication back
@@ -247,6 +263,55 @@ public class CreateKeyFinalFragment extends Fragment {
getActivity().startService(intent);
}
// currently only used for moveToCard
@Override
protected SaveKeyringParcel createOperationInput() {
CachedPublicKeyRing key = (new ProviderHelper(getActivity()))
.getCachedPublicKeyRing(mSaveKeyringParcel.mMasterKeyId);
// overwrite mSaveKeyringParcel!
try {
mSaveKeyringParcel = new SaveKeyringParcel(key.getMasterKeyId(), key.getFingerprint());
} catch (PgpKeyNotFoundException e) {
Log.e(Constants.TAG, "Key that should be moved to YubiKey not found in database!");
return null;
}
Cursor cursor = getActivity().getContentResolver().query(
KeychainContract.Keys.buildKeysUri(mSaveKeyringParcel.mMasterKeyId),
new String[]{KeychainContract.Keys.KEY_ID,}, null, null, null
);
try {
while (cursor != null && cursor.moveToNext()) {
long subkeyId = cursor.getLong(0);
mSaveKeyringParcel.getOrCreateSubkeyChange(subkeyId).mMoveKeyToCard = true;
}
} finally {
if (cursor != null) {
cursor.close();
}
}
return mSaveKeyringParcel;
}
// currently only used for moveToCard
@Override
protected void onCryptoOperationSuccess(OperationResult result) {
EditKeyResult editResult = (EditKeyResult) result;
if (editResult.mMasterKeyId != null && mUploadCheckbox.isChecked()) {
// result will be displayed after upload
uploadKey(editResult);
return;
}
Intent data = new Intent();
data.putExtra(OperationResult.EXTRA_RESULT, result);
getActivity().setResult(Activity.RESULT_OK, data);
getActivity().finish();
}
// TODO move into EditKeyOperation
private void uploadKey(final EditKeyResult saveKeyResult) {
// Send all information needed to service to upload key in other thread
@@ -259,7 +324,6 @@ public class CreateKeyFinalFragment extends Fragment {
saveKeyResult.mMasterKeyId);
intent.setData(blobUri);
// fill values for this action
Bundle data = new Bundle();
// upload to favorite keyserver
@@ -300,7 +364,6 @@ public class CreateKeyFinalFragment extends Fragment {
// start service with intent
getActivity().startService(intent);
}
}

View File

@@ -0,0 +1,90 @@
/*
* 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.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 CreateKeyYubiKeyBlankFragment extends Fragment {
CreateKeyActivity mCreateKeyActivity;
View mBackButton;
View mNextButton;
/**
* Creates new instance of this fragment
*/
public static CreateKeyYubiKeyBlankFragment newInstance() {
CreateKeyYubiKeyBlankFragment frag = new CreateKeyYubiKeyBlankFragment();
Bundle args = new Bundle();
frag.setArguments(args);
return frag;
}
@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(Activity activity) {
super.onAttach(activity);
mCreateKeyActivity = (CreateKeyActivity) getActivity();
}
private void nextClicked() {
mCreateKeyActivity.mUseSmartCardSettings = true;
CreateKeyNameFragment frag = CreateKeyNameFragment.newInstance();
mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
}
}

View File

@@ -64,7 +64,7 @@ public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListe
private TextView vSerNo;
private TextView vUserId;
public static Fragment createInstance(byte[] scannedFingerprints, byte[] nfcAid, String userId) {
public static Fragment newInstance(byte[] scannedFingerprints, byte[] nfcAid, String userId) {
CreateKeyYubiKeyImportFragment frag = new CreateKeyYubiKeyImportFragment();
@@ -95,7 +95,7 @@ public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListe
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.create_yubikey_import_fragment, container, false);
View view = inflater.inflate(R.layout.create_yubi_key_import_fragment, container, false);
vSerNo = (TextView) view.findViewById(R.id.yubikey_serno);
vUserId = (TextView) view.findViewById(R.id.yubikey_userid);

View File

@@ -35,7 +35,7 @@ public class CreateKeyYubiKeyWaitFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.create_yubikey_wait_fragment, container, false);
View view = inflater.inflate(R.layout.create_yubi_key_wait_fragment, container, false);
mBackButton = view.findViewById(R.id.create_key_back_button);

View File

@@ -63,7 +63,7 @@ public class NfcOperationActivity extends BaseNfcActivity {
mServiceIntent = data.getParcelable(EXTRA_SERVICE_INTENT);
// obtain passphrase for this subkey
if (mRequiredInput.mType != RequiredInputParcel.RequiredInputType.NFC_KEYTOCARD) {
if (mRequiredInput.mType != RequiredInputParcel.RequiredInputType.NFC_MOVE_KEY_TO_CARD) {
obtainYubiKeyPin(mRequiredInput);
}
}
@@ -96,7 +96,7 @@ public class NfcOperationActivity extends BaseNfcActivity {
}
break;
}
case NFC_KEYTOCARD: {
case NFC_MOVE_KEY_TO_CARD: {
ProviderHelper providerHelper = new ProviderHelper(this);
CanonicalizedSecretKeyRing secretKeyRing;
try {

View File

@@ -269,7 +269,7 @@ public abstract class BaseNfcActivity extends BaseActivity {
* This method is called by onNewIntent above upon discovery of an NFC tag.
* It handles initialization and login to the application, subsequently
* calls either nfcCalculateSignature() or nfcDecryptSessionKey(), then
* finishes the activity with an appropiate result.
* finishes the activity with an appropriate result.
*
* On general communication, see also
* http://www.cardwerk.com/smartcards/smartcard_standard_ISO7816-4_annex-a.aspx

View File

@@ -52,7 +52,7 @@ public abstract class CryptoOperationFragment <T extends Parcelable, S extends O
private void initiateInputActivity(RequiredInputParcel requiredInput) {
switch (requiredInput.mType) {
case NFC_KEYTOCARD:
case NFC_MOVE_KEY_TO_CARD:
case NFC_DECRYPT:
case NFC_SIGN: {
Intent intent = new Intent(getActivity(), NfcOperationActivity.class);

View File

@@ -0,0 +1,76 @@
<?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_yubikey" />
</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="@color/holo_gray_bright"
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: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"
android:layout_gravity="center_vertical" />
<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:text="@string/first_time_blank_yubikey_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"
android:layout_gravity="center_vertical" />
</LinearLayout>
</RelativeLayout>

View File

@@ -9,7 +9,7 @@
<item
android:id="@+id/decrypt_save"
android:title="@string/btn_save"
android:title="@string/btn_save_file"
android:icon="@drawable/ic_action_encrypt_file_24dp"
/>

View File

@@ -63,7 +63,8 @@
<string name="btn_decrypt_verify_file">"Decrypt, verify, and save file"</string>
<string name="btn_encrypt_share_file">"Encrypt and share file"</string>
<string name="btn_encrypt_save_file">"Encrypt and save file"</string>
<string name="btn_save">"Save file"</string>
<string name="btn_save_file">"Save file"</string>
<string name="btn_save">"Save"</string>
<string name="btn_view_log">"View log"</string>
<string name="btn_do_not_save">"Cancel"</string>
<string name="btn_delete">"Delete"</string>
@@ -1236,8 +1237,8 @@
<string name="first_time_import_key">"Import key from file"</string>
<string name="first_time_yubikey">"Use YubiKey NEO"</string>
<string name="first_time_skip">"Skip Setup"</string>
<string name="first_time_blank_smartcard_title">"Blank smart card / YubiKey detected"</string>
<string name="first_time_blank_smartcard_message">"Would you like to generate a smart card compatible key?"</string>
<string name="first_time_blank_yubikey">"Would you like to use this blank YubiKey NEO with OpenKeychain?\n\nPlease take away the YubiKey now, you will be prompted when it is needed again!"</string>
<string name="first_time_blank_yubikey_yes">"Use this YubiKey"</string>
<!-- unsorted -->
<string name="section_certifier_id">"Certifier"</string>