diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp')
6 files changed, 358 insertions, 0 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/Affirmation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/Affirmation.java new file mode 100644 index 000000000..f12ebd481 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/Affirmation.java @@ -0,0 +1,157 @@ +package org.sufficientlysecure.keychain.pgp.affirmation; + +import org.spongycastle.bcpg.UserAttributeSubpacket; +import org.spongycastle.util.Strings; +import org.sufficientlysecure.keychain.Constants; +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 Affirmation { + + protected byte[] mData; + public final long mNonce; + public final URI mSubUri; + final Set<String> mFlags; + final HashMap<String,String> mParams; + + protected Affirmation(byte[] data, long nonce, Set<String> flags, + HashMap<String,String> params, URI subUri) { + mData = data; + mNonce = nonce; + mFlags = flags; + mParams = params; + mSubUri = subUri; + } + + Affirmation(long nonce, Set<String> flags, + HashMap<String,String> params, URI subUri) { + this(null, nonce, flags, params, subUri); + } + + public byte[] encode() { + 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[] data = Strings.toUTF8ByteArray(b.toString()); + + byte[] result = new byte[data.length+4]; + result[0] = (byte) (mNonce >> 24 & 255); + result[1] = (byte) (mNonce >> 16 & 255); + result[2] = (byte) (mNonce >> 8 & 255); + result[3] = (byte) (mNonce & 255); + + System.arraycopy(data, 0, result, 4, result.length); + + return result; + } + + /** This method parses an affirmation from a UserAttributeSubpacket, or returns null if the + * subpacket can not be parsed as a valid affirmation. + */ + public static Affirmation parseAffirmation(UserAttributeSubpacket subpacket) { + if (subpacket.getType() != 100) { + return null; + } + + byte[] data = subpacket.getData(); + + long nonce = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; + + try { + return parseUri(nonce, Strings.fromUTF8ByteArray(Arrays.copyOfRange(data, 4, data.length))); + + } catch (IllegalArgumentException e) { + Log.e(Constants.TAG, "error parsing uri in (suspected) affirmation packet"); + return null; + } + } + + public static Affirmation generateForUri(String uri) { + return parseUri(generateNonce(), uri); + } + + protected static Affirmation parseUri (long nonce, String uriString) { + URI uri = URI.create(uriString); + + if ("pgpid".equals(uri.getScheme())) { + Log.e(Constants.TAG, "unknown uri scheme in (suspected) affirmation packet"); + return null; + } + + if (!uri.isOpaque()) { + Log.e(Constants.TAG, "non-opaque uri in (suspected) affirmation packet"); + return null; + } + + String specific = uri.getSchemeSpecificPart(); + if (!specific.contains("@")) { + Log.e(Constants.TAG, "unknown uri scheme in affirmation 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 Affirmation(null, nonce, flags, params, subUri); + + } + + public static long generateNonce() { + return 1234567890L; // new SecureRandom().nextLong(); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java new file mode 100644 index 000000000..e356ccb8e --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java @@ -0,0 +1,43 @@ +package org.sufficientlysecure.keychain.pgp.affirmation; + +import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource; +import org.sufficientlysecure.keychain.pgp.affirmation.resources.UnknownResource; + +import java.net.URI; +import java.security.SecureRandom; +import java.util.HashMap; +import java.util.Set; + +public abstract class AffirmationResource { + + protected final URI mUri; + protected final Set<String> mFlags; + protected final HashMap<String,String> mParams; + + protected AffirmationResource(Set<String> flags, HashMap<String,String> params, URI uri) { + mFlags = flags; + mParams = params; + mUri = uri; + } + + public abstract boolean verify(); + + public static AffirmationResource findResourceType + (Set<String> flags, HashMap<String,String> params, URI uri) { + + AffirmationResource res; + + res = GenericHttpsResource.create(flags, params, uri); + if (res != null) { + return res; + } + + return new UnknownResource(flags, params, uri); + + } + + public static long generateNonce() { + return 1234567890L; // new SecureRandom().nextLong(); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResouce.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResouce.java new file mode 100644 index 000000000..3e39a695d --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResouce.java @@ -0,0 +1,4 @@ +package org.sufficientlysecure.keychain.pgp.affirmation.resources; + +public class DnsResouce { +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java new file mode 100644 index 000000000..42615d105 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java @@ -0,0 +1,130 @@ +package org.sufficientlysecure.keychain.pgp.affirmation.resources; + +import android.content.Context; + +import com.textuality.keybase.lib.Search; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; +import org.sufficientlysecure.keychain.pgp.Progressable; +import org.sufficientlysecure.keychain.pgp.affirmation.AffirmationResource; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.util.InputData; +import org.sufficientlysecure.keychain.util.Log; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +import javax.net.ssl.HttpsURLConnection; + +public class GenericHttpsResource extends AffirmationResource { + + GenericHttpsResource(Set<String> flags, HashMap<String,String> params, URI uri) { + super(flags, params, uri); + } + + @Override + public boolean verify() { + return false; + } + + public static String generate (byte[] fingerprint, String uri) { + long nonce = generateNonce(); + + StringBuilder b = new StringBuilder(); + b.append("---\r\n"); + + b.append("fingerprint="); + b.append(KeyFormattingUtils.convertFingerprintToHex(fingerprint)); + b.append('\r').append('\n'); + + b.append("nonce="); + b.append(nonce); + b.append('\r').append('\n'); + + if (uri != null) { + b.append("uri="); + b.append(uri); + b.append('\r').append('\n'); + } + b.append("---\r\n"); + + return b.toString(); + } + + public DecryptVerifyResult verify + (Context context, ProviderHelper providerHelper, Progressable progress) + throws IOException { + + byte[] data = fetchResource(mUri).getBytes(); + InputData input = new InputData(new ByteArrayInputStream(data), data.length); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + PgpDecryptVerify.Builder b = + new PgpDecryptVerify.Builder(context, providerHelper, progress, input, out); + PgpDecryptVerify op = b.build(); + + Log.d(Constants.TAG, new String(out.toByteArray())); + + return op.execute(); + } + + protected static String fetchResource (URI uri) throws IOException { + + try { + HttpsURLConnection conn = null; + URL url = uri.toURL(); + int status = 0; + int redirects = 0; + while (redirects < 5) { + conn = (HttpsURLConnection) url.openConnection(); + conn.addRequestProperty("User-Agent", "OpenKeychain"); + conn.setConnectTimeout(5000); + conn.setReadTimeout(25000); + conn.connect(); + status = conn.getResponseCode(); + if (status == 301) { + redirects++; + url = new URL(conn.getHeaderFields().get("Location").get(0)); + } else { + break; + } + } + if (status >= 200 && status < 300) { + return Search.snarf(conn.getInputStream()); + } else { + throw new IOException("Fetch failed, status " + status + ": " + Search.snarf(conn.getErrorStream())); + } + + } catch (MalformedURLException e) { + throw new IOException(e); + } + + } + + public static GenericHttpsResource createNew (URI uri) { + HashSet<String> flags = new HashSet<String>(); + flags.add("generic"); + HashMap<String,String> params = new HashMap<String,String>(); + return create(flags, params, uri); + } + + public static GenericHttpsResource create(Set<String> flags, HashMap<String,String> params, URI uri) { + if ( ! ("https".equals(uri.getScheme()) + && flags != null && flags.size() == 1 && flags.contains("generic") + && (params == null || params.isEmpty()))) { + return null; + } + return new GenericHttpsResource(flags, params, uri); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/TwitterResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/TwitterResource.java new file mode 100644 index 000000000..4fc3590f8 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/TwitterResource.java @@ -0,0 +1,4 @@ +package org.sufficientlysecure.keychain.pgp.affirmation.resources; + +public class TwitterResource { +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/UnknownResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/UnknownResource.java new file mode 100644 index 000000000..e2d050eb4 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/UnknownResource.java @@ -0,0 +1,20 @@ +package org.sufficientlysecure.keychain.pgp.affirmation.resources; + +import org.sufficientlysecure.keychain.pgp.affirmation.AffirmationResource; + +import java.net.URI; +import java.util.HashMap; +import java.util.Set; + +public class UnknownResource extends AffirmationResource { + + public UnknownResource(Set<String> flags, HashMap<String,String> params, URI uri) { + super(flags, params, uri); + } + + @Override + public boolean verify() { + return false; + } + +} |