diff options
Diffstat (limited to 'OpenKeychain/src/main/java')
19 files changed, 408 insertions, 226 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java index b7d111dc9..6228b29ec 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java @@ -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); +    } + +    protected static LinkedCookieResource fromUri (int nonce, URI 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) { -        LinkedResource res; +        LinkedCookieResource res; -        res = GenericHttpsResource.create(flags, params, uri); +        res = GenericHttpsResource.create(flags, params, subUri);          if (res != null) {              return res;          } -        return new UnknownResource(flags, params, uri); +        return new UnknownResource(flags, params, subUri);      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedIdentity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedIdentity.java deleted file mode 100644 index c46d0aa0a..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedIdentity.java +++ /dev/null @@ -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"; -    } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/RawLinkedIdentity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/RawLinkedIdentity.java new file mode 100644 index 000000000..931f2ec6b --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/RawLinkedIdentity.java @@ -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"; +    } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/DnsResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/DnsResource.java index a2836e666..796e2f120 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/DnsResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/DnsResource.java @@ -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]+)"); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/GenericHttpsResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/GenericHttpsResource.java index abe773f6c..a0f1cf0aa 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/GenericHttpsResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/GenericHttpsResource.java @@ -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)); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/TwitterResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/TwitterResource.java index 1b0db1fa1..84277380d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/TwitterResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/TwitterResource.java @@ -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) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/UnknownResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/UnknownResource.java index ae99cdd86..f29ab5b39 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/UnknownResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/UnknownResource.java @@ -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); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index 5856589c4..a7ca613d7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -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 { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java index 25ca6e8fd..005b60ce0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java @@ -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: { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java index c4e6639a8..492dfd0f7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java @@ -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: diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java index 5afec7903..c6df016ff 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java @@ -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; +            }          }      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/LinkedIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/LinkedIdsAdapter.java new file mode 100644 index 000000000..1e4968615 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/LinkedIdsAdapter.java @@ -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); +    } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java index 457083770..3bc1b200b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java @@ -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); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java index 6a4f61f4b..3c4b0fedf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java @@ -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);      }  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep1Fragment.java index 973067246..14d5f3b5d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep1Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep1Fragment.java @@ -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); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java index 3c413556d..6c9110ffd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java @@ -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); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep1Fragment.java index 7c5f1f032..9fe1d43a3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep1Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep1Fragment.java @@ -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); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java index 6f25ef069..f3fcc4410 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java @@ -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); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java index 25a4f6463..8113525be 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java @@ -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);  | 
