aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain
diff options
context:
space:
mode:
authorVincent Breitmoser <valodim@mugenguild.com>2015-01-12 19:27:46 +0100
committerVincent Breitmoser <valodim@mugenguild.com>2015-01-12 19:27:46 +0100
commite0847cafaf53eac9b364343c1f5e74554b51053d (patch)
treec1ac24aeca5a3d090be8d1773f710e2e4ecdc0fd /OpenKeychain
parent5faeb5f5f060e049000e804deca5445d281f8611 (diff)
downloadopen-keychain-e0847cafaf53eac9b364343c1f5e74554b51053d.tar.gz
open-keychain-e0847cafaf53eac9b364343c1f5e74554b51053d.tar.bz2
open-keychain-e0847cafaf53eac9b364343c1f5e74554b51053d.zip
even more intermediate result
Diffstat (limited to 'OpenKeychain')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/LinkedVerifyResult.java63
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java16
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/Affirmation.java48
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java77
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResouce.java45
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java74
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/TwitterResource.java117
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/UnknownResource.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java168
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java53
-rw-r--r--OpenKeychain/src/main/res/layout/affirmation_create_https_fragment_step2.xml16
-rw-r--r--OpenKeychain/src/main/res/values/strings.xml21
13 files changed, 435 insertions, 271 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/LinkedVerifyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/LinkedVerifyResult.java
new file mode 100644
index 000000000..2dae38cc4
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/LinkedVerifyResult.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 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.operations.results;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.View;
+
+import com.github.johnpersano.supertoasts.SuperCardToast;
+import com.github.johnpersano.supertoasts.SuperToast;
+import com.github.johnpersano.supertoasts.SuperToast.Duration;
+import com.github.johnpersano.supertoasts.util.OnClickWrapper;
+import com.github.johnpersano.supertoasts.util.Style;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
+import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
+
+public class LinkedVerifyResult extends OperationResult {
+
+ public LinkedVerifyResult(int result, OperationLog log) {
+ super(result, log);
+ }
+
+ /** Construct from a parcel - trivial because we have no extra data. */
+ public LinkedVerifyResult(Parcel source) {
+ super(source);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ }
+
+ public static Creator<LinkedVerifyResult> CREATOR = new Creator<LinkedVerifyResult>() {
+ public LinkedVerifyResult createFromParcel(final Parcel source) {
+ return new LinkedVerifyResult(source);
+ }
+
+ public LinkedVerifyResult[] newArray(final int size) {
+ return new LinkedVerifyResult[size];
+ }
+ };
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
index 1388c0eac..c2f5757f8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
@@ -657,6 +657,22 @@ public abstract class OperationResult implements Parcelable {
MSG_DEL_CONSOLIDATE (LogLevel.DEBUG, R.string.msg_del_consolidate),
MSG_DEL_OK (LogLevel.OK, R.plurals.msg_del_ok),
MSG_DEL_FAIL (LogLevel.WARN, R.plurals.msg_del_fail),
+
+ MSG_LV (LogLevel.START, R.string.msg_lv),
+ MSG_LV_MATCH (LogLevel.DEBUG, R.string.msg_lv_match),
+ MSG_LV_MATCH_ERROR (LogLevel.ERROR, R.string.msg_lv_match_error),
+ MSG_LV_FP_OK (LogLevel.DEBUG, R.string.msg_lv_fp_ok),
+ MSG_LV_FP_ERROR (LogLevel.ERROR, R.string.msg_lv_fp_error),
+ MSG_LV_NONCE_OK (LogLevel.OK, R.string.msg_lv_nonce_ok),
+ MSG_LV_NONCE_ERROR (LogLevel.ERROR, R.string.msg_lv_nonce_error),
+
+ MSG_LV_FETCH (LogLevel.DEBUG, R.string.msg_lv_fetch),
+ MSG_LV_FETCH_REDIR (LogLevel.DEBUG, R.string.msg_lv_fetch_redir),
+ MSG_LV_FETCH_OK (LogLevel.DEBUG, R.string.msg_lv_fetch_ok),
+ MSG_LV_FETCH_ERROR (LogLevel.ERROR, R.string.msg_lv_fetch_error),
+ MSG_LV_FETCH_ERROR_URL (LogLevel.ERROR, R.string.msg_lv_fetch_error_url),
+ MSG_LV_FETCH_ERROR_IO (LogLevel.ERROR, R.string.msg_lv_fetch_error_io),
+
;
public final int mMsgId;
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
index f12ebd481..892231cbe 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/Affirmation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/Affirmation.java
@@ -1,11 +1,15 @@
package org.sufficientlysecure.keychain.pgp.affirmation;
import org.spongycastle.bcpg.UserAttributeSubpacket;
+import org.spongycastle.util.BigIntegers;
import org.spongycastle.util.Strings;
+import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.Log;
+import java.math.BigInteger;
import java.net.URI;
+import java.security.SecureRandom;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
@@ -16,13 +20,17 @@ import java.util.Set;
public class Affirmation {
protected byte[] mData;
- public final long mNonce;
+ public final String mNonce;
public final URI mSubUri;
final Set<String> mFlags;
final HashMap<String,String> mParams;
- protected Affirmation(byte[] data, long nonce, Set<String> flags,
+ protected Affirmation(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;
@@ -30,7 +38,7 @@ public class Affirmation {
mSubUri = subUri;
}
- Affirmation(long nonce, Set<String> flags,
+ Affirmation(String nonce, Set<String> flags,
HashMap<String,String> params, URI subUri) {
this(null, nonce, flags, params, subUri);
}
@@ -72,15 +80,12 @@ public class Affirmation {
b.append("@");
b.append(mSubUri);
+ byte[] nonceBytes = Hex.decode(mNonce);
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);
+ byte[] result = new byte[data.length+12];
+ System.arraycopy(nonceBytes, 0, result, 0, 12);
+ System.arraycopy(data, 0, result, 12, result.length);
return result;
}
@@ -94,11 +99,10 @@ public class Affirmation {
}
byte[] data = subpacket.getData();
-
- long nonce = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
+ String nonce = Hex.toHexString(data, 0, 12);
try {
- return parseUri(nonce, Strings.fromUTF8ByteArray(Arrays.copyOfRange(data, 4, data.length)));
+ return parseUri(nonce, Strings.fromUTF8ByteArray(Arrays.copyOfRange(data, 12, data.length)));
} catch (IllegalArgumentException e) {
Log.e(Constants.TAG, "error parsing uri in (suspected) affirmation packet");
@@ -106,11 +110,7 @@ public class Affirmation {
}
}
- public static Affirmation generateForUri(String uri) {
- return parseUri(generateNonce(), uri);
- }
-
- protected static Affirmation parseUri (long nonce, String uriString) {
+ protected static Affirmation parseUri (String nonce, String uriString) {
URI uri = URI.create(uriString);
if ("pgpid".equals(uri.getScheme())) {
@@ -146,12 +146,18 @@ public class Affirmation {
}
}
- return new Affirmation(null, nonce, flags, params, subUri);
+ return new Affirmation(nonce, flags, params, subUri);
}
- public static long generateNonce() {
- return 1234567890L; // new SecureRandom().nextLong();
+ public static String generateNonce() {
+ // TODO make this actually random
+ // byte[] data = new byte[96];
+ // new SecureRandom().nextBytes(data);
+ // return Hex.toHexString(data);
+
+ // debug for now
+ return "0123456789ABCDEF01234567";
}
}
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
index e356ccb8e..45919a89a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java
@@ -1,12 +1,21 @@
package org.sufficientlysecure.keychain.pgp.affirmation;
+import android.content.Context;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource;
import org.sufficientlysecure.keychain.pgp.affirmation.resources.UnknownResource;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.util.Log;
import java.net.URI;
-import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
public abstract class AffirmationResource {
@@ -14,13 +23,73 @@ public abstract class AffirmationResource {
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]+)\\]");
+
protected AffirmationResource(Set<String> flags, HashMap<String,String> params, URI uri) {
mFlags = flags;
mParams = params;
mUri = uri;
}
- public abstract boolean verify();
+ public static String generate (Context context, byte[] fingerprint, String nonce) {
+
+ return "[Verifying my PGP key: pgpid+cookie:"
+ + KeyFormattingUtils.convertFingerprintToHex(fingerprint) + "#" + nonce + "]";
+
+ }
+
+ public LinkedVerifyResult verify(byte[] fingerprint, String nonce) {
+
+ OperationLog log = new OperationLog();
+ log.add(LogType.MSG_LV, 0);
+
+ // Try to fetch resource. Logs for itself
+ String res = fetchResource(log, 1);
+ if (res == null) {
+ // if this is null, an error was recorded in fetchResource above
+ return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log);
+ }
+
+ Log.d(Constants.TAG, res);
+
+ return verifyString(log, 1, res, nonce, fingerprint);
+
+ }
+
+ protected abstract String fetchResource (OperationLog log, int indent);
+
+ protected LinkedVerifyResult verifyString (OperationLog log, int indent,
+ String res,
+ String nonce, byte[] fingerprint) {
+
+ log.add(LogType.MSG_LV_MATCH, indent);
+ Matcher match = magicPattern.matcher(res);
+ if (!match.find()) {
+ log.add(LogType.MSG_LV_MATCH_ERROR, 2);
+ return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log);
+ }
+
+ String candidateFp = match.group(1);
+ String nonceCandidate = match.group(2);
+
+ String fp = KeyFormattingUtils.convertFingerprintToHex(fingerprint);
+
+ if (!fp.equals(candidateFp)) {
+ log.add(LogType.MSG_LV_FP_ERROR, indent);
+ return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log);
+ }
+ log.add(LogType.MSG_LV_FP_OK, indent);
+
+ if (!nonce.equals(nonceCandidate)) {
+ log.add(LogType.MSG_LV_NONCE_ERROR, indent);
+ return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log);
+ }
+
+ log.add(LogType.MSG_LV_NONCE_OK, indent);
+ return new LinkedVerifyResult(LinkedVerifyResult.RESULT_OK, log);
+
+ }
public static AffirmationResource findResourceType
(Set<String> flags, HashMap<String,String> params, URI uri) {
@@ -36,8 +105,4 @@ public abstract class AffirmationResource {
}
- 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
index 3e39a695d..20216972a 100644
--- 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
@@ -1,4 +1,47 @@
package org.sufficientlysecure.keychain.pgp.affirmation.resources;
-public class DnsResouce {
+import android.content.Context;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
+import org.sufficientlysecure.keychain.pgp.affirmation.AffirmationResource;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Set;
+
+import de.measite.minidns.Client;
+import de.measite.minidns.DNSMessage;
+import de.measite.minidns.Question;
+import de.measite.minidns.Record;
+import de.measite.minidns.Record.TYPE;
+import de.measite.minidns.record.TXT;
+
+public class DnsResouce extends AffirmationResource {
+
+ DnsResouce(Set<String> flags, HashMap<String,String> params, URI uri) {
+ super(flags, params, uri);
+ }
+
+ public static String generate (Context context, byte[] fingerprint, String nonce) {
+
+ return "pgpid+cookie:"
+ + KeyFormattingUtils.convertFingerprintToHex(fingerprint) + ";" + nonce + "";
+
+ }
+
+ @Override
+ protected String fetchResource (OperationLog log, int indent) {
+
+ Client c = new Client();
+ DNSMessage msg = c.query(new Question("mugenguild.com", TYPE.TXT));
+ Record aw = msg.getAnswers()[0];
+ TXT txt = (TXT) aw.getPayload();
+ Log.d(Constants.TAG, txt.getText());
+ return txt.getText();
+
+ }
+
}
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
index 42615d105..c8c3cbb4d 100644
--- 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
@@ -5,9 +5,14 @@ import android.content.Context;
import com.textuality.keybase.lib.Search;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
+import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.Progressable;
+import org.sufficientlysecure.keychain.pgp.affirmation.Affirmation;
import org.sufficientlysecure.keychain.pgp.affirmation.AffirmationResource;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
@@ -17,7 +22,6 @@ 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;
@@ -33,58 +37,26 @@ public class GenericHttpsResource extends AffirmationResource {
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');
+ public static String generateText (Context context, byte[] fingerprint, String nonce) {
+ String cookie = AffirmationResource.generate(context, fingerprint, nonce);
- 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();
+ return String.format(context.getResources().getString(R.string.linked_id_generic_text),
+ cookie, "0x" + KeyFormattingUtils.convertFingerprintToHex(fingerprint).substring(24));
}
- 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();
- }
+ @Override
+ protected String fetchResource (OperationLog log, int indent) {
- protected static String fetchResource (URI uri) throws IOException {
+ log.add(LogType.MSG_LV_FETCH, indent, mUri.toString());
+ indent += 1;
try {
+
HttpsURLConnection conn = null;
- URL url = uri.toURL();
+ URL url = mUri.toURL();
int status = 0;
int redirects = 0;
+
while (redirects < 5) {
conn = (HttpsURLConnection) url.openConnection();
conn.addRequestProperty("User-Agent", "OpenKeychain");
@@ -95,18 +67,28 @@ public class GenericHttpsResource extends AffirmationResource {
if (status == 301) {
redirects++;
url = new URL(conn.getHeaderFields().get("Location").get(0));
+ log.add(LogType.MSG_LV_FETCH_REDIR, indent, url.toString());
} else {
break;
}
}
+
if (status >= 200 && status < 300) {
+ log.add(LogType.MSG_LV_FETCH_OK, indent, Integer.toString(status));
return Search.snarf(conn.getInputStream());
} else {
- throw new IOException("Fetch failed, status " + status + ": " + Search.snarf(conn.getErrorStream()));
+ // log verbose output to logcat
+ Log.e(Constants.TAG, Search.snarf(conn.getErrorStream()));
+ log.add(LogType.MSG_LV_FETCH_ERROR, indent, Integer.toString(status));
+ return null;
}
} catch (MalformedURLException e) {
- throw new IOException(e);
+ log.add(LogType.MSG_LV_FETCH_ERROR_URL, indent);
+ return null;
+ } catch (IOException e) {
+ log.add(LogType.MSG_LV_FETCH_ERROR_IO, indent);
+ return null;
}
}
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
index 4fc3590f8..b426c16b9 100644
--- 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
@@ -1,4 +1,119 @@
package org.sufficientlysecure.keychain.pgp.affirmation.resources;
-public class TwitterResource {
+import android.util.Base64;
+import android.util.JsonReader;
+
+import com.textuality.keybase.lib.JWalk;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+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.affirmation.AffirmationResource;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.Set;
+
+public class TwitterResource extends AffirmationResource {
+
+ TwitterResource(Set<String> flags, HashMap<String,String> params, URI uri) {
+ super(flags, params, uri);
+ }
+
+ private String getTwitterStream(String screenName) {
+ String results = null;
+
+ // Step 1: Encode consumer key and secret
+ try {
+ // URL encode the consumer key and secret
+ String urlApiKey = URLEncoder.encode("6IhPnWbYxASAoAzH2QaUtHD0J", "UTF-8");
+ String urlApiSecret = URLEncoder.encode("L0GnuiOnapWbSBbQtLIqtpeS5BTtvh06dmoMoKQfHQS8UwHuWm", "UTF-8");
+
+ // Concatenate the encoded consumer key, a colon character, and the
+ // encoded consumer secret
+ String combined = urlApiKey + ":" + urlApiSecret;
+
+ // Base64 encode the string
+ String base64Encoded = Base64.encodeToString(combined.getBytes(), Base64.NO_WRAP);
+
+ // Step 2: Obtain a bearer token
+ HttpPost httpPost = new HttpPost("https://api.twitter.com/oauth2/token");
+ httpPost.setHeader("Authorization", "Basic " + base64Encoded);
+ httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
+ httpPost.setEntity(new StringEntity("grant_type=client_credentials"));
+ JSONObject rawAuthorization = new JSONObject(getResponseBody(httpPost));
+ String auth = JWalk.getString(rawAuthorization, "access_token");
+
+ // Applications should verify that the value associated with the
+ // token_type key of the returned object is bearer
+ if (auth != null && JWalk.getString(rawAuthorization, "token_type").equals("bearer")) {
+
+ // Step 3: Authenticate API requests with bearer token
+ HttpGet httpGet =
+ new HttpGet("https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=" + screenName);
+
+ // construct a normal HTTPS request and include an Authorization
+ // header with the value of Bearer <>
+ httpGet.setHeader("Authorization", "Bearer " + auth);
+ httpGet.setHeader("Content-Type", "application/json");
+ // update the results with the body of the response
+ results = getResponseBody(httpGet);
+ }
+ } catch (UnsupportedEncodingException ex) {
+ } catch (JSONException ex) {
+ } catch (IllegalStateException ex1) {
+ }
+ return results;
+ }
+
+ private static String getResponseBody(HttpRequestBase request) {
+ StringBuilder sb = new StringBuilder();
+ try {
+
+ DefaultHttpClient httpClient = new DefaultHttpClient(new BasicHttpParams());
+ HttpResponse response = httpClient.execute(request);
+ int statusCode = response.getStatusLine().getStatusCode();
+ String reason = response.getStatusLine().getReasonPhrase();
+
+ if (statusCode == 200) {
+
+ HttpEntity entity = response.getEntity();
+ InputStream inputStream = entity.getContent();
+
+ BufferedReader bReader = new BufferedReader(
+ new InputStreamReader(inputStream, "UTF-8"), 8);
+ String line = null;
+ while ((line = bReader.readLine()) != null) {
+ sb.append(line);
+ }
+ } else {
+ sb.append(reason);
+ }
+ } catch (UnsupportedEncodingException ex) {
+ } catch (ClientProtocolException ex1) {
+ } catch (IOException ex2) {
+ }
+ return sb.toString();
+ }
+
+ @Override
+ protected String fetchResource(OperationLog log, int indent) {
+ return getTwitterStream("Valodim");
+ }
+
}
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
index e2d050eb4..2f67c948e 100644
--- 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
@@ -1,7 +1,9 @@
package org.sufficientlysecure.keychain.pgp.affirmation.resources;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.pgp.affirmation.AffirmationResource;
+import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.Set;
@@ -13,8 +15,8 @@ public class UnknownResource extends AffirmationResource {
}
@Override
- public boolean verify() {
- return false;
+ protected String fetchResource(OperationLog log, int indent) {
+ return null;
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
index 810190fee..5e953ec1e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
@@ -21,6 +21,8 @@ package org.sufficientlysecure.keychain.service;
import android.os.Parcel;
import android.os.Parcelable;
+import org.sufficientlysecure.keychain.pgp.affirmation.Affi;
+
import java.io.Serializable;
import java.util.ArrayList;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java
index 30cfe9a79..818008bb0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java
@@ -17,11 +17,7 @@
package org.sufficientlysecure.keychain.ui.affirmations;
-import android.app.ProgressDialog;
-import android.content.Intent;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.TextWatcher;
@@ -32,33 +28,16 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.EditText;
-import org.openintents.openpgp.util.OpenPgpApi;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
+import org.sufficientlysecure.keychain.pgp.affirmation.Affirmation;
import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-import org.sufficientlysecure.keychain.ui.NfcActivity;
-import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
-
-import java.util.Date;
public class AffirmationCreateHttpsStep1Fragment extends Fragment {
- public static final int REQUEST_CODE_PASSPHRASE = 0x00008001;
- public static final int REQUEST_CODE_NFC = 0x00008002;
-
AffirmationWizard mAffirmationWizard;
- String mProofUri, mProofText;
-
EditText mEditUri;
- // For NFC data
- protected String mSigningKeyPassphrase = null;
- protected Date mNfcTimestamp = null;
- protected byte[] mNfcHash = null;
-
/**
* Creates new instance of this fragment
*/
@@ -93,10 +72,15 @@ public class AffirmationCreateHttpsStep1Fragment extends Fragment {
return;
}
- mProofUri = uri;
- mProofText = GenericHttpsResource.generate(mAffirmationWizard.mFingerprint, null);
+ String proofNonce = Affirmation.generateNonce();
+ String proofText = GenericHttpsResource.generateText(getActivity(),
+ mAffirmationWizard.mFingerprint, proofNonce);
+
+ AffirmationCreateHttpsStep2Fragment frag =
+ AffirmationCreateHttpsStep2Fragment.newInstance(uri, proofNonce, proofText);
+
+ mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT);
- generateResourceAndNext();
}
});
@@ -134,142 +118,8 @@ public class AffirmationCreateHttpsStep1Fragment extends Fragment {
return view;
}
- public void generateResourceAndNext () {
-
- // Send all information needed to service to edit key in other thread
- Intent intent = new Intent(getActivity(), KeychainIntentService.class);
- intent.setAction(KeychainIntentService.ACTION_SIGN_ENCRYPT);
- intent.putExtra(KeychainIntentService.EXTRA_DATA, createEncryptBundle());
-
- // Message is received after encrypting is done in KeychainIntentService
- KeychainIntentServiceHandler serviceHandler = new KeychainIntentServiceHandler(
- mAffirmationWizard, getString(R.string.progress_encrypting),
- ProgressDialog.STYLE_HORIZONTAL) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- SignEncryptResult pgpResult =
- message.getData().getParcelable(SignEncryptResult.EXTRA_RESULT);
-
- if (pgpResult.isPending()) {
- if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_PASSPHRASE) ==
- SignEncryptResult.RESULT_PENDING_PASSPHRASE) {
- startPassphraseDialog(pgpResult.getKeyIdPassphraseNeeded());
- } else if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_NFC) ==
- SignEncryptResult.RESULT_PENDING_NFC) {
-
- mNfcTimestamp = pgpResult.getNfcTimestamp();
- startNfcSign(pgpResult.getNfcKeyId(), pgpResult.getNfcPassphrase(), pgpResult.getNfcHash(), pgpResult.getNfcAlgo());
- } else {
- throw new RuntimeException("Unhandled pending result!");
- }
- return;
- }
-
- if (pgpResult.success()) {
- String proofText = new String(
- message.getData().getByteArray(KeychainIntentService.RESULT_BYTES));
-
- AffirmationCreateHttpsStep2Fragment frag =
- AffirmationCreateHttpsStep2Fragment.newInstance(mProofUri, proofText);
-
- mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT);
- } else {
- pgpResult.createNotify(getActivity()).show();
- }
-
- // no matter the result, reset parameters
- mSigningKeyPassphrase = null;
- mNfcHash = null;
- mNfcTimestamp = null;
- }
- }
- };
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(serviceHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- serviceHandler.showProgressDialog(getActivity());
-
- // start service with intent
- getActivity().startService(intent);
-
- }
-
- protected Bundle createEncryptBundle() {
- // fill values for this action
- Bundle data = new Bundle();
-
- data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_BYTES);
- data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_BYTES);
- data.putByteArray(KeychainIntentService.ENCRYPT_MESSAGE_BYTES, mProofText.getBytes());
-
- // Always use armor for messages
- data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, true);
-
- data.putLong(KeychainIntentService.ENCRYPT_SIGNATURE_MASTER_ID, mAffirmationWizard.mMasterKeyId);
- data.putString(KeychainIntentService.ENCRYPT_SIGNATURE_KEY_PASSPHRASE, mSigningKeyPassphrase);
- data.putSerializable(KeychainIntentService.ENCRYPT_SIGNATURE_NFC_TIMESTAMP, mNfcTimestamp);
- data.putByteArray(KeychainIntentService.ENCRYPT_SIGNATURE_NFC_HASH, mNfcHash);
-
- return data;
- }
-
- protected void startPassphraseDialog(long subkeyId) {
- Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class);
- intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, subkeyId);
- startActivityForResult(intent, REQUEST_CODE_PASSPHRASE);
- }
-
- protected void startNfcSign(long keyId, String pin, byte[] hashToSign, int hashAlgo) {
- // build PendingIntent for Yubikey NFC operations
- Intent intent = new Intent(getActivity(), NfcActivity.class);
- intent.setAction(NfcActivity.ACTION_SIGN_HASH);
-
- // pass params through to activity that it can be returned again later to repeat pgp operation
- intent.putExtra(NfcActivity.EXTRA_DATA, new Intent()); // not used, only relevant to OpenPgpService
- intent.putExtra(NfcActivity.EXTRA_KEY_ID, keyId);
- intent.putExtra(NfcActivity.EXTRA_PIN, pin);
- intent.putExtra(NfcActivity.EXTRA_NFC_HASH_TO_SIGN, hashToSign);
- intent.putExtra(NfcActivity.EXTRA_NFC_HASH_ALGO, hashAlgo);
-
- startActivityForResult(intent, REQUEST_CODE_NFC);
- }
-
private static boolean checkUri(String uri) {
return Patterns.WEB_URL.matcher(uri).matches();
}
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case REQUEST_CODE_PASSPHRASE: {
- if (resultCode == AffirmationWizard.RESULT_OK && data != null) {
- mSigningKeyPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
- generateResourceAndNext();
- return;
- }
- break;
- }
-
- case REQUEST_CODE_NFC: {
- if (resultCode == AffirmationWizard.RESULT_OK && data != null) {
- mNfcHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH);
- generateResourceAndNext();
- return;
- }
- break;
- }
-
- default: {
- super.onActivityResult(requestCode, resultCode, data);
- break;
- }
- }
- }
-
-
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java
index ee7ba1a77..55a6be40c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java
@@ -31,20 +31,18 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ImageView;
+import android.widget.TextView;
-import org.openintents.openpgp.OpenPgpSignatureResult;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
+import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult;
import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.util.FileHelper;
import java.io.File;
import java.io.FileNotFoundException;
-import java.io.IOException;
import java.io.PrintWriter;
import java.net.URI;
import java.net.URISyntaxException;
@@ -53,25 +51,29 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment {
private static final int REQUEST_CODE_OUTPUT = 0x00007007;
- public static final String URI = "uri", TEXT = "text";
+ public static final String URI = "uri", NONCE = "nonce", TEXT = "text";
AffirmationWizard mAffirmationWizard;
EditText mEditUri;
ImageView mVerifyImage;
View mVerifyProgress;
+ TextView mVerifyStatus;
String mResourceUri;
- String mProofString;
+ String mResourceNonce, mResourceString;
/**
* Creates new instance of this fragment
*/
- public static AffirmationCreateHttpsStep2Fragment newInstance(String uri, String proofText) {
+ public static AffirmationCreateHttpsStep2Fragment newInstance
+ (String uri, String proofNonce, String proofText) {
+
AffirmationCreateHttpsStep2Fragment frag = new AffirmationCreateHttpsStep2Fragment();
Bundle args = new Bundle();
args.putString(URI, uri);
+ args.putString(NONCE, proofNonce);
args.putString(TEXT, proofText);
frag.setArguments(args);
@@ -83,7 +85,8 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment {
final View view = inflater.inflate(R.layout.affirmation_create_https_fragment_step2, container, false);
mResourceUri = getArguments().getString(URI);
- mProofString = getArguments().getString(TEXT);
+ mResourceNonce = getArguments().getString(NONCE);
+ mResourceString = getArguments().getString(TEXT);
view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() {
@Override
@@ -98,6 +101,7 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment {
mVerifyImage = (ImageView) view.findViewById(R.id.verify_image);
mVerifyProgress = view.findViewById(R.id.verify_progress);
+ mVerifyStatus = (TextView) view.findViewById(R.id.verify_status);
view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() {
@Override
@@ -124,6 +128,7 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment {
mEditUri.setText(mResourceUri);
setVerifyProgress(false, null);
+ mVerifyStatus.setText(R.string.linked_verify_pending);
return view;
}
@@ -139,14 +144,17 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment {
mVerifyProgress.setVisibility(on ? View.VISIBLE : View.GONE);
mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE);
if (success == null) {
+ mVerifyStatus.setText(R.string.linked_verifying);
mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout);
mVerifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light),
PorterDuff.Mode.SRC_IN);
} else if (success) {
+ mVerifyStatus.setText(R.string.linked_verify_success);
mVerifyImage.setImageResource(R.drawable.status_signature_verified_cutout);
mVerifyImage.setColorFilter(getResources().getColor(R.color.android_green_dark),
PorterDuff.Mode.SRC_IN);
} else {
+ mVerifyStatus.setText(R.string.linked_verify_error);
mVerifyImage.setImageResource(R.drawable.status_signature_unknown_cutout);
mVerifyImage.setColorFilter(getResources().getColor(R.color.android_red_dark),
PorterDuff.Mode.SRC_IN);
@@ -159,33 +167,18 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment {
try {
final GenericHttpsResource resource = GenericHttpsResource.createNew(new URI(mResourceUri));
- new AsyncTask<Void,Void,DecryptVerifyResult>() {
+ new AsyncTask<Void,Void,LinkedVerifyResult>() {
@Override
- protected DecryptVerifyResult doInBackground(Void... params) {
-
- try {
- return resource.verify(getActivity(), new ProviderHelper(getActivity()), null);
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- return null;
+ protected LinkedVerifyResult doInBackground(Void... params) {
+ return resource.verify(mAffirmationWizard.mFingerprint, mResourceNonce);
}
@Override
- protected void onPostExecute(DecryptVerifyResult result) {
+ protected void onPostExecute(LinkedVerifyResult result) {
super.onPostExecute(result);
if (result.success()) {
- switch (result.getSignatureResult().getStatus()) {
- case OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED:
- setVerifyProgress(false, true);
- break;
- default:
- setVerifyProgress(false, false);
- // on error, show error message
- result.createNotify(getActivity()).show();
- }
+ setVerifyProgress(false, true);
} else {
setVerifyProgress(false, false);
// on error, show error message
@@ -202,7 +195,7 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment {
private void proofSend() {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
- sendIntent.putExtra(Intent.EXTRA_TEXT, mProofString);
+ sendIntent.putExtra(Intent.EXTRA_TEXT, mResourceString);
sendIntent.setType("text/plain");
startActivity(sendIntent);
}
@@ -229,7 +222,7 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment {
try {
PrintWriter out =
new PrintWriter(getActivity().getContentResolver().openOutputStream(uri));
- out.print(mProofString);
+ out.print(mResourceString);
if (out.checkError()) {
Notify.showNotify(getActivity(), "Error writing file!", Style.ERROR);
}
diff --git a/OpenKeychain/src/main/res/layout/affirmation_create_https_fragment_step2.xml b/OpenKeychain/src/main/res/layout/affirmation_create_https_fragment_step2.xml
index 50a0d6514..68555fa92 100644
--- a/OpenKeychain/src/main/res/layout/affirmation_create_https_fragment_step2.xml
+++ b/OpenKeychain/src/main/res/layout/affirmation_create_https_fragment_step2.xml
@@ -82,7 +82,9 @@
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginLeft="16dip"/>
+ android:layout_marginLeft="16dip"
+ android:layout_marginStart="16dip"
+ />
<ProgressBar
android:id="@+id/verify_progress"
@@ -92,20 +94,24 @@
android:indeterminateOnly="true"/>
<TextView
- android:layout_width="fill_parent"
+ android:id="@+id/verify_status"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
- android:text="Not verified"
+ android:text="@string/linked_verify_pending"
android:layout_marginLeft="16dip"
+ android:layout_marginStart="16dip"
android:layout_weight="1"
- android:id="@+id/verify_status"/>
+ />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Verify"
android:id="@+id/button_verify"
- android:layout_marginRight="16dp"/>
+ android:layout_marginRight="16dp"
+ android:layout_marginEnd="16dip"
+ />
</LinearLayout>
diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml
index b6b7cdd0f..55bb35f3f 100644
--- a/OpenKeychain/src/main/res/values/strings.xml
+++ b/OpenKeychain/src/main/res/values/strings.xml
@@ -1013,6 +1013,22 @@
<item quantity="other">"Failed to delete %d keys"</item>
</plurals>
+ <!-- Linked identity verification -->
+ <string name="msg_lv">"Verifying linked identity…"</string>
+ <string name="msg_lv_match">"Searching for cookie"</string>
+ <string name="msg_lv_match_error">"No cookie found in resource!"</string>
+ <string name="msg_lv_fp_ok">"Fingerprint ok."</string>
+ <string name="msg_lv_fp_error">"Fingerprint mismatch!"</string>
+ <string name="msg_lv_nonce_ok">"Link verified!"</string>
+ <string name="msg_lv_nonce_error">"Nonce mismatch!"</string>
+
+ <string name="msg_lv_fetch">"Fetching URI '%s'"</string>
+ <string name="msg_lv_fetch_redir">"Following redirect to '%s'"</string>
+ <string name="msg_lv_fetch_ok">"Successfully fetched (HTTP %s)"</string>
+ <string name="msg_lv_fetch_error">"Server error (HTTP %s)"</string>
+ <string name="msg_lv_fetch_error_url">"URL is malformed!"</string>
+ <string name="msg_lv_fetch_error_io">"IO Error!"</string>
+
<string name="msg_acc_saved">"Account saved"</string>
<string name="msg_download_success">"Downloaded successfully!"</string>
@@ -1108,5 +1124,10 @@
<string name="aff_create_https_2_2">"For the next step, you should save and upload this file."</string>
<string name="aff_create_https_2_3">"Make sure the file is reachable at the correct URI, then verify your setup."</string>
<string name="aff_create_https_2_4">"After verification is successful, hit next to finish the process."</string>
+ <string name="linked_id_generic_text">"This file claims ownership of the OpenPGP key with long id %2$s.\n\nCookie for proof:\n%1$s"</string>
+ <string name="linked_verifying">Verifying…</string>
+ <string name="linked_verify_success">Verification successful!</string>
+ <string name="linked_verify_error">Veriication error!</string>
+ <string name="linked_verify_pending">Not yet verified</string>
</resources>