work more on separation of linked identities and resources, initial ui work
This commit is contained in:
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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]+)");
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user