From 2f110344ed37e62320d2ff30ffad6c24315ff179 Mon Sep 17 00:00:00 2001 From: Adithya Abraham Philip Date: Wed, 24 Jun 2015 06:15:01 +0530 Subject: migrated Keybase verification --- .../keychain/operations/CertifyOperation.java | 9 +- .../operations/KeybaseVerificationOperation.java | 141 +++++++++++++++++++++ .../results/KeybaseVerificationResult.java | 59 +++++++++ .../operations/results/OperationResult.java | 14 +- 4 files changed, 219 insertions(+), 4 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/KeybaseVerificationOperation.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/KeybaseVerificationResult.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java index 439260b74..cb76c8ba3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java @@ -58,12 +58,13 @@ import java.util.concurrent.atomic.AtomicBoolean; * @see CertifyActionsParcel * */ -public class CertifyOperation extends BaseOperation { +public class CertifyOperation extends BaseOperation { 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(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/KeybaseVerificationOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/KeybaseVerificationOperation.java new file mode 100644 index 000000000..683fc9fa9 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/KeybaseVerificationOperation.java @@ -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 { + + 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> extents = new ArrayList>(); + 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; + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/KeybaseVerificationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/KeybaseVerificationResult.java new file mode 100644 index 000000000..f4312ae47 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/KeybaseVerificationResult.java @@ -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 CREATOR = new Parcelable.Creator() { + @Override + public KeybaseVerificationResult createFromParcel(Parcel in) { + return new KeybaseVerificationResult(in); + } + + @Override + public KeybaseVerificationResult[] newArray(int size) { + return new KeybaseVerificationResult[size]; + } + }; +} \ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java index c9e427462..f0561bef2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java @@ -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), -- cgit v1.2.3