work more on separation of linked identities and resources, initial ui work

This commit is contained in:
Vincent Breitmoser
2015-03-04 12:30:56 +01:00
parent d4df509a1d
commit 8222315dbd
21 changed files with 438 additions and 227 deletions

View File

@@ -14,20 +14,21 @@ import org.sufficientlysecure.keychain.util.Log;
import java.net.URI;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public abstract class LinkedResource {
public abstract class LinkedCookieResource {
protected final URI mSubUri;
protected final Set<String> mFlags;
protected final HashMap<String,String> mParams;
static Pattern magicPattern =
Pattern.compile("\\[Verifying my PGP key: pgpid\\+cookie:([a-zA-Z0-9]+)#([a-zA-Z0-9]+)\\]");
Pattern.compile("\\[Verifying my PGP key: openpgp4fpr:([a-zA-Z0-9]+)#([a-zA-Z0-9]+)\\]");
protected LinkedResource(Set<String> flags, HashMap<String, String> params, URI uri) {
protected LinkedCookieResource(Set<String> flags, HashMap<String, String> params, URI uri) {
mFlags = flags;
mParams = params;
mSubUri = uri;
@@ -41,22 +42,58 @@ public abstract class LinkedResource {
return new HashMap<String,String>(mParams);
}
public URI toUri () {
StringBuilder b = new StringBuilder();
b.append("pgpid+cookie:");
// add flags
if (mFlags != null) {
boolean first = true;
for (String flag : mFlags) {
if (!first) {
b.append(";");
}
first = false;
b.append(flag);
}
}
// add parameters
if (mParams != null) {
boolean first = true;
for (Entry<String, String> stringStringEntry : mParams.entrySet()) {
if (!first) {
b.append(";");
}
first = false;
b.append(stringStringEntry.getKey()).append("=").append(stringStringEntry.getValue());
}
}
b.append("@");
b.append(mSubUri);
return URI.create(b.toString());
}
public URI getSubUri () {
return mSubUri;
}
public static String generate (Context context, byte[] fingerprint, String nonce) {
return "[Verifying my PGP key: pgpid+cookie:"
return "[Verifying my PGP key: openpgp4fpr:"
+ KeyFormattingUtils.convertFingerprintToHex(fingerprint) + "#" + nonce + "]";
}
public static String generatePreview () {
return "[Verifying my PGP key: pgpid+cookie:0x…]";
return "[Verifying my PGP key: openpgp4fpr:0x…]";
}
public LinkedVerifyResult verify(byte[] fingerprint, String nonce) {
public LinkedVerifyResult verify(byte[] fingerprint, int nonce) {
OperationLog log = new OperationLog();
log.add(LogType.MSG_LV, 0);
@@ -82,7 +119,7 @@ public abstract class LinkedResource {
protected LinkedVerifyResult verifyString (OperationLog log, int indent,
String res,
String nonce, byte[] fingerprint) {
int nonce, byte[] fingerprint) {
log.add(LogType.MSG_LV_MATCH, indent);
Matcher match = matchResource(log, indent+1, res);
@@ -92,7 +129,7 @@ public abstract class LinkedResource {
}
String candidateFp = match.group(1).toLowerCase();
String nonceCandidate = match.group(2).toLowerCase();
int nonceCandidate = Integer.parseInt(match.group(2).toLowerCase(), 16);
String fp = KeyFormattingUtils.convertFingerprintToHex(fingerprint);
@@ -102,7 +139,7 @@ public abstract class LinkedResource {
}
log.add(LogType.MSG_LV_FP_OK, indent);
if (!nonce.equals(nonceCandidate)) {
if (nonce != nonceCandidate) {
log.add(LogType.MSG_LV_NONCE_ERROR, indent);
return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log);
}
@@ -112,17 +149,61 @@ public abstract class LinkedResource {
}
public static LinkedResource findResourceType
(Set<String> flags, HashMap<String,String> params, URI uri) {
protected static LinkedCookieResource fromRawLinkedId (RawLinkedIdentity id) {
return fromUri(id.mNonce, id.mUri);
}
LinkedResource res;
protected static LinkedCookieResource fromUri (int nonce, URI uri) {
res = GenericHttpsResource.create(flags, params, uri);
if ("pgpid".equals(uri.getScheme())) {
Log.e(Constants.TAG, "unknown uri scheme in (suspected) linked id packet");
return null;
}
if (!uri.isOpaque()) {
Log.e(Constants.TAG, "non-opaque uri in (suspected) linked id packet");
return null;
}
String specific = uri.getSchemeSpecificPart();
if (!specific.contains("@")) {
Log.e(Constants.TAG, "unknown uri scheme in linked id packet");
return null;
}
String[] pieces = specific.split("@", 2);
URI subUri = URI.create(pieces[1]);
Set<String> flags = new HashSet<String>();
HashMap<String,String> params = new HashMap<String,String>();
{
String[] rawParams = pieces[0].split(";");
for (String param : rawParams) {
String[] p = param.split("=", 2);
if (p.length == 1) {
flags.add(param);
} else {
params.put(p[0], p[1]);
}
}
}
return findResourceType(nonce, flags, params, subUri);
}
protected static LinkedCookieResource findResourceType (int nonce, Set<String> flags,
HashMap<String,String> params,
URI subUri) {
LinkedCookieResource res;
res = GenericHttpsResource.create(flags, params, subUri);
if (res != null) {
return res;
}
return new UnknownResource(flags, params, uri);
return new UnknownResource(flags, params, subUri);
}

View File

@@ -1,172 +0,0 @@
package org.sufficientlysecure.keychain.pgp.linked;
import org.spongycastle.bcpg.UserAttributeSubpacket;
import org.spongycastle.util.Strings;
import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
import org.sufficientlysecure.keychain.util.Log;
import java.net.URI;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
public class LinkedIdentity {
protected byte[] mData;
public final String mNonce;
public final URI mSubUri;
final Set<String> mFlags;
final HashMap<String,String> mParams;
protected LinkedIdentity(byte[] data, String nonce, Set<String> flags,
HashMap<String, String> params, URI subUri) {
if ( ! nonce.matches("[0-9a-zA-Z]+")) {
throw new AssertionError("bug: nonce must be hexstring!");
}
mData = data;
mNonce = nonce;
mFlags = flags;
mParams = params;
mSubUri = subUri;
}
LinkedIdentity(String nonce, Set<String> flags,
HashMap<String, String> params, URI subUri) {
this(null, nonce, flags, params, subUri);
}
public byte[] getEncoded() {
if (mData != null) {
return mData;
}
StringBuilder b = new StringBuilder();
b.append("pgpid:");
// add flags
if (mFlags != null) {
boolean first = true;
for (String flag : mFlags) {
if (!first) {
b.append(";");
}
first = false;
b.append(flag);
}
}
// add parameters
if (mParams != null) {
boolean first = true;
Iterator<Entry<String, String>> it = mParams.entrySet().iterator();
while (it.hasNext()) {
if (!first) {
b.append(";");
}
first = false;
Entry<String, String> entry = it.next();
b.append(entry.getKey()).append("=").append(entry.getValue());
}
}
b.append("@");
b.append(mSubUri);
byte[] nonceBytes = Hex.decode(mNonce);
if (nonceBytes.length != 4) {
throw new AssertionError("nonce must be 4 bytes");
}
byte[] data = Strings.toUTF8ByteArray(b.toString());
byte[] result = new byte[data.length+4];
System.arraycopy(nonceBytes, 0, result, 0, 4);
System.arraycopy(data, 0, result, 4, data.length);
return result;
}
/** This method parses a linked id from a UserAttributeSubpacket, or returns null if the
* subpacket can not be parsed as a valid linked id.
*/
static LinkedIdentity parseAttributeSubpacket(UserAttributeSubpacket subpacket) {
if (subpacket.getType() != 100) {
return null;
}
byte[] data = subpacket.getData();
String nonce = Hex.toHexString(data, 0, 4);
try {
return parseUri(nonce, Strings.fromUTF8ByteArray(Arrays.copyOfRange(data, 4, data.length)));
} catch (IllegalArgumentException e) {
Log.e(Constants.TAG, "error parsing uri in (suspected) linked id packet");
return null;
}
}
protected static LinkedIdentity parseUri (String nonce, String uriString) {
URI uri = URI.create(uriString);
if ("pgpid".equals(uri.getScheme())) {
Log.e(Constants.TAG, "unknown uri scheme in (suspected) linked id packet");
return null;
}
if (!uri.isOpaque()) {
Log.e(Constants.TAG, "non-opaque uri in (suspected) linked id packet");
return null;
}
String specific = uri.getSchemeSpecificPart();
if (!specific.contains("@")) {
Log.e(Constants.TAG, "unknown uri scheme in linked id packet");
return null;
}
String[] pieces = specific.split("@", 2);
URI subUri = URI.create(pieces[1]);
Set<String> flags = new HashSet<String>();
HashMap<String,String> params = new HashMap<String,String>();
{
String[] rawParams = pieces[0].split(";");
for (String param : rawParams) {
String[] p = param.split("=", 2);
if (p.length == 1) {
flags.add(param);
} else {
params.put(p[0], p[1]);
}
}
}
return new LinkedIdentity(nonce, flags, params, subUri);
}
public static LinkedIdentity fromResource (LinkedResource res, String nonce) {
return new LinkedIdentity(nonce, res.getFlags(), res.getParams(), res.getSubUri());
}
public WrappedUserAttribute toUserAttribute () {
return WrappedUserAttribute.fromSubpacket(WrappedUserAttribute.UAT_LINKED_ID, getEncoded());
}
public static String generateNonce() {
// TODO make this actually random
// byte[] data = new byte[4];
// new SecureRandom().nextBytes(data);
// return Hex.toHexString(data);
// debug for now
return "01234567";
}
}

View File

@@ -0,0 +1,85 @@
package org.sufficientlysecure.keychain.pgp.linked;
import org.spongycastle.bcpg.UserAttributeSubpacket;
import org.spongycastle.util.Strings;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
import org.sufficientlysecure.keychain.util.Log;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Set;
/** The RawLinkedIdentity contains raw parsed data from a Linked Identity subpacket. */
public class RawLinkedIdentity {
public final int mNonce;
public final URI mUri;
protected RawLinkedIdentity(int nonce, URI uri) {
mNonce = nonce;
mUri = uri;
}
public byte[] getEncoded() {
byte[] uriData = Strings.toUTF8ByteArray(mUri.toASCIIString());
ByteBuffer buf = ByteBuffer.allocate(4 + uriData.length);
buf.putInt(mNonce);
buf.put(uriData);
return buf.array();
}
/** This method parses a linked id from a UserAttributeSubpacket, or returns null if the
* subpacket can not be parsed as a valid linked id.
*/
static RawLinkedIdentity fromAttributeSubpacket(UserAttributeSubpacket subpacket) {
if (subpacket.getType() != 100) {
return null;
}
byte[] data = subpacket.getData();
return fromSubpacketData(data);
}
public static RawLinkedIdentity fromSubpacketData(byte[] data) {
try {
int nonce = ByteBuffer.wrap(data).getInt();
String uri = Strings.fromUTF8ByteArray(Arrays.copyOfRange(data, 4, data.length));
return new RawLinkedIdentity(nonce, URI.create(uri));
} catch (IllegalArgumentException e) {
Log.e(Constants.TAG, "error parsing uri in (suspected) linked id packet");
return null;
}
}
public static RawLinkedIdentity fromResource (LinkedCookieResource res, int nonce) {
return new RawLinkedIdentity(nonce, res.toUri());
}
public WrappedUserAttribute toUserAttribute () {
return WrappedUserAttribute.fromSubpacket(WrappedUserAttribute.UAT_LINKED_ID, getEncoded());
}
public static String generateNonce() {
// TODO make this actually random
// byte[] data = new byte[4];
// new SecureRandom().nextBytes(data);
// return Hex.toHexString(data);
// debug for now
return "01234567";
}
}

View File

@@ -3,7 +3,7 @@ package org.sufficientlysecure.keychain.pgp.linked.resources;
import android.content.Context;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.pgp.linked.LinkedResource;
import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import java.net.URI;
@@ -21,7 +21,7 @@ import de.measite.minidns.Record.CLASS;
import de.measite.minidns.Record.TYPE;
import de.measite.minidns.record.TXT;
public class DnsResource extends LinkedResource {
public class DnsResource extends LinkedCookieResource {
final static Pattern magicPattern =
Pattern.compile("pgpid\\+cookie=([a-zA-Z0-9]+)(?:#|;)([a-zA-Z0-9]+)");

View File

@@ -8,7 +8,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.pgp.linked.LinkedResource;
import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
@@ -22,14 +22,14 @@ import java.util.Set;
import javax.net.ssl.HttpsURLConnection;
public class GenericHttpsResource extends LinkedResource {
public class GenericHttpsResource extends LinkedCookieResource {
GenericHttpsResource(Set<String> flags, HashMap<String,String> params, URI uri) {
super(flags, params, uri);
}
public static String generateText (Context context, byte[] fingerprint, String nonce) {
String cookie = LinkedResource.generate(context, fingerprint, nonce);
String cookie = LinkedCookieResource.generate(context, fingerprint, nonce);
return String.format(context.getResources().getString(R.string.linked_id_generic_text),
cookie, "0x" + KeyFormattingUtils.convertFingerprintToHex(fingerprint).substring(24));

View File

@@ -17,7 +17,7 @@ import org.apache.http.params.BasicHttpParams;
import org.json.JSONException;
import org.json.JSONObject;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.pgp.linked.LinkedResource;
import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource;
import java.io.BufferedReader;
import java.io.IOException;
@@ -29,7 +29,7 @@ import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Set;
public class TwitterResource extends LinkedResource {
public class TwitterResource extends LinkedCookieResource {
TwitterResource(Set<String> flags, HashMap<String,String> params, URI uri) {
super(flags, params, uri);
@@ -37,7 +37,7 @@ public class TwitterResource extends LinkedResource {
public static String generateText (Context context, byte[] fingerprint, String nonce) {
// nothing special here for now, might change this later
return LinkedResource.generate(context, fingerprint, nonce);
return LinkedCookieResource.generate(context, fingerprint, nonce);
}
private String getTwitterStream(String screenName) {

View File

@@ -1,13 +1,13 @@
package org.sufficientlysecure.keychain.pgp.linked.resources;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.pgp.linked.LinkedResource;
import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource;
import java.net.URI;
import java.util.HashMap;
import java.util.Set;
public class UnknownResource extends LinkedResource {
public class UnknownResource extends LinkedCookieResource {
public UnknownResource(Set<String> flags, HashMap<String,String> params, URI uri) {
super(flags, params, uri);

View File

@@ -106,6 +106,7 @@ public class KeychainContract {
public static final String PATH_PUBLIC = "public";
public static final String PATH_SECRET = "secret";
public static final String PATH_USER_IDS = "user_ids";
public static final String PATH_LINKED_IDS = "linked_ids";
public static final String PATH_KEYS = "keys";
public static final String PATH_CERTS = "certs";
@@ -261,6 +262,10 @@ public class KeychainContract {
public static Uri buildUserIdsUri(Uri uri) {
return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_USER_IDS).build();
}
public static Uri buildLinkedIdsUri(Uri uri) {
return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_LINKED_IDS).build();
}
}
public static class ApiApps implements ApiAppsColumns, BaseColumns {

View File

@@ -328,7 +328,7 @@ public class EditKeyFragment extends LoaderFragment implements
case LOADER_ID_USER_IDS: {
Uri baseUri = UserPackets.buildUserIdsUri(mDataUri);
return new CursorLoader(getActivity(), baseUri,
UserIdsAdapter.USER_IDS_PROJECTION, null, null, null);
UserIdsAdapter.USER_PACKETS_PROJECTION, null, null, null);
}
case LOADER_ID_SUBKEYS: {

View File

@@ -133,7 +133,7 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements
case LOADER_ID_USER_IDS: {
Uri baseUri = UserPackets.buildUserIdsUri(mDataUri);
return new CursorLoader(getActivity(), baseUri,
UserIdsAdapter.USER_IDS_PROJECTION, null, null, null);
UserIdsAdapter.USER_PACKETS_PROJECTION, null, null, null);
}
default:

View File

@@ -24,6 +24,7 @@ import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v7.widget.CardView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -34,6 +35,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.ui.adapter.LinkedIdsAdapter;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
@@ -49,10 +51,14 @@ public class ViewKeyFragment extends LoaderFragment implements
private static final int LOADER_ID_UNIFIED = 0;
private static final int LOADER_ID_USER_IDS = 1;
private static final int LOADER_ID_LINKED_IDS = 2;
private UserIdsAdapter mUserIdsAdapter;
private LinkedIdsAdapter mLinkedIdsAdapter;
private Uri mDataUri;
private ListView mLinkedIds;
private CardView mLinkedIdsCard;
/**
* Creates new instance of this fragment
@@ -73,6 +79,11 @@ public class ViewKeyFragment extends LoaderFragment implements
View view = inflater.inflate(R.layout.view_key_fragment, getContainer());
mUserIds = (ListView) view.findViewById(R.id.view_key_user_ids);
mLinkedIdsCard = (CardView) view.findViewById(R.id.card_linked_ids);
mLinkedIds = (ListView) view.findViewById(R.id.view_key_linked_ids);
mLinkedIdsAdapter = new LinkedIdsAdapter(getActivity(), null, 0);
mLinkedIds.setAdapter(mLinkedIdsAdapter);
mUserIds.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
@@ -80,6 +91,7 @@ public class ViewKeyFragment extends LoaderFragment implements
showUserIdInfo(position);
}
});
mLinkedIdsCard.setVisibility(View.GONE);
return root;
}
@@ -145,23 +157,23 @@ public class ViewKeyFragment extends LoaderFragment implements
case LOADER_ID_USER_IDS:
return UserIdsAdapter.createLoader(getActivity(), mDataUri);
case LOADER_ID_LINKED_IDS:
return LinkedIdsAdapter.createLoader(getActivity(), mDataUri);
default:
return null;
}
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
/* TODO better error handling? May cause problems when a key is deleted,
* because the notification triggers faster than the activity closes.
*/
// Avoid NullPointerExceptions...
if (data.getCount() == 0) {
return;
}
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
switch (loader.getId()) {
case LOADER_ID_UNIFIED: {
// Avoid NullPointerExceptions...
if (data.getCount() == 0) {
return;
}
if (data.moveToFirst()) {
mIsSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0;
@@ -180,6 +192,11 @@ public class ViewKeyFragment extends LoaderFragment implements
break;
}
case LOADER_ID_LINKED_IDS: {
mLinkedIdsCard.setVisibility(data.getCount() > 0 ? View.VISIBLE : View.GONE);
mLinkedIdsAdapter.swapCursor(data);
break;
}
}
setContentShown(true);
}
@@ -194,6 +211,11 @@ public class ViewKeyFragment extends LoaderFragment implements
mUserIdsAdapter.swapCursor(null);
break;
}
case LOADER_ID_LINKED_IDS: {
mLinkedIdsCard.setVisibility(View.GONE);
mLinkedIdsAdapter.swapCursor(null);
break;
}
}
}

View File

@@ -0,0 +1,158 @@
/*
* Copyright (C) 2014-2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
*
* 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.adapter;
import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Typeface;
import android.net.Uri;
import android.support.v4.content.CursorLoader;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
public class LinkedIdsAdapter extends UserAttributesAdapter {
protected LayoutInflater mInflater;
public LinkedIdsAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
mInflater = LayoutInflater.from(context);
}
@Override
public int getItemViewType(int position) {
RawLinkedIdentity id = (RawLinkedIdentity) getItem(position);
// TODO return different ids by type
return 0;
}
@Override
public int getViewTypeCount() {
return 1;
}
@Override
public Object getItem(int position) {
Cursor c = getCursor();
c.moveToPosition(position);
byte[] data = c.getBlob(INDEX_ATTRIBUTE_DATA);
RawLinkedIdentity identity = RawLinkedIdentity.fromSubpacketData(data);
return identity;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
TextView vName = (TextView) view.findViewById(R.id.user_id_item_name);
TextView vAddress = (TextView) view.findViewById(R.id.user_id_item_address);
TextView vComment = (TextView) view.findViewById(R.id.user_id_item_comment);
ImageView vVerified = (ImageView) view.findViewById(R.id.user_id_item_certified);
View vVerifiedLayout = view.findViewById(R.id.user_id_item_certified_layout);
ImageView vEditImage = (ImageView) view.findViewById(R.id.user_id_item_edit_image);
ImageView vDeleteButton = (ImageView) view.findViewById(R.id.user_id_item_delete_button);
vDeleteButton.setVisibility(View.GONE); // not used
String userId = cursor.getString(INDEX_USER_ID);
String[] splitUserId = KeyRing.splitUserId(userId);
if (splitUserId[0] != null) {
vName.setText(splitUserId[0]);
} else {
vName.setText(R.string.user_id_no_name);
}
if (splitUserId[1] != null) {
vAddress.setText(splitUserId[1]);
vAddress.setVisibility(View.VISIBLE);
} else {
vAddress.setVisibility(View.GONE);
}
if (splitUserId[2] != null) {
vComment.setText(splitUserId[2]);
vComment.setVisibility(View.VISIBLE);
} else {
vComment.setVisibility(View.GONE);
}
boolean isPrimary = cursor.getInt(INDEX_IS_PRIMARY) != 0;
boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
vVerifiedLayout.setVisibility(View.VISIBLE);
if (isRevoked) {
// set revocation icon (can this even be primary?)
KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_REVOKED, R.color.bg_gray);
// disable revoked user ids
vName.setEnabled(false);
vAddress.setEnabled(false);
vComment.setEnabled(false);
} else {
vName.setEnabled(true);
vAddress.setEnabled(true);
vComment.setEnabled(true);
if (isPrimary) {
vName.setTypeface(null, Typeface.BOLD);
vAddress.setTypeface(null, Typeface.BOLD);
} else {
vName.setTypeface(null, Typeface.NORMAL);
vAddress.setTypeface(null, Typeface.NORMAL);
}
int isVerified = cursor.getInt(INDEX_VERIFIED);
switch (isVerified) {
case Certs.VERIFIED_SECRET:
KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_VERIFIED, KeyFormattingUtils.DEFAULT_COLOR);
break;
case Certs.VERIFIED_SELF:
KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_UNVERIFIED, KeyFormattingUtils.DEFAULT_COLOR);
break;
default:
KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_INVALID, KeyFormattingUtils.DEFAULT_COLOR);
break;
}
}
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return mInflater.inflate(R.layout.view_key_adv_user_id_item, null);
}
// don't show revoked user ids, irrelevant for average users
public static final String LINKED_IDS_WHERE = UserPackets.IS_REVOKED + " = 0";
public static CursorLoader createLoader(Activity activity, Uri dataUri) {
Uri baseUri = UserPackets.buildLinkedIdsUri(dataUri);
return new CursorLoader(activity, baseUri,
UserIdsAdapter.USER_PACKETS_PROJECTION, LINKED_IDS_WHERE, null, null);
}
}

View File

@@ -8,10 +8,11 @@ import android.view.View;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
public abstract class UserAttributesAdapter extends CursorAdapter {
public static final String[] USER_IDS_PROJECTION = new String[]{
public static final String[] USER_PACKETS_PROJECTION = new String[]{
UserPackets._ID,
UserPackets.TYPE,
UserPackets.USER_ID,
UserPackets.ATTRIBUTE_DATA,
UserPackets.RANK,
UserPackets.VERIFIED,
UserPackets.IS_PRIMARY,
@@ -20,10 +21,11 @@ public abstract class UserAttributesAdapter extends CursorAdapter {
protected static final int INDEX_ID = 0;
protected static final int INDEX_TYPE = 1;
protected static final int INDEX_USER_ID = 2;
protected static final int INDEX_RANK = 3;
protected static final int INDEX_VERIFIED = 4;
protected static final int INDEX_IS_PRIMARY = 5;
protected static final int INDEX_IS_REVOKED = 6;
protected static final int INDEX_ATTRIBUTE_DATA = 3;
protected static final int INDEX_RANK = 4;
protected static final int INDEX_VERIFIED = 5;
protected static final int INDEX_IS_PRIMARY = 6;
protected static final int INDEX_IS_REVOKED = 7;
public UserAttributesAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);

View File

@@ -187,7 +187,7 @@ public class UserIdsAdapter extends UserAttributesAdapter {
public static CursorLoader createLoader(Activity activity, Uri dataUri) {
Uri baseUri = UserPackets.buildUserIdsUri(dataUri);
return new CursorLoader(activity, baseUri,
UserIdsAdapter.USER_IDS_PROJECTION, USER_IDS_WHERE, null, null);
UserIdsAdapter.USER_PACKETS_PROJECTION, USER_IDS_WHERE, null, null);
}
}

View File

@@ -29,7 +29,7 @@ import android.view.ViewGroup;
import android.widget.EditText;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity;
import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity;
import org.sufficientlysecure.keychain.pgp.linked.resources.DnsResource;
public class LinkedIdCreateDnsStep1Fragment extends Fragment {
@@ -73,7 +73,7 @@ public class LinkedIdCreateDnsStep1Fragment extends Fragment {
return;
}
String proofNonce = LinkedIdentity.generateNonce();
String proofNonce = RawLinkedIdentity.generateNonce();
String proofText = DnsResource.generateText(getActivity(),
mLinkedIdWizard.mFingerprint, proofNonce);

View File

@@ -33,7 +33,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
@@ -42,7 +41,7 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity;
import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity;
import org.sufficientlysecure.keychain.pgp.linked.resources.DnsResource;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
@@ -71,7 +70,8 @@ public class LinkedIdCreateDnsStep2Fragment extends Fragment {
TextView mVerifyStatus;
String mResourceDomain;
String mResourceNonce, mResourceString;
int mResourceNonce;
String mResourceString;
// This is a resource, set AFTER it has been verified
DnsResource mVerifiedResource = null;
@@ -98,7 +98,7 @@ public class LinkedIdCreateDnsStep2Fragment extends Fragment {
final View view = inflater.inflate(R.layout.linked_create_dns_fragment_step2, container, false);
mResourceDomain = getArguments().getString(DOMAIN);
mResourceNonce = getArguments().getString(NONCE);
mResourceNonce = getArguments().getInt(NONCE);
mResourceString = getArguments().getString(TEXT);
view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() {
@@ -308,7 +308,7 @@ public class LinkedIdCreateDnsStep2Fragment extends Fragment {
new SaveKeyringParcel(mLinkedIdWizard.mMasterKeyId, mLinkedIdWizard.mFingerprint);
WrappedUserAttribute ua =
LinkedIdentity.fromResource(mVerifiedResource, mResourceNonce).toUserAttribute();
RawLinkedIdentity.fromResource(mVerifiedResource, mResourceNonce).toUserAttribute();
skp.mAddUserAttribute.add(ua);

View File

@@ -29,7 +29,7 @@ import android.view.ViewGroup;
import android.widget.EditText;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity;
import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity;
import org.sufficientlysecure.keychain.pgp.linked.resources.GenericHttpsResource;
public class LinkedIdCreateHttpsStep1Fragment extends Fragment {
@@ -72,7 +72,7 @@ public class LinkedIdCreateHttpsStep1Fragment extends Fragment {
return;
}
String proofNonce = LinkedIdentity.generateNonce();
String proofNonce = RawLinkedIdentity.generateNonce();
String proofText = GenericHttpsResource.generateText(getActivity(),
mLinkedIdWizard.mFingerprint, proofNonce);

View File

@@ -42,7 +42,7 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity;
import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity;
import org.sufficientlysecure.keychain.pgp.linked.resources.GenericHttpsResource;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
@@ -73,7 +73,8 @@ public class LinkedIdCreateHttpsStep2Fragment extends Fragment {
TextView mVerifyStatus;
String mResourceUri;
String mResourceNonce, mResourceString;
int mResourceNonce;
String mResourceString;
// This is a resource, set AFTER it has been verified
GenericHttpsResource mVerifiedResource = null;
@@ -100,7 +101,7 @@ public class LinkedIdCreateHttpsStep2Fragment extends Fragment {
final View view = inflater.inflate(R.layout.linked_create_https_fragment_step2, container, false);
mResourceUri = getArguments().getString(URI);
mResourceNonce = getArguments().getString(NONCE);
mResourceNonce = getArguments().getInt(NONCE);
mResourceString = getArguments().getString(TEXT);
view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() {
@@ -314,7 +315,7 @@ public class LinkedIdCreateHttpsStep2Fragment extends Fragment {
new SaveKeyringParcel(mLinkedIdWizard.mMasterKeyId, mLinkedIdWizard.mFingerprint);
WrappedUserAttribute ua =
LinkedIdentity.fromResource(mVerifiedResource, mResourceNonce).toUserAttribute();
RawLinkedIdentity.fromResource(mVerifiedResource, mResourceNonce).toUserAttribute();
skp.mAddUserAttribute.add(ua);

View File

@@ -27,7 +27,7 @@ import android.view.ViewGroup;
import android.widget.EditText;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity;
import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity;
import org.sufficientlysecure.keychain.pgp.linked.resources.TwitterResource;
import org.sufficientlysecure.keychain.ui.util.Notify;
@@ -92,7 +92,7 @@ public class LinkedIdCreateTwitterStep1Fragment extends Fragment {
return;
}
String proofNonce = LinkedIdentity.generateNonce();
String proofNonce = RawLinkedIdentity.generateNonce();
String proofText = TwitterResource.generateText(getActivity(),
mLinkedIdWizard.mFingerprint, proofNonce);

View File

@@ -13,7 +13,7 @@
android:paddingRight="16dp">
<android.support.v7.widget.CardView
android:id="@+id/card_view"
android:id="@+id/card_identities"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -41,6 +41,34 @@
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:id="@+id/card_linked_ids"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardBackgroundColor="@android:color/white"
card_view:cardElevation="2dp"
card_view:cardUseCompatPadding="true"
card_view:cardCornerRadius="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
style="@style/CardViewHeader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/section_linked_identities" />
<org.sufficientlysecure.keychain.ui.widget.FixedListView
android:id="@+id/view_key_linked_ids"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp" />
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>

View File

@@ -1296,5 +1296,6 @@
<string name="linked_verify_pending">Not yet verified</string>
<string name="linked_need_verify">The resource needs to be verified before you can proceed!</string>
<string name="menu_linked_add_identity">"Add Linked Identity"</string>
<string name="section_linked_identities">Linked Identities</string>
</resources>