migrated Keybase verification
This commit is contained in:
@@ -58,12 +58,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
* @see CertifyActionsParcel
|
||||
*
|
||||
*/
|
||||
public class CertifyOperation extends BaseOperation {
|
||||
public class CertifyOperation extends BaseOperation<CertifyActionsParcel> {
|
||||
|
||||
public CertifyOperation(Context context, ProviderHelper providerHelper, Progressable progressable, AtomicBoolean cancelled) {
|
||||
super(context, providerHelper, progressable, cancelled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CertifyResult execute(CertifyActionsParcel parcel, CryptoInputParcel cryptoInput) {
|
||||
|
||||
OperationLog log = new OperationLog();
|
||||
@@ -86,8 +87,10 @@ public class CertifyOperation extends BaseOperation {
|
||||
case PATTERN:
|
||||
case PASSPHRASE:
|
||||
if (!cryptoInput.hasPassphrase()) {
|
||||
return new CertifyResult(log, RequiredInputParcel.createRequiredSignPassphrase(
|
||||
certificationKey.getKeyId(), certificationKey.getKeyId(), null));
|
||||
return new CertifyResult(log,
|
||||
RequiredInputParcel.createRequiredSignPassphrase(
|
||||
certificationKey.getKeyId(), certificationKey.getKeyId(), null)
|
||||
);
|
||||
}
|
||||
// certification is always with the master key id, so use that one
|
||||
passphrase = cryptoInput.getPassphrase();
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
package org.sufficientlysecure.keychain.operations;
|
||||
|
||||
import android.content.Context;
|
||||
import com.textuality.keybase.lib.Proof;
|
||||
import com.textuality.keybase.lib.prover.Prover;
|
||||
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.Data;
|
||||
import de.measite.minidns.record.TXT;
|
||||
import org.json.JSONObject;
|
||||
import org.spongycastle.openpgp.PGPUtil;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.KeybaseVerificationResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
|
||||
import org.sufficientlysecure.keychain.pgp.Progressable;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.KeybaseVerificationParcel;
|
||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class KeybaseVerificationOperation extends BaseOperation<KeybaseVerificationParcel> {
|
||||
|
||||
public KeybaseVerificationOperation(Context context, ProviderHelper providerHelper,
|
||||
Progressable progressable) {
|
||||
super(context, providerHelper, progressable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeybaseVerificationResult execute(KeybaseVerificationParcel keybaseInput,
|
||||
CryptoInputParcel cryptoInput) {
|
||||
|
||||
String requiredFingerprint = keybaseInput.mRequiredFingerprint;
|
||||
|
||||
OperationResult.OperationLog log = new OperationResult.OperationLog();
|
||||
log.add(OperationResult.LogType.MSG_KEYBASE_VERIFICATION, 0, requiredFingerprint);
|
||||
|
||||
try {
|
||||
String keybaseProof = keybaseInput.mKeybaseProof;
|
||||
Proof proof = new Proof(new JSONObject(keybaseProof));
|
||||
mProgressable.setProgress(R.string.keybase_message_fetching_data, 0, 100);
|
||||
|
||||
Prover prover = Prover.findProverFor(proof);
|
||||
|
||||
if (prover == null) {
|
||||
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_NO_PROVER, 1,
|
||||
proof.getPrettyName());
|
||||
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
|
||||
}
|
||||
|
||||
if (!prover.fetchProofData()) {
|
||||
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_FETCH_PROOF, 1);
|
||||
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
|
||||
}
|
||||
|
||||
if (!prover.checkFingerprint(requiredFingerprint)) {
|
||||
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_FINGERPRINT_MISMATCH, 1);
|
||||
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
|
||||
}
|
||||
|
||||
String domain = prover.dnsTxtCheckRequired();
|
||||
if (domain != null) {
|
||||
DNSMessage dnsQuery = new Client().query(new Question(domain, Record.TYPE.TXT));
|
||||
if (dnsQuery == null) {
|
||||
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_DNS_FAIL, 1);
|
||||
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_SPECIFIC, 2,
|
||||
getFlattenedProverLog(prover));
|
||||
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
|
||||
}
|
||||
Record[] records = dnsQuery.getAnswers();
|
||||
List<List<byte[]>> extents = new ArrayList<List<byte[]>>();
|
||||
for (Record r : records) {
|
||||
Data d = r.getPayload();
|
||||
if (d instanceof TXT) {
|
||||
extents.add(((TXT) d).getExtents());
|
||||
}
|
||||
}
|
||||
if (!prover.checkDnsTxt(extents)) {
|
||||
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_SPECIFIC, 1,
|
||||
getFlattenedProverLog(prover));
|
||||
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] messageBytes = prover.getPgpMessage().getBytes();
|
||||
if (prover.rawMessageCheckRequired()) {
|
||||
InputStream messageByteStream = PGPUtil.getDecoderStream(new
|
||||
ByteArrayInputStream
|
||||
(messageBytes));
|
||||
if (!prover.checkRawMessageBytes(messageByteStream)) {
|
||||
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_SPECIFIC, 1,
|
||||
getFlattenedProverLog(prover));
|
||||
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
|
||||
}
|
||||
}
|
||||
|
||||
PgpDecryptVerify op = new PgpDecryptVerify(mContext, mProviderHelper, mProgressable);
|
||||
|
||||
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(messageBytes)
|
||||
.setSignedLiteralData(true)
|
||||
.setRequiredSignerFingerprint(requiredFingerprint);
|
||||
|
||||
DecryptVerifyResult decryptVerifyResult = op.execute(input, new CryptoInputParcel());
|
||||
|
||||
if (!decryptVerifyResult.success()) {
|
||||
log.add(decryptVerifyResult, 1);
|
||||
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
|
||||
}
|
||||
|
||||
if (!prover.validate(new String(decryptVerifyResult.getOutputBytes()))) {
|
||||
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_PAYLOAD_MISMATCH, 1);
|
||||
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
|
||||
}
|
||||
|
||||
return new KeybaseVerificationResult(OperationResult.RESULT_OK, log, prover);
|
||||
} catch (Exception e) {
|
||||
// just adds the passed parameter, in this case e.getMessage()
|
||||
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_SPECIFIC, 1, e.getMessage());
|
||||
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
|
||||
}
|
||||
}
|
||||
|
||||
private String getFlattenedProverLog(Prover prover) {
|
||||
String log = "";
|
||||
for (String line : prover.getLog()) {
|
||||
log += line + "\n";
|
||||
}
|
||||
return log;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package org.sufficientlysecure.keychain.operations.results;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import com.textuality.keybase.lib.KeybaseException;
|
||||
import com.textuality.keybase.lib.prover.Prover;
|
||||
|
||||
public class KeybaseVerificationResult extends OperationResult implements Parcelable {
|
||||
public final String mProofUrl;
|
||||
public final String mPresenceUrl;
|
||||
public final String mPresenceLabel;
|
||||
|
||||
public KeybaseVerificationResult(int result, OperationLog log) {
|
||||
super(result, log);
|
||||
mProofUrl = null;
|
||||
mPresenceLabel = null;
|
||||
mPresenceUrl = null;
|
||||
}
|
||||
|
||||
public KeybaseVerificationResult(int result, OperationLog log, Prover prover)
|
||||
throws KeybaseException {
|
||||
super(result, log);
|
||||
mProofUrl = prover.getProofUrl();
|
||||
mPresenceUrl = prover.getPresenceUrl();
|
||||
mPresenceLabel = prover.getPresenceLabel();
|
||||
}
|
||||
|
||||
protected KeybaseVerificationResult(Parcel in) {
|
||||
super(in);
|
||||
mProofUrl = in.readString();
|
||||
mPresenceUrl = in.readString();
|
||||
mPresenceLabel = in.readString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeString(mProofUrl);
|
||||
dest.writeString(mPresenceUrl);
|
||||
dest.writeString(mPresenceLabel);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<KeybaseVerificationResult> CREATOR = new Parcelable.Creator<KeybaseVerificationResult>() {
|
||||
@Override
|
||||
public KeybaseVerificationResult createFromParcel(Parcel in) {
|
||||
return new KeybaseVerificationResult(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeybaseVerificationResult[] newArray(int size) {
|
||||
return new KeybaseVerificationResult[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -750,7 +750,19 @@ public abstract class OperationResult implements Parcelable {
|
||||
MSG_DEL_OK (LogLevel.OK, R.plurals.msg_del_ok),
|
||||
MSG_DEL_FAIL (LogLevel.WARN, R.plurals.msg_del_fail),
|
||||
|
||||
//export log
|
||||
// keybase verification
|
||||
MSG_KEYBASE_VERIFICATION(LogLevel.START, R.string.msg_keybase_verification),
|
||||
|
||||
MSG_KEYBASE_ERROR_NO_PROVER(LogLevel.ERROR, R.string.msg_keybase_error_no_prover),
|
||||
MSG_KEYBASE_ERROR_FETCH_PROOF(LogLevel.ERROR, R.string.msg_keybase_error_fetching_evidence),
|
||||
MSG_KEYBASE_ERROR_FINGERPRINT_MISMATCH(LogLevel.ERROR,
|
||||
R.string.msg_keybase_error_key_mismatch),
|
||||
MSG_KEYBASE_ERROR_DNS_FAIL(LogLevel.ERROR, R.string.msg_keybase_error_dns_fail),
|
||||
MSG_KEYBASE_ERROR_SPECIFIC(LogLevel.ERROR, R.string.msg_keybase_error_specific),
|
||||
MSG_KEYBASE_ERROR_PAYLOAD_MISMATCH(LogLevel.ERROR,
|
||||
R.string.msg_keybase_error_msg_payload_mismatch),
|
||||
|
||||
// export log
|
||||
MSG_EXPORT_LOG(LogLevel.START,R.string.msg_export_log_start),
|
||||
MSG_EXPORT_LOG_EXPORT_ERROR_NO_FILE(LogLevel.ERROR,R.string.msg_export_log_error_no_file),
|
||||
MSG_EXPORT_LOG_EXPORT_ERROR_FOPEN(LogLevel.ERROR,R.string.msg_export_log_error_fopen),
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.sufficientlysecure.keychain.service;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class KeybaseVerificationParcel implements Parcelable {
|
||||
|
||||
public String mKeybaseProof;
|
||||
public String mRequiredFingerprint;
|
||||
|
||||
public KeybaseVerificationParcel(String keybaseProof, String requiredFingerprint) {
|
||||
mKeybaseProof = keybaseProof;
|
||||
mRequiredFingerprint = requiredFingerprint;
|
||||
}
|
||||
|
||||
protected KeybaseVerificationParcel(Parcel in) {
|
||||
mKeybaseProof = in.readString();
|
||||
mRequiredFingerprint = in.readString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(mKeybaseProof);
|
||||
dest.writeString(mRequiredFingerprint);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<KeybaseVerificationParcel> CREATOR = new Parcelable.Creator<KeybaseVerificationParcel>() {
|
||||
@Override
|
||||
public KeybaseVerificationParcel createFromParcel(Parcel in) {
|
||||
return new KeybaseVerificationParcel(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeybaseVerificationParcel[] newArray(int size) {
|
||||
return new KeybaseVerificationParcel[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -106,19 +106,23 @@ public class KeychainNewService extends Service implements Progressable {
|
||||
mActionCanceled);
|
||||
} else if (inputParcel instanceof DeleteKeyringParcel) {
|
||||
op = new DeleteOperation(outerThis, new ProviderHelper(outerThis), outerThis);
|
||||
} else if (inputParcel instanceof PromoteKeyringParcel){
|
||||
} else if (inputParcel instanceof PromoteKeyringParcel) {
|
||||
op = new PromoteKeyOperation(outerThis, new ProviderHelper(outerThis),
|
||||
outerThis, mActionCanceled);
|
||||
} else if (inputParcel instanceof ImportKeyringParcel
|
||||
|| inputParcel instanceof ExportKeyringParcel){
|
||||
|| inputParcel instanceof ExportKeyringParcel) {
|
||||
op = new ImportExportOperation(outerThis, new ProviderHelper(outerThis),
|
||||
outerThis, mActionCanceled);
|
||||
} else if (inputParcel instanceof ConsolidateInputParcel) {
|
||||
op = new ConsolidateOperation(outerThis, new ProviderHelper(outerThis),
|
||||
outerThis);
|
||||
} else if (inputParcel instanceof KeybaseVerificationParcel) {
|
||||
op = new KeybaseVerificationOperation(outerThis, new ProviderHelper(outerThis),
|
||||
outerThis);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked") // this is unchecked, we make sure it's the correct op above!
|
||||
OperationResult result = op.execute(inputParcel, cryptoInput);
|
||||
sendMessageToHandler(MessageStatus.OKAY, result);
|
||||
|
||||
@@ -38,14 +38,8 @@ import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
|
||||
import org.sufficientlysecure.keychain.keyimport.Keyserver;
|
||||
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
||||
import org.sufficientlysecure.keychain.operations.*;
|
||||
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.DeleteResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.ExportResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.*;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
|
||||
import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
|
||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
|
||||
@@ -61,14 +55,6 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
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.Data;
|
||||
import de.measite.minidns.record.TXT;
|
||||
|
||||
/**
|
||||
* This Service contains all important long lasting operations for OpenKeychain. It receives Intents with
|
||||
@@ -80,28 +66,8 @@ public class KeychainService extends Service implements Progressable {
|
||||
public static final String EXTRA_MESSENGER = "messenger";
|
||||
public static final String EXTRA_DATA = "data";
|
||||
|
||||
/* possible actions */
|
||||
|
||||
public static final String ACTION_VERIFY_KEYBASE_PROOF = Constants.INTENT_PREFIX + "VERIFY_KEYBASE_PROOF";
|
||||
|
||||
public static final String ACTION_CONSOLIDATE = Constants.INTENT_PREFIX + "CONSOLIDATE";
|
||||
|
||||
public static final String ACTION_CANCEL = Constants.INTENT_PREFIX + "CANCEL";
|
||||
|
||||
/* keys for data bundle */
|
||||
|
||||
// keybase proof
|
||||
public static final String KEYBASE_REQUIRED_FINGERPRINT = "keybase_required_fingerprint";
|
||||
public static final String KEYBASE_PROOF = "keybase_proof";
|
||||
|
||||
// consolidate
|
||||
public static final String CONSOLIDATE_RECOVERY = "consolidate_recovery";
|
||||
|
||||
Messenger mMessenger;
|
||||
|
||||
// this attribute can possibly merged with the one above? not sure...
|
||||
private AtomicBoolean mActionCanceled = new AtomicBoolean(false);
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
@@ -112,150 +78,6 @@ public class KeychainService extends Service implements Progressable {
|
||||
*/
|
||||
@Override
|
||||
public int onStartCommand(final Intent intent, int flags, int startId) {
|
||||
|
||||
if (ACTION_CANCEL.equals(intent.getAction())) {
|
||||
mActionCanceled.set(true);
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
Runnable actionRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// We have not been cancelled! (yet)
|
||||
mActionCanceled.set(false);
|
||||
|
||||
Bundle extras = intent.getExtras();
|
||||
if (extras == null) {
|
||||
Log.e(Constants.TAG, "Extras bundle is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(extras.containsKey(EXTRA_MESSENGER) || extras.containsKey(EXTRA_DATA) || (intent
|
||||
.getAction() == null))) {
|
||||
Log.e(Constants.TAG,
|
||||
"Extra bundle must contain a messenger, a data bundle, and an action!");
|
||||
return;
|
||||
}
|
||||
|
||||
Uri dataUri = intent.getData();
|
||||
|
||||
mMessenger = (Messenger) extras.get(EXTRA_MESSENGER);
|
||||
Bundle data = extras.getBundle(EXTRA_DATA);
|
||||
if (data == null) {
|
||||
Log.e(Constants.TAG, "data extra is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.logDebugBundle(data, "EXTRA_DATA");
|
||||
|
||||
ProviderHelper providerHelper = new ProviderHelper(KeychainService.this);
|
||||
|
||||
String action = intent.getAction();
|
||||
|
||||
// executeServiceMethod action from extra bundle
|
||||
switch (action) {
|
||||
case ACTION_VERIFY_KEYBASE_PROOF: {
|
||||
|
||||
try {
|
||||
Proof proof = new Proof(new JSONObject(data.getString(KEYBASE_PROOF)));
|
||||
setProgress(R.string.keybase_message_fetching_data, 0, 100);
|
||||
|
||||
Prover prover = Prover.findProverFor(proof);
|
||||
|
||||
if (prover == null) {
|
||||
sendProofError(getString(R.string.keybase_no_prover_found) + ": " + proof
|
||||
.getPrettyName());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!prover.fetchProofData()) {
|
||||
sendProofError(prover.getLog(), getString(R.string.keybase_problem_fetching_evidence));
|
||||
return;
|
||||
}
|
||||
String requiredFingerprint = data.getString(KEYBASE_REQUIRED_FINGERPRINT);
|
||||
if (!prover.checkFingerprint(requiredFingerprint)) {
|
||||
sendProofError(getString(R.string.keybase_key_mismatch));
|
||||
return;
|
||||
}
|
||||
|
||||
String domain = prover.dnsTxtCheckRequired();
|
||||
if (domain != null) {
|
||||
DNSMessage dnsQuery = new Client().query(new Question(domain, Record.TYPE.TXT));
|
||||
if (dnsQuery == null) {
|
||||
sendProofError(prover.getLog(), getString(R.string.keybase_dns_query_failure));
|
||||
return;
|
||||
}
|
||||
Record[] records = dnsQuery.getAnswers();
|
||||
List<List<byte[]>> extents = new ArrayList<List<byte[]>>();
|
||||
for (Record r : records) {
|
||||
Data d = r.getPayload();
|
||||
if (d instanceof TXT) {
|
||||
extents.add(((TXT) d).getExtents());
|
||||
}
|
||||
}
|
||||
if (!prover.checkDnsTxt(extents)) {
|
||||
sendProofError(prover.getLog(), null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] messageBytes = prover.getPgpMessage().getBytes();
|
||||
if (prover.rawMessageCheckRequired()) {
|
||||
InputStream messageByteStream = PGPUtil.getDecoderStream(new ByteArrayInputStream
|
||||
(messageBytes));
|
||||
if (!prover.checkRawMessageBytes(messageByteStream)) {
|
||||
sendProofError(prover.getLog(), null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PgpDecryptVerify op = new PgpDecryptVerify(KeychainService.this, providerHelper,
|
||||
KeychainService.this);
|
||||
|
||||
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(messageBytes)
|
||||
.setSignedLiteralData(true)
|
||||
.setRequiredSignerFingerprint(requiredFingerprint);
|
||||
|
||||
DecryptVerifyResult decryptVerifyResult = op.execute(input, new CryptoInputParcel());
|
||||
|
||||
if (!decryptVerifyResult.success()) {
|
||||
OperationLog log = decryptVerifyResult.getLog();
|
||||
OperationResult.LogEntryParcel lastEntry = null;
|
||||
for (OperationResult.LogEntryParcel entry : log) {
|
||||
lastEntry = entry;
|
||||
}
|
||||
sendProofError(getString(lastEntry.mType.getMsgId()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!prover.validate(new String(decryptVerifyResult.getOutputBytes()))) {
|
||||
sendProofError(getString(R.string.keybase_message_payload_mismatch));
|
||||
return;
|
||||
}
|
||||
|
||||
Bundle resultData = new Bundle();
|
||||
resultData.putString(ServiceProgressHandler.DATA_MESSAGE, "OK");
|
||||
|
||||
// these help the handler construct a useful human-readable message
|
||||
resultData.putString(ServiceProgressHandler.KEYBASE_PROOF_URL, prover.getProofUrl());
|
||||
resultData.putString(ServiceProgressHandler.KEYBASE_PRESENCE_URL, prover.getPresenceUrl());
|
||||
resultData.putString(ServiceProgressHandler.KEYBASE_PRESENCE_LABEL, prover
|
||||
.getPresenceLabel());
|
||||
sendMessageToHandler(MessageStatus.OKAY, resultData);
|
||||
} catch (Exception e) {
|
||||
sendErrorToHandler(e);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
stopSelf();
|
||||
}
|
||||
};
|
||||
|
||||
Thread actionThread = new Thread(actionRunnable);
|
||||
actionThread.start();
|
||||
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,9 +48,12 @@ import com.textuality.keybase.lib.User;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.operations.results.KeybaseVerificationResult;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||
import org.sufficientlysecure.keychain.service.KeybaseVerificationParcel;
|
||||
import org.sufficientlysecure.keychain.service.KeychainService;
|
||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
@@ -59,7 +62,8 @@ import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
|
||||
public class ViewKeyTrustFragment extends LoaderFragment implements
|
||||
LoaderManager.LoaderCallbacks<Cursor> {
|
||||
LoaderManager.LoaderCallbacks<Cursor>,
|
||||
CryptoOperationHelper.Callback<KeybaseVerificationParcel, KeybaseVerificationResult> {
|
||||
|
||||
public static final String ARG_DATA_URI = "uri";
|
||||
|
||||
@@ -76,6 +80,14 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
|
||||
// for retrieving the key we’re working on
|
||||
private Uri mDataUri;
|
||||
|
||||
private Proof mProof;
|
||||
|
||||
// for CryptoOperationHelper,Callback
|
||||
private String mKeybaseProof;
|
||||
private String mKeybaseFingerprint;
|
||||
private CryptoOperationHelper<KeybaseVerificationParcel, KeybaseVerificationResult>
|
||||
mKeybaseOpHelper;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
|
||||
View root = super.onCreateView(inflater, superContainer, savedInstanceState);
|
||||
@@ -349,112 +361,125 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
|
||||
}
|
||||
|
||||
private void verify(final Proof proof, final String fingerprint) {
|
||||
Intent intent = new Intent(getActivity(), KeychainService.class);
|
||||
Bundle data = new Bundle();
|
||||
intent.setAction(KeychainService.ACTION_VERIFY_KEYBASE_PROOF);
|
||||
|
||||
data.putString(KeychainService.KEYBASE_PROOF, proof.toString());
|
||||
data.putString(KeychainService.KEYBASE_REQUIRED_FINGERPRINT, fingerprint);
|
||||
intent.putExtra(KeychainService.EXTRA_DATA, data);
|
||||
mProof = proof;
|
||||
mKeybaseProof = proof.toString();
|
||||
mKeybaseFingerprint = fingerprint;
|
||||
|
||||
mProofVerifyDetail.setVisibility(View.GONE);
|
||||
|
||||
// Create a new Messenger for the communication back after proof work is done
|
||||
ServiceProgressHandler handler = new ServiceProgressHandler(getActivity()) {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
// handle messages by standard KeychainIntentServiceHandler first
|
||||
super.handleMessage(message);
|
||||
mKeybaseOpHelper = new CryptoOperationHelper<>(this, this,
|
||||
R.string.progress_verifying_signature);
|
||||
mKeybaseOpHelper.cryptoOperation();
|
||||
}
|
||||
|
||||
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
||||
Bundle returnData = message.getData();
|
||||
String msg = returnData.getString(ServiceProgressHandler.DATA_MESSAGE);
|
||||
SpannableStringBuilder ssb = new SpannableStringBuilder();
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (mKeybaseOpHelper != null) {
|
||||
mKeybaseOpHelper.handleActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
if ((msg != null) && msg.equals("OK")) {
|
||||
// CryptoOperationHelper.Callback methods
|
||||
@Override
|
||||
public KeybaseVerificationParcel createOperationInput() {
|
||||
return new KeybaseVerificationParcel(mKeybaseProof, mKeybaseFingerprint);
|
||||
}
|
||||
|
||||
//yay
|
||||
String proofUrl = returnData.getString(ServiceProgressHandler.KEYBASE_PROOF_URL);
|
||||
String presenceUrl = returnData.getString(ServiceProgressHandler.KEYBASE_PRESENCE_URL);
|
||||
String presenceLabel = returnData.getString(ServiceProgressHandler.KEYBASE_PRESENCE_LABEL);
|
||||
@Override
|
||||
public void onCryptoOperationSuccess(KeybaseVerificationResult result) {
|
||||
|
||||
String proofLabel;
|
||||
switch (proof.getType()) {
|
||||
case Proof.PROOF_TYPE_TWITTER:
|
||||
proofLabel = getString(R.string.keybase_twitter_proof);
|
||||
break;
|
||||
case Proof.PROOF_TYPE_DNS:
|
||||
proofLabel = getString(R.string.keybase_dns_proof);
|
||||
break;
|
||||
case Proof.PROOF_TYPE_WEB_SITE:
|
||||
proofLabel = getString(R.string.keybase_web_site_proof);
|
||||
break;
|
||||
case Proof.PROOF_TYPE_GITHUB:
|
||||
proofLabel = getString(R.string.keybase_github_proof);
|
||||
break;
|
||||
case Proof.PROOF_TYPE_REDDIT:
|
||||
proofLabel = getString(R.string.keybase_reddit_proof);
|
||||
break;
|
||||
default:
|
||||
proofLabel = getString(R.string.keybase_a_post);
|
||||
break;
|
||||
}
|
||||
result.createNotify(getActivity()).show();
|
||||
|
||||
ssb.append(getString(R.string.keybase_proof_succeeded));
|
||||
StyleSpan bold = new StyleSpan(Typeface.BOLD);
|
||||
ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
ssb.append("\n\n");
|
||||
int length = ssb.length();
|
||||
ssb.append(proofLabel);
|
||||
if (proofUrl != null) {
|
||||
URLSpan postLink = new URLSpan(proofUrl);
|
||||
ssb.setSpan(postLink, length, length + proofLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
if (Proof.PROOF_TYPE_DNS == proof.getType()) {
|
||||
ssb.append(" ").append(getString(R.string.keybase_for_the_domain)).append(" ");
|
||||
} else {
|
||||
ssb.append(" ").append(getString(R.string.keybase_fetched_from)).append(" ");
|
||||
}
|
||||
length = ssb.length();
|
||||
URLSpan presenceLink = new URLSpan(presenceUrl);
|
||||
ssb.append(presenceLabel);
|
||||
ssb.setSpan(presenceLink, length, length + presenceLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
if (Proof.PROOF_TYPE_REDDIT == proof.getType()) {
|
||||
ssb.append(", ").
|
||||
append(getString(R.string.keybase_reddit_attribution)).
|
||||
append(" “").append(proof.getHandle()).append("”, ");
|
||||
}
|
||||
ssb.append(" ").append(getString(R.string.keybase_contained_signature));
|
||||
} else {
|
||||
// verification failed!
|
||||
msg = returnData.getString(ServiceProgressHandler.DATA_ERROR);
|
||||
ssb.append(getString(R.string.keybase_proof_failure));
|
||||
if (msg == null) {
|
||||
msg = getString(R.string.keybase_unknown_proof_failure);
|
||||
}
|
||||
StyleSpan bold = new StyleSpan(Typeface.BOLD);
|
||||
ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
ssb.append("\n\n").append(msg);
|
||||
}
|
||||
mProofVerifyHeader.setVisibility(View.VISIBLE);
|
||||
mProofVerifyDetail.setVisibility(View.VISIBLE);
|
||||
mProofVerifyDetail.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
mProofVerifyDetail.setText(ssb);
|
||||
}
|
||||
}
|
||||
};
|
||||
String proofUrl = result.mProofUrl;
|
||||
String presenceUrl = result.mPresenceUrl;
|
||||
String presenceLabel = result.mPresenceLabel;
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(handler);
|
||||
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
|
||||
Proof proof = mProof; // TODO: should ideally be contained in result
|
||||
|
||||
// show progress dialog
|
||||
handler.showProgressDialog(
|
||||
getString(R.string.progress_verifying_signature),
|
||||
ProgressDialog.STYLE_HORIZONTAL, false
|
||||
);
|
||||
String proofLabel;
|
||||
switch (proof.getType()) {
|
||||
case Proof.PROOF_TYPE_TWITTER:
|
||||
proofLabel = getString(R.string.keybase_twitter_proof);
|
||||
break;
|
||||
case Proof.PROOF_TYPE_DNS:
|
||||
proofLabel = getString(R.string.keybase_dns_proof);
|
||||
break;
|
||||
case Proof.PROOF_TYPE_WEB_SITE:
|
||||
proofLabel = getString(R.string.keybase_web_site_proof);
|
||||
break;
|
||||
case Proof.PROOF_TYPE_GITHUB:
|
||||
proofLabel = getString(R.string.keybase_github_proof);
|
||||
break;
|
||||
case Proof.PROOF_TYPE_REDDIT:
|
||||
proofLabel = getString(R.string.keybase_reddit_proof);
|
||||
break;
|
||||
default:
|
||||
proofLabel = getString(R.string.keybase_a_post);
|
||||
break;
|
||||
}
|
||||
|
||||
// start service with intent
|
||||
getActivity().startService(intent);
|
||||
SpannableStringBuilder ssb = new SpannableStringBuilder();
|
||||
|
||||
ssb.append(getString(R.string.keybase_proof_succeeded));
|
||||
StyleSpan bold = new StyleSpan(Typeface.BOLD);
|
||||
ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
ssb.append("\n\n");
|
||||
int length = ssb.length();
|
||||
ssb.append(proofLabel);
|
||||
if (proofUrl != null) {
|
||||
URLSpan postLink = new URLSpan(proofUrl);
|
||||
ssb.setSpan(postLink, length, length + proofLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
if (Proof.PROOF_TYPE_DNS == proof.getType()) {
|
||||
ssb.append(" ").append(getString(R.string.keybase_for_the_domain)).append(" ");
|
||||
} else {
|
||||
ssb.append(" ").append(getString(R.string.keybase_fetched_from)).append(" ");
|
||||
}
|
||||
length = ssb.length();
|
||||
URLSpan presenceLink = new URLSpan(presenceUrl);
|
||||
ssb.append(presenceLabel);
|
||||
ssb.setSpan(presenceLink, length, length + presenceLabel.length(), Spanned
|
||||
.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
if (Proof.PROOF_TYPE_REDDIT == proof.getType()) {
|
||||
ssb.append(", ").
|
||||
append(getString(R.string.keybase_reddit_attribution)).
|
||||
append(" “").append(proof.getHandle()).append("”, ");
|
||||
}
|
||||
ssb.append(" ").append(getString(R.string.keybase_contained_signature));
|
||||
|
||||
displaySpannableResult(ssb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationCancelled() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationError(KeybaseVerificationResult result) {
|
||||
|
||||
result.createNotify(getActivity()).show();
|
||||
|
||||
SpannableStringBuilder ssb = new SpannableStringBuilder();
|
||||
|
||||
ssb.append(getString(R.string.keybase_proof_failure));
|
||||
String msg = getString(result.getLog().getLast().mType.mMsgId);
|
||||
if (msg == null) {
|
||||
msg = getString(R.string.keybase_unknown_proof_failure);
|
||||
}
|
||||
StyleSpan bold = new StyleSpan(Typeface.BOLD);
|
||||
ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
ssb.append("\n\n").append(msg);
|
||||
|
||||
displaySpannableResult(ssb);
|
||||
}
|
||||
|
||||
private void displaySpannableResult(SpannableStringBuilder ssb) {
|
||||
mProofVerifyHeader.setVisibility(View.VISIBLE);
|
||||
mProofVerifyDetail.setVisibility(View.VISIBLE);
|
||||
mProofVerifyDetail.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
mProofVerifyDetail.setText(ssb);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user