migrated Keybase verification
This commit is contained in:
@@ -58,12 +58,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
* @see CertifyActionsParcel
|
* @see CertifyActionsParcel
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class CertifyOperation extends BaseOperation {
|
public class CertifyOperation extends BaseOperation<CertifyActionsParcel> {
|
||||||
|
|
||||||
public CertifyOperation(Context context, ProviderHelper providerHelper, Progressable progressable, AtomicBoolean cancelled) {
|
public CertifyOperation(Context context, ProviderHelper providerHelper, Progressable progressable, AtomicBoolean cancelled) {
|
||||||
super(context, providerHelper, progressable, cancelled);
|
super(context, providerHelper, progressable, cancelled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public CertifyResult execute(CertifyActionsParcel parcel, CryptoInputParcel cryptoInput) {
|
public CertifyResult execute(CertifyActionsParcel parcel, CryptoInputParcel cryptoInput) {
|
||||||
|
|
||||||
OperationLog log = new OperationLog();
|
OperationLog log = new OperationLog();
|
||||||
@@ -86,8 +87,10 @@ public class CertifyOperation extends BaseOperation {
|
|||||||
case PATTERN:
|
case PATTERN:
|
||||||
case PASSPHRASE:
|
case PASSPHRASE:
|
||||||
if (!cryptoInput.hasPassphrase()) {
|
if (!cryptoInput.hasPassphrase()) {
|
||||||
return new CertifyResult(log, RequiredInputParcel.createRequiredSignPassphrase(
|
return new CertifyResult(log,
|
||||||
certificationKey.getKeyId(), certificationKey.getKeyId(), null));
|
RequiredInputParcel.createRequiredSignPassphrase(
|
||||||
|
certificationKey.getKeyId(), certificationKey.getKeyId(), null)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// certification is always with the master key id, so use that one
|
// certification is always with the master key id, so use that one
|
||||||
passphrase = cryptoInput.getPassphrase();
|
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_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),
|
||||||
|
|
||||||
//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(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_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),
|
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);
|
mActionCanceled);
|
||||||
} else if (inputParcel instanceof DeleteKeyringParcel) {
|
} else if (inputParcel instanceof DeleteKeyringParcel) {
|
||||||
op = new DeleteOperation(outerThis, new ProviderHelper(outerThis), outerThis);
|
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),
|
op = new PromoteKeyOperation(outerThis, new ProviderHelper(outerThis),
|
||||||
outerThis, mActionCanceled);
|
outerThis, mActionCanceled);
|
||||||
} else if (inputParcel instanceof ImportKeyringParcel
|
} else if (inputParcel instanceof ImportKeyringParcel
|
||||||
|| inputParcel instanceof ExportKeyringParcel){
|
|| inputParcel instanceof ExportKeyringParcel) {
|
||||||
op = new ImportExportOperation(outerThis, new ProviderHelper(outerThis),
|
op = new ImportExportOperation(outerThis, new ProviderHelper(outerThis),
|
||||||
outerThis, mActionCanceled);
|
outerThis, mActionCanceled);
|
||||||
} else if (inputParcel instanceof ConsolidateInputParcel) {
|
} else if (inputParcel instanceof ConsolidateInputParcel) {
|
||||||
op = new ConsolidateOperation(outerThis, new ProviderHelper(outerThis),
|
op = new ConsolidateOperation(outerThis, new ProviderHelper(outerThis),
|
||||||
outerThis);
|
outerThis);
|
||||||
|
} else if (inputParcel instanceof KeybaseVerificationParcel) {
|
||||||
|
op = new KeybaseVerificationOperation(outerThis, new ProviderHelper(outerThis),
|
||||||
|
outerThis);
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked") // this is unchecked, we make sure it's the correct op above!
|
@SuppressWarnings("unchecked") // this is unchecked, we make sure it's the correct op above!
|
||||||
OperationResult result = op.execute(inputParcel, cryptoInput);
|
OperationResult result = op.execute(inputParcel, cryptoInput);
|
||||||
sendMessageToHandler(MessageStatus.OKAY, result);
|
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.Keyserver;
|
||||||
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
||||||
import org.sufficientlysecure.keychain.operations.*;
|
import org.sufficientlysecure.keychain.operations.*;
|
||||||
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
|
import org.sufficientlysecure.keychain.operations.results.*;
|
||||||
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.OperationResult.OperationLog;
|
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.CanonicalizedPublicKeyRing;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
|
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
|
||||||
@@ -61,14 +55,6 @@ import java.io.ByteArrayInputStream;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
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
|
* 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_MESSENGER = "messenger";
|
||||||
public static final String EXTRA_DATA = "data";
|
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;
|
Messenger mMessenger;
|
||||||
|
|
||||||
// this attribute can possibly merged with the one above? not sure...
|
|
||||||
private AtomicBoolean mActionCanceled = new AtomicBoolean(false);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBinder onBind(Intent intent) {
|
public IBinder onBind(Intent intent) {
|
||||||
return null;
|
return null;
|
||||||
@@ -112,150 +78,6 @@ public class KeychainService extends Service implements Progressable {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int onStartCommand(final Intent intent, int flags, int startId) {
|
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;
|
return START_NOT_STICKY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,9 +48,12 @@ import com.textuality.keybase.lib.User;
|
|||||||
|
|
||||||
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.KeybaseVerificationResult;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||||
|
import org.sufficientlysecure.keychain.service.KeybaseVerificationParcel;
|
||||||
import org.sufficientlysecure.keychain.service.KeychainService;
|
import org.sufficientlysecure.keychain.service.KeychainService;
|
||||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||||
|
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
@@ -59,7 +62,8 @@ import java.util.Hashtable;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class ViewKeyTrustFragment extends LoaderFragment implements
|
public class ViewKeyTrustFragment extends LoaderFragment implements
|
||||||
LoaderManager.LoaderCallbacks<Cursor> {
|
LoaderManager.LoaderCallbacks<Cursor>,
|
||||||
|
CryptoOperationHelper.Callback<KeybaseVerificationParcel, KeybaseVerificationResult> {
|
||||||
|
|
||||||
public static final String ARG_DATA_URI = "uri";
|
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
|
// for retrieving the key we’re working on
|
||||||
private Uri mDataUri;
|
private Uri mDataUri;
|
||||||
|
|
||||||
|
private Proof mProof;
|
||||||
|
|
||||||
|
// for CryptoOperationHelper,Callback
|
||||||
|
private String mKeybaseProof;
|
||||||
|
private String mKeybaseFingerprint;
|
||||||
|
private CryptoOperationHelper<KeybaseVerificationParcel, KeybaseVerificationResult>
|
||||||
|
mKeybaseOpHelper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
|
||||||
View root = super.onCreateView(inflater, superContainer, savedInstanceState);
|
View root = super.onCreateView(inflater, superContainer, savedInstanceState);
|
||||||
@@ -349,34 +361,42 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void verify(final Proof proof, final String fingerprint) {
|
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());
|
mProof = proof;
|
||||||
data.putString(KeychainService.KEYBASE_REQUIRED_FINGERPRINT, fingerprint);
|
mKeybaseProof = proof.toString();
|
||||||
intent.putExtra(KeychainService.EXTRA_DATA, data);
|
mKeybaseFingerprint = fingerprint;
|
||||||
|
|
||||||
mProofVerifyDetail.setVisibility(View.GONE);
|
mProofVerifyDetail.setVisibility(View.GONE);
|
||||||
|
|
||||||
// Create a new Messenger for the communication back after proof work is done
|
mKeybaseOpHelper = new CryptoOperationHelper<>(this, this,
|
||||||
ServiceProgressHandler handler = new ServiceProgressHandler(getActivity()) {
|
R.string.progress_verifying_signature);
|
||||||
|
mKeybaseOpHelper.cryptoOperation();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleMessage(Message message) {
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
// handle messages by standard KeychainIntentServiceHandler first
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
super.handleMessage(message);
|
if (mKeybaseOpHelper != null) {
|
||||||
|
mKeybaseOpHelper.handleActivityResult(requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
// CryptoOperationHelper.Callback methods
|
||||||
Bundle returnData = message.getData();
|
@Override
|
||||||
String msg = returnData.getString(ServiceProgressHandler.DATA_MESSAGE);
|
public KeybaseVerificationParcel createOperationInput() {
|
||||||
SpannableStringBuilder ssb = new SpannableStringBuilder();
|
return new KeybaseVerificationParcel(mKeybaseProof, mKeybaseFingerprint);
|
||||||
|
}
|
||||||
|
|
||||||
if ((msg != null) && msg.equals("OK")) {
|
@Override
|
||||||
|
public void onCryptoOperationSuccess(KeybaseVerificationResult result) {
|
||||||
|
|
||||||
//yay
|
result.createNotify(getActivity()).show();
|
||||||
String proofUrl = returnData.getString(ServiceProgressHandler.KEYBASE_PROOF_URL);
|
|
||||||
String presenceUrl = returnData.getString(ServiceProgressHandler.KEYBASE_PRESENCE_URL);
|
String proofUrl = result.mProofUrl;
|
||||||
String presenceLabel = returnData.getString(ServiceProgressHandler.KEYBASE_PRESENCE_LABEL);
|
String presenceUrl = result.mPresenceUrl;
|
||||||
|
String presenceLabel = result.mPresenceLabel;
|
||||||
|
|
||||||
|
Proof proof = mProof; // TODO: should ideally be contained in result
|
||||||
|
|
||||||
String proofLabel;
|
String proofLabel;
|
||||||
switch (proof.getType()) {
|
switch (proof.getType()) {
|
||||||
@@ -400,6 +420,8 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SpannableStringBuilder ssb = new SpannableStringBuilder();
|
||||||
|
|
||||||
ssb.append(getString(R.string.keybase_proof_succeeded));
|
ssb.append(getString(R.string.keybase_proof_succeeded));
|
||||||
StyleSpan bold = new StyleSpan(Typeface.BOLD);
|
StyleSpan bold = new StyleSpan(Typeface.BOLD);
|
||||||
ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
@@ -418,43 +440,46 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
|
|||||||
length = ssb.length();
|
length = ssb.length();
|
||||||
URLSpan presenceLink = new URLSpan(presenceUrl);
|
URLSpan presenceLink = new URLSpan(presenceUrl);
|
||||||
ssb.append(presenceLabel);
|
ssb.append(presenceLabel);
|
||||||
ssb.setSpan(presenceLink, length, length + presenceLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
ssb.setSpan(presenceLink, length, length + presenceLabel.length(), Spanned
|
||||||
|
.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
if (Proof.PROOF_TYPE_REDDIT == proof.getType()) {
|
if (Proof.PROOF_TYPE_REDDIT == proof.getType()) {
|
||||||
ssb.append(", ").
|
ssb.append(", ").
|
||||||
append(getString(R.string.keybase_reddit_attribution)).
|
append(getString(R.string.keybase_reddit_attribution)).
|
||||||
append(" “").append(proof.getHandle()).append("”, ");
|
append(" “").append(proof.getHandle()).append("”, ");
|
||||||
}
|
}
|
||||||
ssb.append(" ").append(getString(R.string.keybase_contained_signature));
|
ssb.append(" ").append(getString(R.string.keybase_contained_signature));
|
||||||
} else {
|
|
||||||
// verification failed!
|
displaySpannableResult(ssb);
|
||||||
msg = returnData.getString(ServiceProgressHandler.DATA_ERROR);
|
}
|
||||||
|
|
||||||
|
@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));
|
ssb.append(getString(R.string.keybase_proof_failure));
|
||||||
|
String msg = getString(result.getLog().getLast().mType.mMsgId);
|
||||||
if (msg == null) {
|
if (msg == null) {
|
||||||
msg = getString(R.string.keybase_unknown_proof_failure);
|
msg = getString(R.string.keybase_unknown_proof_failure);
|
||||||
}
|
}
|
||||||
StyleSpan bold = new StyleSpan(Typeface.BOLD);
|
StyleSpan bold = new StyleSpan(Typeface.BOLD);
|
||||||
ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
ssb.append("\n\n").append(msg);
|
ssb.append("\n\n").append(msg);
|
||||||
|
|
||||||
|
displaySpannableResult(ssb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void displaySpannableResult(SpannableStringBuilder ssb) {
|
||||||
mProofVerifyHeader.setVisibility(View.VISIBLE);
|
mProofVerifyHeader.setVisibility(View.VISIBLE);
|
||||||
mProofVerifyDetail.setVisibility(View.VISIBLE);
|
mProofVerifyDetail.setVisibility(View.VISIBLE);
|
||||||
mProofVerifyDetail.setMovementMethod(LinkMovementMethod.getInstance());
|
mProofVerifyDetail.setMovementMethod(LinkMovementMethod.getInstance());
|
||||||
mProofVerifyDetail.setText(ssb);
|
mProofVerifyDetail.setText(ssb);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create a new Messenger for the communication back
|
|
||||||
Messenger messenger = new Messenger(handler);
|
|
||||||
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
|
|
||||||
|
|
||||||
// show progress dialog
|
|
||||||
handler.showProgressDialog(
|
|
||||||
getString(R.string.progress_verifying_signature),
|
|
||||||
ProgressDialog.STYLE_HORIZONTAL, false
|
|
||||||
);
|
|
||||||
|
|
||||||
// start service with intent
|
|
||||||
getActivity().startService(intent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1213,6 +1213,15 @@
|
|||||||
|
|
||||||
<string name="msg_download_query_failed">"An error occurred when searching for keys."</string>
|
<string name="msg_download_query_failed">"An error occurred when searching for keys."</string>
|
||||||
|
|
||||||
|
<!-- Messages for Keybase Verification operation -->
|
||||||
|
<string name="msg_keybase_verification">"Attempting keybase verification for %s"</string>
|
||||||
|
<string name="msg_keybase_error_no_prover">"No proof checker found for %s"</string>
|
||||||
|
<string name="msg_keybase_error_fetching_evidence">"Problem with fetching proof"</string>
|
||||||
|
<string name="msg_keybase_error_key_mismatch">"Key fingerprint doesn’t match that in proof post"</string>
|
||||||
|
<string name="msg_keybase_error_dns_fail">"DNS TXT Record retrieval failed"</string>
|
||||||
|
<string name="msg_keybase_error_specific">"%s"</string>
|
||||||
|
<string name="msg_keybase_error_msg_payload_mismatch">"Decrypted proof post does not match expected value"</string>
|
||||||
|
|
||||||
<!-- Messages for Export Log operation -->
|
<!-- Messages for Export Log operation -->
|
||||||
<string name="msg_export_log_start">"Exporting log"</string>
|
<string name="msg_export_log_start">"Exporting log"</string>
|
||||||
<string name="msg_export_log_error_fopen">"Error opening file"</string>
|
<string name="msg_export_log_error_fopen">"Error opening file"</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user