improve status reporting in yubikey dialogue

This commit is contained in:
Vincent Breitmoser
2015-03-22 03:34:34 +01:00
parent a7c52a1c9f
commit 22063cdd6e
3 changed files with 114 additions and 14 deletions

View File

@@ -1,35 +1,48 @@
package org.sufficientlysecure.keychain.ui; package org.sufficientlysecure.keychain.ui;
import java.nio.ByteBuffer;
import java.util.Arrays;
import android.content.Intent; import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle; import android.os.Bundle;
import android.os.Message; import android.os.Message;
import android.os.Messenger; import android.os.Messenger;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView; import android.widget.TextView;
import org.spongycastle.util.encoders.Hex; import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult; import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
public class ViewKeyYubikeyFragment extends Fragment { public class ViewKeyYubikeyFragment extends Fragment
implements LoaderCallbacks<Cursor> {
public static final String ARG_FINGERPRINT = "fingerprint"; public static final String ARG_FINGERPRINT = "fingerprint";
public static final String ARG_USER_ID = "user_id"; public static final String ARG_USER_ID = "user_id";
public static final String ARG_AID = "aid"; public static final String ARG_AID = "aid";
private byte[] mFingerprints; private byte[][] mFingerprints;
private String mUserId; private String mUserId;
private byte[] mAid; private byte[] mAid;
private long mMasterKeyId;
private Button vButton;
private TextView vStatus;
public static ViewKeyYubikeyFragment newInstance(byte[] fingerprints, String userId, byte[] aid) { public static ViewKeyYubikeyFragment newInstance(byte[] fingerprints, String userId, byte[] aid) {
@@ -50,10 +63,19 @@ public class ViewKeyYubikeyFragment extends Fragment {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
Bundle args = getArguments(); Bundle args = getArguments();
mFingerprints = args.getByteArray(ARG_FINGERPRINT); ByteBuffer buf = ByteBuffer.wrap(args.getByteArray(ARG_FINGERPRINT));
mFingerprints = new byte[buf.remaining()/40][];
for (int i = 0; i < mFingerprints.length; i++) {
mFingerprints[i] = new byte[20];
buf.get(mFingerprints[i]);
}
mUserId = args.getString(ARG_USER_ID); mUserId = args.getString(ARG_USER_ID);
mAid = args.getByteArray(ARG_AID); mAid = args.getByteArray(ARG_AID);
mMasterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mFingerprints[0]);
getLoaderManager().initLoader(0, null, this);
} }
@Override @Override
@@ -64,21 +86,23 @@ public class ViewKeyYubikeyFragment extends Fragment {
TextView vUserId = (TextView) view.findViewById(R.id.yubikey_userid); TextView vUserId = (TextView) view.findViewById(R.id.yubikey_userid);
String serno = Hex.toHexString(mAid, 10, 4); String serno = Hex.toHexString(mAid, 10, 4);
vSerNo.setText("Serial N° " + serno); vSerNo.setText(getString(R.string.yubikey_serno, serno));
if (!mUserId.isEmpty()) { if (!mUserId.isEmpty()) {
vUserId.setText("Key holder: " + mUserId); vUserId.setText(getString(R.string.yubikey_key_holder, mUserId));
} else { } else {
vUserId.setText("Key holder: " + "<unset>"); vUserId.setText(getString(R.string.yubikey_key_holder_unset));
} }
view.findViewById(R.id.button_import).setOnClickListener(new OnClickListener() { vButton = (Button) view.findViewById(R.id.button_bind);
vButton.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
promoteToSecretKey(); promoteToSecretKey();
} }
}); });
vStatus = (TextView) view.findViewById(R.id.yubikey_status);
return view; return view;
} }
@@ -111,10 +135,8 @@ public class ViewKeyYubikeyFragment extends Fragment {
intent.setAction(KeychainIntentService.ACTION_PROMOTE_KEYRING); intent.setAction(KeychainIntentService.ACTION_PROMOTE_KEYRING);
long masterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mFingerprints);
Bundle data = new Bundle(); Bundle data = new Bundle();
data.putLong(KeychainIntentService.PROMOTE_MASTER_KEY_ID, masterKeyId); data.putLong(KeychainIntentService.PROMOTE_MASTER_KEY_ID, mMasterKeyId);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data); intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
// Create a new Messenger for the communication back // Create a new Messenger for the communication back
@@ -126,4 +148,73 @@ public class ViewKeyYubikeyFragment extends Fragment {
} }
public static final String[] PROJECTION = new String[]{
Keys._ID,
Keys.KEY_ID,
Keys.RANK,
Keys.HAS_SECRET,
Keys.FINGERPRINT
};
private static final int INDEX_KEY_ID = 1;
private static final int INDEX_RANK = 2;
private static final int INDEX_HAS_SECRET = 3;
private static final int INDEX_FINGERPRINT = 4;
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(getActivity(), Keys.buildKeysUri(mMasterKeyId),
PROJECTION, null, null, null);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
if (!data.moveToFirst()) {
// wut?
return;
}
boolean allBound = true;
boolean noneBound = true;
do {
SecretKeyType keyType = SecretKeyType.fromNum(data.getInt(INDEX_HAS_SECRET));
byte[] fingerprint = data.getBlob(INDEX_FINGERPRINT);
Integer index = naiveIndexOf(mFingerprints, fingerprint);
if (index == null) {
continue;
}
if (keyType == SecretKeyType.DIVERT_TO_CARD) {
noneBound = false;
} else {
allBound = false;
}
} while (data.moveToNext());
if (allBound) {
vButton.setVisibility(View.GONE);
vStatus.setText("Key matches, fully bound");
} else {
vButton.setVisibility(View.VISIBLE);
if (noneBound) {
vStatus.setText("Key matches, can be bound");
} else {
vStatus.setText("Key matches, partly bound");
}
}
}
public Integer naiveIndexOf(byte[][] haystack, byte[] needle) {
for (int i = 0; i < haystack.length; i++) {
if (Arrays.equals(needle, haystack[i])) {
return i;
}
}
return null;
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
}
} }

