Skip to content

Adds Proxy Support to KeybaseLib #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions Lib/build.gradle
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
apply plugin: 'android-library'
apply plugin: 'com.android.library'

dependencies {
compile 'com.squareup.okhttp:okhttp:2.4.0'
}

android {
compileSdkVersion 19
buildToolsVersion "19.1.0"
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion

defaultConfig {
applicationId "com.textuality.keybase.lib"
minSdkVersion 9
targetSdkVersion 19
}

buildTypes {
release {
runProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}

lintOptions {
abortOnError false
}
}
}
38 changes: 35 additions & 3 deletions Lib/src/main/java/com/textuality/keybase/lib/Match.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;

public class Match {
private final JSONObject mComponents;
Expand Down Expand Up @@ -86,18 +88,48 @@ public int getBitStrength() throws KeybaseException {
public List<String> getProofLabels() {
ArrayList<String> labels = new ArrayList<String>();
try {
labels.add("twitter.com/" + JWalk.getString(mComponents, "twitter", "val"));
labels.add("Twitter: @" + JWalk.getString(mComponents, "twitter", "val"));
} catch (JSONException e) {
// s'OK
}
try {
labels.add("github.com/" + JWalk.getString(mComponents, "github", "val"));
labels.add("GitHub: " + JWalk.getString(mComponents, "github", "val"));
} catch (JSONException e) {
// s'OK
}
try {
labels.add("Reddit: " + JWalk.getString(mComponents, "reddit", "val"));
} catch (JSONException e) {
// s'OK
}
try {
labels.add("Hacker News: " + JWalk.getString(mComponents, "hackernews", "val"));
} catch (JSONException e) {
// s'OK
}
try {
labels.add("Coinbase: " + JWalk.getString(mComponents, "coinbase", "val"));
} catch (JSONException e) {
// s'OK
}
try {
JSONArray sites = JWalk.getArray(mComponents, "websites");
labels.add(JWalk.getString(sites.getJSONObject(0), "val"));
Hashtable<String, Integer> uniqueNames = new Hashtable<String, Integer>();
int i;
for (i = 0; i < sites.length(); i++) {
uniqueNames.put(JWalk.getString(sites.getJSONObject(i), "val"), 1);
}
Set<String> names = uniqueNames.keySet();
StringBuilder label = new StringBuilder("Web: ");
i = 0;
for (String name : names) {
label.append(name);
if (i < names.size() - 1) {
label.append(", ");
}
i++;
}
labels.add(label.toString());
} catch (JSONException e) {
// s'OK
}
Expand Down
42 changes: 29 additions & 13 deletions Lib/src/main/java/com/textuality/keybase/lib/Search.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,56 +18,72 @@

import android.util.Log;

import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.concurrent.TimeUnit;

public class Search {

private static final String TAG = "KEYBASE-LIB";

public static Iterable<Match> search(String query) throws KeybaseException {
JSONObject result = getFromKeybase("_/api/1.0/user/autocomplete.json?q=", query);
public static Iterable<Match> search(String query, Proxy proxy) throws KeybaseException {
JSONObject result = getFromKeybase("_/api/1.0/user/autocomplete.json?q=", query, proxy);
try {
return new MatchIterator(JWalk.getArray(result, "completions"));
} catch (JSONException e) {
throw KeybaseException.keybaseScrewup(e);
}
}

public static JSONObject getFromKeybase(String path, String query) throws KeybaseException {
public static JSONObject getFromKeybase(String path, String query, Proxy proxy) throws KeybaseException {
try {
String url = "https://keybase.io/" + path + URLEncoder.encode(query, "utf8");

URL realUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
conn.setConnectTimeout(5000); // TODO: Reasonable values for keybase
conn.setReadTimeout(25000);
conn.connect();
int response = conn.getResponseCode();

OkHttpClient client = new OkHttpClient();
client.setProxy(proxy);

if (proxy != null) {
client.setConnectTimeout(30000, TimeUnit.MILLISECONDS);
client.setReadTimeout(40000, TimeUnit.MILLISECONDS);
} else {
client.setConnectTimeout(5000, TimeUnit.MILLISECONDS); // TODO: Reasonable values for keybase
client.setReadTimeout(25000, TimeUnit.MILLISECONDS);
}

Response resp = client.newCall(new Request.Builder().url(realUrl).build()).execute();

int response = resp.code();

String text = resp.body().string();

if (response >= 200 && response < 300) {
String text = snarf(conn.getInputStream());
try {
JSONObject json = new JSONObject(text);
if (JWalk.getInt(json, "status", "code") != 0) {
throw KeybaseException.queryScrewup("Keybase.io query failed: " + path + "?" + query);
throw KeybaseException.queryScrewup("Keybase.io query failed: " + path + "?" + query +
" using proxy: " + proxy);
}
return json;
} catch (JSONException e) {
throw KeybaseException.keybaseScrewup(e);
}
} else {
String message = snarf(conn.getErrorStream());
throw KeybaseException.networkScrewup("Keybase.io query error (status=" + response + "): " + message);
throw KeybaseException.networkScrewup("Keybase.io query error (status=" + response + "): " + text);
}
} catch (Exception e) {
throw KeybaseException.networkScrewup(e);
Expand Down
13 changes: 7 additions & 6 deletions Lib/src/main/java/com/textuality/keybase/lib/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,27 @@
import org.json.JSONException;
import org.json.JSONObject;

import java.net.Proxy;
import java.util.Iterator;

public class User {

private final JSONObject mJson;

public static User findByUsername(String username) throws KeybaseException {
JSONObject json = Search.getFromKeybase("_/api/1.0/user/lookup.json?username=", username);
public static User findByUsername(String username, Proxy proxy) throws KeybaseException {
JSONObject json = Search.getFromKeybase("_/api/1.0/user/lookup.json?username=", username, proxy);
try {
json = JWalk.getObject(json, "them");
} catch (JSONException e) {
throw KeybaseException.keybaseScrewup(e);
}
return new User(json);
}
public static String keyForUsername(String username) throws KeybaseException {
return findByUsername(username).getKey();
public static String keyForUsername(String username, Proxy proxy) throws KeybaseException {
return findByUsername(username, proxy).getKey();
}
public static User findByFingerprint(String fingerprint) throws KeybaseException {
JSONObject json = Search.getFromKeybase("_/api/1.0/user/lookup.json?key_fingerprint=", fingerprint);
public static User findByFingerprint(String fingerprint, Proxy proxy) throws KeybaseException {
JSONObject json = Search.getFromKeybase("_/api/1.0/user/lookup.json?key_fingerprint=", fingerprint, proxy);
try {
JSONArray them = JWalk.getArray(json, "them");
if (them.length() != 1) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,16 @@
import org.json.JSONObject;

import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL;

public class Coinbase extends Prover {

@Override
public boolean fetchProofData() {
public boolean fetchProofData(Proxy proxy) {

try {
JSONObject sigJSON = readSig(mProof.getSigId());
JSONObject sigJSON = readSig(mProof.getSigId(), proxy);

String proofUrl = mProof.getProofUrl();

Expand Down
5 changes: 3 additions & 2 deletions Lib/src/main/java/com/textuality/keybase/lib/prover/DNS.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,18 @@
import org.json.JSONException;
import org.json.JSONObject;

import java.net.Proxy;
import java.util.List;

public class DNS extends Prover {

private String mDomain = null;

@Override
public boolean fetchProofData() {
public boolean fetchProofData(Proxy proxy) {

try {
JSONObject sigJSON = readSig(mProof.getSigId());
JSONObject sigJSON = readSig(mProof.getSigId(), proxy);

// the magic string is the base64 of the SHA of the raw message
mShortenedMessageHash = JWalk.getString(sigJSON, "sig_id_short");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,19 @@
import org.json.JSONException;
import org.json.JSONObject;

import java.net.Proxy;

public class GitHub extends Prover {

private static final String[] sAllowedApiBases = {
"https://gist.githubusercontent.com/", "https://gist.github.com/"
};

@Override
public boolean fetchProofData() {
public boolean fetchProofData(Proxy proxy) {

try {
JSONObject sigJSON = readSig(mProof.getSigId());
JSONObject sigJSON = readSig(mProof.getSigId(), proxy);

// find the URL for the markdown form of the gist
String markdownURL = JWalk.getString(sigJSON, "api_url");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,16 @@
import org.json.JSONObject;

import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL;

public class HackerNews extends Prover {

@Override
public boolean fetchProofData() {
public boolean fetchProofData(Proxy proxy) {

try {
JSONObject sigJSON = readSig(mProof.getSigId());
JSONObject sigJSON = readSig(mProof.getSigId(), proxy);

// the magic string is the base64 of the SHA of the raw message
mShortenedMessageHash = JWalk.getString(sigJSON, "sig_id_short");
Expand Down
23 changes: 16 additions & 7 deletions Lib/src/main/java/com/textuality/keybase/lib/prover/Prover.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
Expand All @@ -52,22 +53,25 @@
* How to use:
* 1. call fetchProofData(), which will exhibit network latency. If it returns false the proof
* verification failed; an explanation can be found in the log.
* 2. fetch the PGP message with getPgpMessage(), check that it’s signed with the right fingerprint
* 2. call checkFingerprint(), passing it the fingerprint of the key you’re checking up on; if
* if it returns false the verification failed.
* 3. fetch the PGP message with getPgpMessage(), check that it’s signed with the right fingerprint
* (see above).
* 3. Call dnsTxtCheckRequired() and if it returns non-null, the return value is a domain name;
* 4. Call dnsTxtCheckRequired() and if it returns non-null, the return value is a domain name;
* retrieve TXT records from that domain and pass them to checkDnsTxt(); if it returns false
* the proof verification failed; an explanation can be found in the log.
* 4. call rawMessageCheckRequired() and if it returns true, feed the raw (de-armored) bytes
* 5. call rawMessageCheckRequired() and if it returns true, feed the raw (de-armored) bytes
* of the message to checkRawMessageBytes(). if it returns false the proof verification failed;
* an explanation can be found in the log. This may exhibit crypto latency.
* 5. Pass the message to validate(), which should have no real latency. If it returns false the
* 6. Pass the message to validate(), which should have no real latency. If it returns false the
* proof verification failed; an explanation can be found in the log.
*/
public abstract class Prover {

String mPgpMessage;
String mPayload;
String mShortenedMessageHash;
String mFingerprintUsedInProof = null;
final Proof mProof;
final List<String> mLog = new ArrayList<String>();

Expand All @@ -88,12 +92,16 @@ public Prover(Proof proof) {
mProof = proof;
}

abstract public boolean fetchProofData();
abstract public boolean fetchProofData(Proxy proxy);

public String getPgpMessage() {
return mPgpMessage;
}

public boolean checkFingerprint(String fingerprint) {
return fingerprint.equalsIgnoreCase(mFingerprintUsedInProof);
}

public boolean validate(String decryptedMessage) {
return mPayload.equals(decryptedMessage);
}
Expand All @@ -102,15 +110,16 @@ public List<String> getLog() {
return mLog;
}

JSONObject readSig(String sigId) throws JSONException, KeybaseException {
JSONObject readSig(String sigId, Proxy proxy) throws JSONException, KeybaseException {

// fetch the sig
JSONObject sigJSON = Search.getFromKeybase("_/api/1.0/sig/get.json?sig_id=", sigId);
JSONObject sigJSON = Search.getFromKeybase("_/api/1.0/sig/get.json?sig_id=", sigId, proxy);
mLog.add("Successfully retrieved sig from Keybase");

sigJSON = JWalk.getArray(sigJSON, "sigs").getJSONObject(0);
mPayload = JWalk.getString(sigJSON, "payload_json");
mPgpMessage = JWalk.getString(sigJSON, "sig");
mFingerprintUsedInProof = JWalk.getString(sigJSON, "fingerprint");

mLog.add("Extracted payload & message from sig");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,18 @@
import org.json.JSONObject;

import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL;

public class Reddit extends Prover {

private String mApiUrl = null;

@Override
public boolean fetchProofData() {
public boolean fetchProofData(Proxy proxy) {

try {
JSONObject sigJSON = readSig(mProof.getSigId());
JSONObject sigJSON = readSig(mProof.getSigId(), proxy);

// the magic string is the base64 of the SHA of the raw message
mShortenedMessageHash = JWalk.getString(sigJSON, "sig_id_short");
Expand Down
Loading