mirror of
https://github.com/moparisthebest/open-keychain
synced 2025-02-23 23:12:06 -05:00
even more intermediate result
This commit is contained in:
parent
5faeb5f5f0
commit
e0847cafaf
@ -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];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -657,6 +657,22 @@ public abstract class OperationResult implements Parcelable {
|
|||||||
MSG_DEL_CONSOLIDATE (LogLevel.DEBUG, R.string.msg_del_consolidate),
|
MSG_DEL_CONSOLIDATE (LogLevel.DEBUG, R.string.msg_del_consolidate),
|
||||||
MSG_DEL_OK (LogLevel.OK, R.plurals.msg_del_ok),
|
MSG_DEL_OK (LogLevel.OK, R.plurals.msg_del_ok),
|
||||||
MSG_DEL_FAIL (LogLevel.WARN, R.plurals.msg_del_fail),
|
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;
|
public final int mMsgId;
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
package org.sufficientlysecure.keychain.pgp.affirmation;
|
package org.sufficientlysecure.keychain.pgp.affirmation;
|
||||||
|
|
||||||
import org.spongycastle.bcpg.UserAttributeSubpacket;
|
import org.spongycastle.bcpg.UserAttributeSubpacket;
|
||||||
|
import org.spongycastle.util.BigIntegers;
|
||||||
import org.spongycastle.util.Strings;
|
import org.spongycastle.util.Strings;
|
||||||
|
import org.spongycastle.util.encoders.Hex;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.security.SecureRandom;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -16,13 +20,17 @@ import java.util.Set;
|
|||||||
public class Affirmation {
|
public class Affirmation {
|
||||||
|
|
||||||
protected byte[] mData;
|
protected byte[] mData;
|
||||||
public final long mNonce;
|
public final String mNonce;
|
||||||
public final URI mSubUri;
|
public final URI mSubUri;
|
||||||
final Set<String> mFlags;
|
final Set<String> mFlags;
|
||||||
final HashMap<String,String> mParams;
|
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) {
|
HashMap<String,String> params, URI subUri) {
|
||||||
|
if ( ! nonce.matches("[0-9a-zA-Z]+")) {
|
||||||
|
throw new AssertionError("bug: nonce must be hexstring!");
|
||||||
|
}
|
||||||
|
|
||||||
mData = data;
|
mData = data;
|
||||||
mNonce = nonce;
|
mNonce = nonce;
|
||||||
mFlags = flags;
|
mFlags = flags;
|
||||||
@ -30,7 +38,7 @@ public class Affirmation {
|
|||||||
mSubUri = subUri;
|
mSubUri = subUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
Affirmation(long nonce, Set<String> flags,
|
Affirmation(String nonce, Set<String> flags,
|
||||||
HashMap<String,String> params, URI subUri) {
|
HashMap<String,String> params, URI subUri) {
|
||||||
this(null, nonce, flags, params, subUri);
|
this(null, nonce, flags, params, subUri);
|
||||||
}
|
}
|
||||||
@ -72,15 +80,12 @@ public class Affirmation {
|
|||||||
b.append("@");
|
b.append("@");
|
||||||
b.append(mSubUri);
|
b.append(mSubUri);
|
||||||
|
|
||||||
|
byte[] nonceBytes = Hex.decode(mNonce);
|
||||||
byte[] data = Strings.toUTF8ByteArray(b.toString());
|
byte[] data = Strings.toUTF8ByteArray(b.toString());
|
||||||
|
|
||||||
byte[] result = new byte[data.length+4];
|
byte[] result = new byte[data.length+12];
|
||||||
result[0] = (byte) (mNonce >> 24 & 255);
|
System.arraycopy(nonceBytes, 0, result, 0, 12);
|
||||||
result[1] = (byte) (mNonce >> 16 & 255);
|
System.arraycopy(data, 0, result, 12, result.length);
|
||||||
result[2] = (byte) (mNonce >> 8 & 255);
|
|
||||||
result[3] = (byte) (mNonce & 255);
|
|
||||||
|
|
||||||
System.arraycopy(data, 0, result, 4, result.length);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -94,11 +99,10 @@ public class Affirmation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
byte[] data = subpacket.getData();
|
byte[] data = subpacket.getData();
|
||||||
|
String nonce = Hex.toHexString(data, 0, 12);
|
||||||
long nonce = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
|
|
||||||
|
|
||||||
try {
|
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) {
|
} catch (IllegalArgumentException e) {
|
||||||
Log.e(Constants.TAG, "error parsing uri in (suspected) affirmation packet");
|
Log.e(Constants.TAG, "error parsing uri in (suspected) affirmation packet");
|
||||||
@ -106,11 +110,7 @@ public class Affirmation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Affirmation generateForUri(String uri) {
|
protected static Affirmation parseUri (String nonce, String uriString) {
|
||||||
return parseUri(generateNonce(), uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static Affirmation parseUri (long nonce, String uriString) {
|
|
||||||
URI uri = URI.create(uriString);
|
URI uri = URI.create(uriString);
|
||||||
|
|
||||||
if ("pgpid".equals(uri.getScheme())) {
|
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() {
|
public static String generateNonce() {
|
||||||
return 1234567890L; // new SecureRandom().nextLong();
|
// TODO make this actually random
|
||||||
|
// byte[] data = new byte[96];
|
||||||
|
// new SecureRandom().nextBytes(data);
|
||||||
|
// return Hex.toHexString(data);
|
||||||
|
|
||||||
|
// debug for now
|
||||||
|
return "0123456789ABCDEF01234567";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,21 @@
|
|||||||
package org.sufficientlysecure.keychain.pgp.affirmation;
|
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.GenericHttpsResource;
|
||||||
import org.sufficientlysecure.keychain.pgp.affirmation.resources.UnknownResource;
|
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.net.URI;
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public abstract class AffirmationResource {
|
public abstract class AffirmationResource {
|
||||||
|
|
||||||
@ -14,13 +23,73 @@ public abstract class AffirmationResource {
|
|||||||
protected final Set<String> mFlags;
|
protected final Set<String> mFlags;
|
||||||
protected final HashMap<String,String> mParams;
|
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) {
|
protected AffirmationResource(Set<String> flags, HashMap<String,String> params, URI uri) {
|
||||||
mFlags = flags;
|
mFlags = flags;
|
||||||
mParams = params;
|
mParams = params;
|
||||||
mUri = uri;
|
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
|
public static AffirmationResource findResourceType
|
||||||
(Set<String> flags, HashMap<String,String> params, URI uri) {
|
(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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,47 @@
|
|||||||
package org.sufficientlysecure.keychain.pgp.affirmation.resources;
|
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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,14 @@ import android.content.Context;
|
|||||||
import com.textuality.keybase.lib.Search;
|
import com.textuality.keybase.lib.Search;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
|
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.PgpDecryptVerify;
|
||||||
import org.sufficientlysecure.keychain.pgp.Progressable;
|
import org.sufficientlysecure.keychain.pgp.Progressable;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.affirmation.Affirmation;
|
||||||
import org.sufficientlysecure.keychain.pgp.affirmation.AffirmationResource;
|
import org.sufficientlysecure.keychain.pgp.affirmation.AffirmationResource;
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||||
@ -17,7 +22,6 @@ import org.sufficientlysecure.keychain.util.Log;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
@ -33,58 +37,26 @@ public class GenericHttpsResource extends AffirmationResource {
|
|||||||
super(flags, params, uri);
|
super(flags, params, uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String generateText (Context context, byte[] fingerprint, String nonce) {
|
||||||
|
String cookie = AffirmationResource.generate(context, fingerprint, nonce);
|
||||||
|
|
||||||
|
return String.format(context.getResources().getString(R.string.linked_id_generic_text),
|
||||||
|
cookie, "0x" + KeyFormattingUtils.convertFingerprintToHex(fingerprint).substring(24));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean verify() {
|
protected String fetchResource (OperationLog log, int indent) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String generate (byte[] fingerprint, String uri) {
|
log.add(LogType.MSG_LV_FETCH, indent, mUri.toString());
|
||||||
long nonce = generateNonce();
|
indent += 1;
|
||||||
|
|
||||||
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 {
|
try {
|
||||||
|
|
||||||
HttpsURLConnection conn = null;
|
HttpsURLConnection conn = null;
|
||||||
URL url = uri.toURL();
|
URL url = mUri.toURL();
|
||||||
int status = 0;
|
int status = 0;
|
||||||
int redirects = 0;
|
int redirects = 0;
|
||||||
|
|
||||||
while (redirects < 5) {
|
while (redirects < 5) {
|
||||||
conn = (HttpsURLConnection) url.openConnection();
|
conn = (HttpsURLConnection) url.openConnection();
|
||||||
conn.addRequestProperty("User-Agent", "OpenKeychain");
|
conn.addRequestProperty("User-Agent", "OpenKeychain");
|
||||||
@ -95,18 +67,28 @@ public class GenericHttpsResource extends AffirmationResource {
|
|||||||
if (status == 301) {
|
if (status == 301) {
|
||||||
redirects++;
|
redirects++;
|
||||||
url = new URL(conn.getHeaderFields().get("Location").get(0));
|
url = new URL(conn.getHeaderFields().get("Location").get(0));
|
||||||
|
log.add(LogType.MSG_LV_FETCH_REDIR, indent, url.toString());
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status >= 200 && status < 300) {
|
if (status >= 200 && status < 300) {
|
||||||
|
log.add(LogType.MSG_LV_FETCH_OK, indent, Integer.toString(status));
|
||||||
return Search.snarf(conn.getInputStream());
|
return Search.snarf(conn.getInputStream());
|
||||||
} else {
|
} 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) {
|
} 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,119 @@
|
|||||||
package org.sufficientlysecure.keychain.pgp.affirmation.resources;
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package org.sufficientlysecure.keychain.pgp.affirmation.resources;
|
package org.sufficientlysecure.keychain.pgp.affirmation.resources;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
|
||||||
import org.sufficientlysecure.keychain.pgp.affirmation.AffirmationResource;
|
import org.sufficientlysecure.keychain.pgp.affirmation.AffirmationResource;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -13,8 +15,8 @@ public class UnknownResource extends AffirmationResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean verify() {
|
protected String fetchResource(OperationLog log, int indent) {
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,8 @@ package org.sufficientlysecure.keychain.service;
|
|||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.pgp.affirmation.Affi;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
@ -17,11 +17,7 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui.affirmations;
|
package org.sufficientlysecure.keychain.ui.affirmations;
|
||||||
|
|
||||||
import android.app.ProgressDialog;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Message;
|
|
||||||
import android.os.Messenger;
|
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
@ -32,33 +28,16 @@ import android.view.View.OnClickListener;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
|
||||||
import org.openintents.openpgp.util.OpenPgpApi;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
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.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 class AffirmationCreateHttpsStep1Fragment extends Fragment {
|
||||||
|
|
||||||
public static final int REQUEST_CODE_PASSPHRASE = 0x00008001;
|
|
||||||
public static final int REQUEST_CODE_NFC = 0x00008002;
|
|
||||||
|
|
||||||
AffirmationWizard mAffirmationWizard;
|
AffirmationWizard mAffirmationWizard;
|
||||||
|
|
||||||
String mProofUri, mProofText;
|
|
||||||
|
|
||||||
EditText mEditUri;
|
EditText mEditUri;
|
||||||
|
|
||||||
// For NFC data
|
|
||||||
protected String mSigningKeyPassphrase = null;
|
|
||||||
protected Date mNfcTimestamp = null;
|
|
||||||
protected byte[] mNfcHash = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new instance of this fragment
|
* Creates new instance of this fragment
|
||||||
*/
|
*/
|
||||||
@ -93,10 +72,15 @@ public class AffirmationCreateHttpsStep1Fragment extends Fragment {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mProofUri = uri;
|
String proofNonce = Affirmation.generateNonce();
|
||||||
mProofText = GenericHttpsResource.generate(mAffirmationWizard.mFingerprint, null);
|
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;
|
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) {
|
private static boolean checkUri(String uri) {
|
||||||
return Patterns.WEB_URL.matcher(uri).matches();
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,20 +31,18 @@ import android.view.View.OnClickListener;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
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.pgp.affirmation.resources.GenericHttpsResource;
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
|
||||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||||
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
|
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
|
||||||
import org.sufficientlysecure.keychain.util.FileHelper;
|
import org.sufficientlysecure.keychain.util.FileHelper;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
@ -53,25 +51,29 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment {
|
|||||||
|
|
||||||
private static final int REQUEST_CODE_OUTPUT = 0x00007007;
|
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;
|
AffirmationWizard mAffirmationWizard;
|
||||||
|
|
||||||
EditText mEditUri;
|
EditText mEditUri;
|
||||||
ImageView mVerifyImage;
|
ImageView mVerifyImage;
|
||||||
View mVerifyProgress;
|
View mVerifyProgress;
|
||||||
|
TextView mVerifyStatus;
|
||||||
|
|
||||||
String mResourceUri;
|
String mResourceUri;
|
||||||
String mProofString;
|
String mResourceNonce, mResourceString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new instance of this fragment
|
* 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();
|
AffirmationCreateHttpsStep2Fragment frag = new AffirmationCreateHttpsStep2Fragment();
|
||||||
|
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putString(URI, uri);
|
args.putString(URI, uri);
|
||||||
|
args.putString(NONCE, proofNonce);
|
||||||
args.putString(TEXT, proofText);
|
args.putString(TEXT, proofText);
|
||||||
frag.setArguments(args);
|
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);
|
final View view = inflater.inflate(R.layout.affirmation_create_https_fragment_step2, container, false);
|
||||||
|
|
||||||
mResourceUri = getArguments().getString(URI);
|
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() {
|
view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
@ -98,6 +101,7 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment {
|
|||||||
|
|
||||||
mVerifyImage = (ImageView) view.findViewById(R.id.verify_image);
|
mVerifyImage = (ImageView) view.findViewById(R.id.verify_image);
|
||||||
mVerifyProgress = view.findViewById(R.id.verify_progress);
|
mVerifyProgress = view.findViewById(R.id.verify_progress);
|
||||||
|
mVerifyStatus = (TextView) view.findViewById(R.id.verify_status);
|
||||||
|
|
||||||
view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() {
|
view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
@ -124,6 +128,7 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment {
|
|||||||
mEditUri.setText(mResourceUri);
|
mEditUri.setText(mResourceUri);
|
||||||
|
|
||||||
setVerifyProgress(false, null);
|
setVerifyProgress(false, null);
|
||||||
|
mVerifyStatus.setText(R.string.linked_verify_pending);
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
@ -139,14 +144,17 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment {
|
|||||||
mVerifyProgress.setVisibility(on ? View.VISIBLE : View.GONE);
|
mVerifyProgress.setVisibility(on ? View.VISIBLE : View.GONE);
|
||||||
mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE);
|
mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE);
|
||||||
if (success == null) {
|
if (success == null) {
|
||||||
|
mVerifyStatus.setText(R.string.linked_verifying);
|
||||||
mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout);
|
mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout);
|
||||||
mVerifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light),
|
mVerifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light),
|
||||||
PorterDuff.Mode.SRC_IN);
|
PorterDuff.Mode.SRC_IN);
|
||||||
} else if (success) {
|
} else if (success) {
|
||||||
|
mVerifyStatus.setText(R.string.linked_verify_success);
|
||||||
mVerifyImage.setImageResource(R.drawable.status_signature_verified_cutout);
|
mVerifyImage.setImageResource(R.drawable.status_signature_verified_cutout);
|
||||||
mVerifyImage.setColorFilter(getResources().getColor(R.color.android_green_dark),
|
mVerifyImage.setColorFilter(getResources().getColor(R.color.android_green_dark),
|
||||||
PorterDuff.Mode.SRC_IN);
|
PorterDuff.Mode.SRC_IN);
|
||||||
} else {
|
} else {
|
||||||
|
mVerifyStatus.setText(R.string.linked_verify_error);
|
||||||
mVerifyImage.setImageResource(R.drawable.status_signature_unknown_cutout);
|
mVerifyImage.setImageResource(R.drawable.status_signature_unknown_cutout);
|
||||||
mVerifyImage.setColorFilter(getResources().getColor(R.color.android_red_dark),
|
mVerifyImage.setColorFilter(getResources().getColor(R.color.android_red_dark),
|
||||||
PorterDuff.Mode.SRC_IN);
|
PorterDuff.Mode.SRC_IN);
|
||||||
@ -159,33 +167,18 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment {
|
|||||||
try {
|
try {
|
||||||
final GenericHttpsResource resource = GenericHttpsResource.createNew(new URI(mResourceUri));
|
final GenericHttpsResource resource = GenericHttpsResource.createNew(new URI(mResourceUri));
|
||||||
|
|
||||||
new AsyncTask<Void,Void,DecryptVerifyResult>() {
|
new AsyncTask<Void,Void,LinkedVerifyResult>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected DecryptVerifyResult doInBackground(Void... params) {
|
protected LinkedVerifyResult doInBackground(Void... params) {
|
||||||
|
return resource.verify(mAffirmationWizard.mFingerprint, mResourceNonce);
|
||||||
try {
|
|
||||||
return resource.verify(getActivity(), new ProviderHelper(getActivity()), null);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(DecryptVerifyResult result) {
|
protected void onPostExecute(LinkedVerifyResult result) {
|
||||||
super.onPostExecute(result);
|
super.onPostExecute(result);
|
||||||
if (result.success()) {
|
if (result.success()) {
|
||||||
switch (result.getSignatureResult().getStatus()) {
|
|
||||||
case OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED:
|
|
||||||
setVerifyProgress(false, true);
|
setVerifyProgress(false, true);
|
||||||
break;
|
|
||||||
default:
|
|
||||||
setVerifyProgress(false, false);
|
|
||||||
// on error, show error message
|
|
||||||
result.createNotify(getActivity()).show();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
setVerifyProgress(false, false);
|
setVerifyProgress(false, false);
|
||||||
// on error, show error message
|
// on error, show error message
|
||||||
@ -202,7 +195,7 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment {
|
|||||||
private void proofSend() {
|
private void proofSend() {
|
||||||
Intent sendIntent = new Intent();
|
Intent sendIntent = new Intent();
|
||||||
sendIntent.setAction(Intent.ACTION_SEND);
|
sendIntent.setAction(Intent.ACTION_SEND);
|
||||||
sendIntent.putExtra(Intent.EXTRA_TEXT, mProofString);
|
sendIntent.putExtra(Intent.EXTRA_TEXT, mResourceString);
|
||||||
sendIntent.setType("text/plain");
|
sendIntent.setType("text/plain");
|
||||||
startActivity(sendIntent);
|
startActivity(sendIntent);
|
||||||
}
|
}
|
||||||
@ -229,7 +222,7 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment {
|
|||||||
try {
|
try {
|
||||||
PrintWriter out =
|
PrintWriter out =
|
||||||
new PrintWriter(getActivity().getContentResolver().openOutputStream(uri));
|
new PrintWriter(getActivity().getContentResolver().openOutputStream(uri));
|
||||||
out.print(mProofString);
|
out.print(mResourceString);
|
||||||
if (out.checkError()) {
|
if (out.checkError()) {
|
||||||
Notify.showNotify(getActivity(), "Error writing file!", Style.ERROR);
|
Notify.showNotify(getActivity(), "Error writing file!", Style.ERROR);
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,9 @@
|
|||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginLeft="16dip"/>
|
android:layout_marginLeft="16dip"
|
||||||
|
android:layout_marginStart="16dip"
|
||||||
|
/>
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/verify_progress"
|
android:id="@+id/verify_progress"
|
||||||
@ -92,20 +94,24 @@
|
|||||||
android:indeterminateOnly="true"/>
|
android:indeterminateOnly="true"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="fill_parent"
|
android:id="@+id/verify_status"
|
||||||
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
android:text="Not verified"
|
android:text="@string/linked_verify_pending"
|
||||||
android:layout_marginLeft="16dip"
|
android:layout_marginLeft="16dip"
|
||||||
|
android:layout_marginStart="16dip"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:id="@+id/verify_status"/>
|
/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Verify"
|
android:text="Verify"
|
||||||
android:id="@+id/button_verify"
|
android:id="@+id/button_verify"
|
||||||
android:layout_marginRight="16dp"/>
|
android:layout_marginRight="16dp"
|
||||||
|
android:layout_marginEnd="16dip"
|
||||||
|
/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
@ -1013,6 +1013,22 @@
|
|||||||
<item quantity="other">"Failed to delete %d keys"</item>
|
<item quantity="other">"Failed to delete %d keys"</item>
|
||||||
</plurals>
|
</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_acc_saved">"Account saved"</string>
|
||||||
|
|
||||||
<string name="msg_download_success">"Downloaded successfully!"</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_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_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="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>
|
</resources>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user