All keybase proofs now in place

This commit is contained in:
Tim Bray 2014-11-11 18:45:36 -08:00
parent 3c19e6cfc1
commit 36bac67dd5
4 changed files with 96 additions and 44 deletions

View File

@ -84,6 +84,12 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import de.measite.minidns.Client;
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 APG. It receives Intents with * This Service contains all important long lasting operations for APG. It receives Intents with
* data from the activities or other apps, queues these intents, executes them, and stops itself * data from the activities or other apps, queues these intents, executes them, and stops itself
@ -124,6 +130,7 @@ public class KeychainIntentService extends IntentService implements Progressable
// encrypt, decrypt, import export // encrypt, decrypt, import export
public static final String TARGET = "target"; public static final String TARGET = "target";
public static final String SOURCE = "source"; public static final String SOURCE = "source";
// possible targets: // possible targets:
public static final int IO_BYTES = 1; public static final int IO_BYTES = 1;
public static final int IO_URI = 2; public static final int IO_URI = 2;
@ -321,12 +328,27 @@ public class KeychainIntentService extends IntentService implements Progressable
return; return;
} }
String domain = prover.dnsTxtCheckRequired();
if (domain != null) {
Record[] records = new Client().query(new Question(domain, Record.TYPE.TXT)).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(); byte[] messageBytes = prover.getPgpMessage().getBytes();
if (prover.rawMessageCheckRequired()) { if (prover.rawMessageCheckRequired()) {
InputStream messageByteStream = PGPUtil.getDecoderStream(new ByteArrayInputStream(messageBytes)); InputStream messageByteStream = PGPUtil.getDecoderStream(new ByteArrayInputStream(messageBytes));
String problem = prover.checkRawMessageBytes(messageByteStream); if (!prover.checkRawMessageBytes(messageByteStream)) {
if (problem != null) { sendProofError(prover.getLog(), null);
sendProofError(prover.getLog(), problem);
return; return;
} }
} }
@ -365,6 +387,11 @@ public class KeychainIntentService extends IntentService implements Progressable
Bundle resultData = new Bundle(); Bundle resultData = new Bundle();
resultData.putString(KeychainIntentServiceHandler.DATA_MESSAGE, "OK"); resultData.putString(KeychainIntentServiceHandler.DATA_MESSAGE, "OK");
// these help the handler construct a useful human-readable message
resultData.putString(KeychainIntentServiceHandler.KEYBASE_PROOF_URL, prover.getProofUrl());
resultData.putString(KeychainIntentServiceHandler.KEYBASE_PRESENCE_URL, prover.getPresenceUrl());
resultData.putString(KeychainIntentServiceHandler.KEYBASE_PRESENCE_LABEL, prover.getPresenceLabel());
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
} catch (Exception e) { } catch (Exception e) {
sendErrorToHandler(e); sendErrorToHandler(e);
@ -678,11 +705,12 @@ public class KeychainIntentService extends IntentService implements Progressable
private void sendProofError(List<String> log, String label) { private void sendProofError(List<String> log, String label) {
String msg = null; String msg = null;
label = (label == null) ? "" : label + ": ";
for (String m : log) { for (String m : log) {
Log.e(Constants.TAG, label + ": " + m); Log.e(Constants.TAG, label + m);
msg = m; msg = m;
} }
sendProofError(label + ": " + msg); sendProofError(label + msg);
} }
private void sendProofError(String msg) { private void sendProofError(String msg) {

View File

@ -45,6 +45,11 @@ public class KeychainIntentServiceHandler extends Handler {
public static final String DATA_MESSAGE = "message"; public static final String DATA_MESSAGE = "message";
public static final String DATA_MESSAGE_ID = "message_id"; public static final String DATA_MESSAGE_ID = "message_id";
// keybase proof specific
public static final String KEYBASE_PROOF_URL = "keybase_proof_url";
public static final String KEYBASE_PRESENCE_URL = "keybase_presence_url";
public static final String KEYBASE_PRESENCE_LABEL = "keybase_presence_label";
Activity mActivity; Activity mActivity;
ProgressDialogFragment mProgressDialogFragment; ProgressDialogFragment mProgressDialogFragment;

View File

@ -324,9 +324,6 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
} }
private void appendIfOK(Hashtable<Integer, ArrayList<Proof>> table, Integer proofType, Proof proof) throws KeybaseException { private void appendIfOK(Hashtable<Integer, ArrayList<Proof>> table, Integer proofType, Proof proof) throws KeybaseException {
if (!proofIsOK(proof)) {
return;
}
ArrayList<Proof> list = table.get(proofType); ArrayList<Proof> list = table.get(proofType);
if (list == null) { if (list == null) {
list = new ArrayList<Proof>(); list = new ArrayList<Proof>();
@ -335,23 +332,16 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
list.add(proof); list.add(proof);
} }
// We only accept http & https proofs. Maybe whitelist later?
private boolean proofIsOK(Proof proof) throws KeybaseException {
Uri uri = Uri.parse(proof.getServiceUrl());
String scheme = uri.getScheme();
return ("https".equalsIgnoreCase(scheme) || "http".equalsIgnoreCase(scheme));
}
// which proofs do we have working verifiers for? // which proofs do we have working verifiers for?
private boolean haveProofFor(int proofType) { private boolean haveProofFor(int proofType) {
switch (proofType) { switch (proofType) {
case Proof.PROOF_TYPE_TWITTER: return true; case Proof.PROOF_TYPE_TWITTER: return true;
case Proof.PROOF_TYPE_GITHUB: return true; case Proof.PROOF_TYPE_GITHUB: return true;
case Proof.PROOF_TYPE_DNS: return false; case Proof.PROOF_TYPE_DNS: return true;
case Proof.PROOF_TYPE_WEB_SITE: return true; case Proof.PROOF_TYPE_WEB_SITE: return true;
case Proof.PROOF_TYPE_HACKERNEWS: return true; case Proof.PROOF_TYPE_HACKERNEWS: return true;
case Proof.PROOF_TYPE_COINBASE: return false; case Proof.PROOF_TYPE_COINBASE: return true;
case Proof.PROOF_TYPE_REDDIT: return false; case Proof.PROOF_TYPE_REDDIT: return true;
default: return false; default: return false;
} }
} }
@ -381,48 +371,70 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
SpannableStringBuilder ssb = new SpannableStringBuilder(); SpannableStringBuilder ssb = new SpannableStringBuilder();
if ((msg != null) && msg.equals("OK")) { if ((msg != null) && msg.equals("OK")) {
//yay
String serviceUrl, urlLabel, postUrl;
try {
serviceUrl = proof.getServiceUrl();
if (serviceUrl.startsWith("https://")) {
urlLabel = serviceUrl.substring("https://".length());
} else if (serviceUrl.startsWith("http://")) {
urlLabel = serviceUrl.substring("http://".length());
} else {
urlLabel = serviceUrl;
}
postUrl = proof.getHumanUrl();
} catch (KeybaseException e) { //yay
throw new RuntimeException(e); String proofUrl = returnData.getString(KeychainIntentServiceHandler.KEYBASE_PROOF_URL);
String presenceUrl = returnData.getString(KeychainIntentServiceHandler.KEYBASE_PRESENCE_URL);
String presenceLabel = returnData.getString(KeychainIntentServiceHandler.KEYBASE_PRESENCE_LABEL);
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;
} }
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);
ssb.append("\n\n"); ssb.append("\n\n");
int length = ssb.length(); int length = ssb.length();
String segment = getString(R.string.keybase_a_post); ssb.append(proofLabel);
ssb.append(segment); if (proofUrl != null) {
URLSpan postLink = new URLSpan(postUrl); URLSpan postLink = new URLSpan(proofUrl);
ssb.setSpan(postLink, length, length + segment.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.setSpan(postLink, length, length + proofLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.append(" ").append(getString(R.string.keybase_fetched_from)).append(" "); }
URLSpan serviceLink = new URLSpan(serviceUrl); if (Proof.PROOF_TYPE_DNS == proof.getType()) {
length = ssb.length(); ssb.append(" ").append(getString(R.string.keybase_for_the_domain)).append(" ");
ssb.append(urlLabel);
ssb.setSpan(serviceLink, length, length + urlLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.append(" ").append(getString(R.string.keybase_contained_signature));
} else { } 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(KeychainIntentServiceHandler.DATA_ERROR); msg = returnData.getString(KeychainIntentServiceHandler.DATA_ERROR);
ssb.append(getString(R.string.keybase_proof_failure)); ssb.append(getString(R.string.keybase_proof_failure));
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);
} }
}
mProofVerifyHeader.setVisibility(View.VISIBLE); mProofVerifyHeader.setVisibility(View.VISIBLE);
mProofVerifyDetail.setVisibility(View.VISIBLE); mProofVerifyDetail.setVisibility(View.VISIBLE);
mProofVerifyDetail.setMovementMethod(LinkMovementMethod.getInstance()); mProofVerifyDetail.setMovementMethod(LinkMovementMethod.getInstance());

View File

@ -567,7 +567,14 @@
<string name="keybase_proof_succeeded">"This proof has been verified!"</string> <string name="keybase_proof_succeeded">"This proof has been verified!"</string>
<string name="keybase_a_post">"A post"</string> <string name="keybase_a_post">"A post"</string>
<string name="keybase_fetched_from">"fetched from"</string> <string name="keybase_fetched_from">"fetched from"</string>
<string name="keybase_for_the_domain">"for the domain"</string>
<string name="keybase_contained_signature">"contains a message which could only have been created by the owner of this key."</string> <string name="keybase_contained_signature">"contains a message which could only have been created by the owner of this key."</string>
<string name="keybase_twitter_proof">"A tweet"</string>
<string name="keybase_dns_proof">"A DNS TXT record"</string>
<string name="keybase_web_site_proof">"A text file"</string>
<string name="keybase_github_proof">"A gist"</string>
<string name="keybase_reddit_proof">"A JSON file"</string>
<string name="keybase_reddit_attribution">"attributed by Reddit to"</string>
<!-- Edit key --> <!-- Edit key -->
<string name="edit_key_action_change_passphrase">"Change Passphrase"</string> <string name="edit_key_action_change_passphrase">"Change Passphrase"</string>