View File

@@ -21,7 +21,8 @@
card_view:cardBackgroundColor="@android:color/white" card_view:cardBackgroundColor="@android:color/white"
card_view:cardElevation="2dp" card_view:cardElevation="2dp"
card_view:cardUseCompatPadding="true" card_view:cardUseCompatPadding="true"
card_view:cardCornerRadius="4dp"> card_view:cardCornerRadius="4dp"
android:animateLayoutChanges="true">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -37,6 +38,8 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:orientation="horizontal"> android:orientation="horizontal">
<ImageView <ImageView
@@ -69,6 +72,7 @@
/> />
<TextView <TextView
android:id="@+id/yubikey_status"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
@@ -80,13 +84,14 @@
</LinearLayout> </LinearLayout>
<Button <Button
android:id="@+id/button_import" android:id="@+id/button_bind"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="right|end" android:layout_gravity="right|end"
android:text="Import" android:text="@string/button_bind_key"
android:textColor="@color/link_text_material_light" android:textColor="@color/link_text_material_light"
style="?android:attr/borderlessButtonStyle" style="?android:attr/borderlessButtonStyle"
android:visibility="gone"
/> />
</LinearLayout> </LinearLayout>

View File

@@ -1269,5 +1269,9 @@
<string name="unlocked">Unlocked</string> <string name="unlocked">Unlocked</string>
<string name="nfc_settings">Settings</string> <string name="nfc_settings">Settings</string>
<string name="snack_yubikey_view">View</string> <string name="snack_yubikey_view">View</string>
<string name="button_bind_key">Bind Key</string>
<string name="yubikey_serno">"Serial No: %s"</string>
<string name="yubikey_key_holder">"Key holder: "</string>
<string name="yubikey_key_holder_unset">"Key holder: &lt;unset&gt;"</string>
</resources> </resources>