linked-ids: add certificate pinning, pin twitter api cert
This commit is contained in:
@@ -28,6 +28,7 @@ dependencies {
|
|||||||
compile 'com.mikepenz.iconics:octicons-typeface:2.2.0@aar'
|
compile 'com.mikepenz.iconics:octicons-typeface:2.2.0@aar'
|
||||||
compile 'com.mikepenz.iconics:meteocons-typeface:1.1.1@aar'
|
compile 'com.mikepenz.iconics:meteocons-typeface:1.1.1@aar'
|
||||||
compile 'com.mikepenz.iconics:community-material-typeface:1.0.0@aar'
|
compile 'com.mikepenz.iconics:community-material-typeface:1.0.0@aar'
|
||||||
|
compile 'org.thoughtcrime.ssl.pinning:AndroidPinning:1.0.0'
|
||||||
|
|
||||||
// libs as submodules
|
// libs as submodules
|
||||||
compile project(':extern:openpgp-api-lib')
|
compile project(':extern:openpgp-api-lib')
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.sufficientlysecure.keychain.linked;
|
|||||||
|
|
||||||
import org.apache.http.HttpEntity;
|
import org.apache.http.HttpEntity;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.client.HttpClient;
|
||||||
import org.apache.http.client.methods.HttpRequestBase;
|
import org.apache.http.client.methods.HttpRequestBase;
|
||||||
import org.apache.http.impl.client.DefaultHttpClient;
|
import org.apache.http.impl.client.DefaultHttpClient;
|
||||||
import org.apache.http.params.BasicHttpParams;
|
import org.apache.http.params.BasicHttpParams;
|
||||||
@@ -16,6 +17,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.LogTyp
|
|||||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
|
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
|
||||||
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;
|
||||||
|
import org.thoughtcrime.ssl.pinning.util.PinningHelper;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -30,6 +32,8 @@ import java.util.Set;
|
|||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
|
||||||
public abstract class LinkedTokenResource extends LinkedResource {
|
public abstract class LinkedTokenResource extends LinkedResource {
|
||||||
|
|
||||||
@@ -166,7 +170,7 @@ public abstract class LinkedTokenResource extends LinkedResource {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LinkedVerifyResult verify(byte[] fingerprint) {
|
public LinkedVerifyResult verify(Context context, byte[] fingerprint) {
|
||||||
|
|
||||||
OperationLog log = new OperationLog();
|
OperationLog log = new OperationLog();
|
||||||
log.add(LogType.MSG_LV, 0);
|
log.add(LogType.MSG_LV, 0);
|
||||||
@@ -174,7 +178,7 @@ public abstract class LinkedTokenResource extends LinkedResource {
|
|||||||
// Try to fetch resource. Logs for itself
|
// Try to fetch resource. Logs for itself
|
||||||
String res = null;
|
String res = null;
|
||||||
try {
|
try {
|
||||||
res = fetchResource(log, 1);
|
res = fetchResource(context, log, 1);
|
||||||
} catch (HttpStatusException e) {
|
} catch (HttpStatusException e) {
|
||||||
// log verbose output to logcat
|
// log verbose output to logcat
|
||||||
Log.e(Constants.TAG, "http error (" + e.getStatus() + "): " + e.getReason());
|
Log.e(Constants.TAG, "http error (" + e.getStatus() + "): " + e.getReason());
|
||||||
@@ -200,8 +204,8 @@ public abstract class LinkedTokenResource extends LinkedResource {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract String fetchResource (OperationLog log, int indent) throws HttpStatusException, IOException,
|
protected abstract String fetchResource (Context context, OperationLog log, int indent)
|
||||||
JSONException;
|
throws HttpStatusException, IOException, JSONException;
|
||||||
|
|
||||||
protected Matcher matchResource (OperationLog log, int indent, String res) {
|
protected Matcher matchResource (OperationLog log, int indent, String res) {
|
||||||
return magicPattern.matcher(res);
|
return magicPattern.matcher(res);
|
||||||
@@ -231,12 +235,26 @@ public abstract class LinkedTokenResource extends LinkedResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation") // HttpRequestBase is deprecated
|
@SuppressWarnings("deprecation") // HttpRequestBase is deprecated
|
||||||
public static String getResponseBody(HttpRequestBase request) throws IOException, HttpStatusException {
|
public static String getResponseBody(Context context, HttpRequestBase request)
|
||||||
|
throws IOException, HttpStatusException {
|
||||||
|
return getResponseBody(context, request, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation") // HttpRequestBase is deprecated
|
||||||
|
public static String getResponseBody(Context context, HttpRequestBase request, String[] pins)
|
||||||
|
throws IOException, HttpStatusException {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
request.setHeader("User-Agent", "Open Keychain");
|
request.setHeader("User-Agent", "Open Keychain");
|
||||||
|
|
||||||
DefaultHttpClient httpClient = new DefaultHttpClient(new BasicHttpParams());
|
|
||||||
|
HttpClient httpClient;
|
||||||
|
if (pins == null) {
|
||||||
|
httpClient = new DefaultHttpClient(new BasicHttpParams());
|
||||||
|
} else {
|
||||||
|
httpClient = PinningHelper.getPinnedHttpClient(context, pins);
|
||||||
|
}
|
||||||
|
|
||||||
HttpResponse response = httpClient.execute(request);
|
HttpResponse response = httpClient.execute(request);
|
||||||
int statusCode = response.getStatusLine().getStatusCode();
|
int statusCode = response.getStatusLine().getStatusCode();
|
||||||
String reason = response.getStatusLine().getReasonPhrase();
|
String reason = response.getStatusLine().getReasonPhrase();
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ public class DnsResource extends LinkedTokenResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String fetchResource (OperationLog log, int indent) {
|
protected String fetchResource (Context context, OperationLog log, int indent) {
|
||||||
|
|
||||||
Client c = new Client();
|
Client c = new Client();
|
||||||
DNSMessage msg = c.query(new Question(mFqdn, mType, mClass));
|
DNSMessage msg = c.query(new Question(mFqdn, mType, mClass));
|
||||||
|
|||||||
@@ -34,11 +34,12 @@ public class GenericHttpsResource extends LinkedTokenResource {
|
|||||||
|
|
||||||
@SuppressWarnings("deprecation") // HttpGet is deprecated
|
@SuppressWarnings("deprecation") // HttpGet is deprecated
|
||||||
@Override
|
@Override
|
||||||
protected String fetchResource (OperationLog log, int indent) throws HttpStatusException, IOException {
|
protected String fetchResource (Context context, OperationLog log, int indent)
|
||||||
|
throws HttpStatusException, IOException {
|
||||||
|
|
||||||
log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString());
|
log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString());
|
||||||
HttpGet httpGet = new HttpGet(mSubUri);
|
HttpGet httpGet = new HttpGet(mSubUri);
|
||||||
return getResponseBody(httpGet);
|
return getResponseBody(context, httpGet);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,14 +49,14 @@ public class GithubResource extends LinkedTokenResource {
|
|||||||
|
|
||||||
@SuppressWarnings("deprecation") // HttpGet is deprecated
|
@SuppressWarnings("deprecation") // HttpGet is deprecated
|
||||||
@Override
|
@Override
|
||||||
protected String fetchResource (OperationLog log, int indent)
|
protected String fetchResource (Context context, OperationLog log, int indent)
|
||||||
throws HttpStatusException, IOException, JSONException {
|
throws HttpStatusException, IOException, JSONException {
|
||||||
|
|
||||||
log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString());
|
log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString());
|
||||||
indent += 1;
|
indent += 1;
|
||||||
|
|
||||||
HttpGet httpGet = new HttpGet("https://api.github.com/gists/" + mGistId);
|
HttpGet httpGet = new HttpGet("https://api.github.com/gists/" + mGistId);
|
||||||
String response = getResponseBody(httpGet);
|
String response = getResponseBody(context, httpGet);
|
||||||
|
|
||||||
JSONObject obj = new JSONObject(response);
|
JSONObject obj = new JSONObject(response);
|
||||||
|
|
||||||
@@ -80,8 +80,8 @@ public class GithubResource extends LinkedTokenResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public static GithubResource searchInGithubStream(String screenName, String needle,
|
public static GithubResource searchInGithubStream(
|
||||||
OperationLog log) {
|
Context context, String screenName, String needle, OperationLog log) {
|
||||||
|
|
||||||
// narrow the needle down to important part
|
// narrow the needle down to important part
|
||||||
Matcher matcher = magicPattern.matcher(needle);
|
Matcher matcher = magicPattern.matcher(needle);
|
||||||
@@ -98,7 +98,7 @@ public class GithubResource extends LinkedTokenResource {
|
|||||||
httpGet.setHeader("Content-Type", "application/json");
|
httpGet.setHeader("Content-Type", "application/json");
|
||||||
httpGet.setHeader("User-Agent", "OpenKeychain");
|
httpGet.setHeader("User-Agent", "OpenKeychain");
|
||||||
|
|
||||||
String response = getResponseBody(httpGet);
|
String response = getResponseBody(context, httpGet);
|
||||||
array = new JSONArray(response);
|
array = new JSONArray(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ public class GithubResource extends LinkedTokenResource {
|
|||||||
HttpGet httpGet = new HttpGet("https://api.github.com/gists/" + id);
|
HttpGet httpGet = new HttpGet("https://api.github.com/gists/" + id);
|
||||||
httpGet.setHeader("User-Agent", "OpenKeychain");
|
httpGet.setHeader("User-Agent", "OpenKeychain");
|
||||||
|
|
||||||
JSONObject gistObj = new JSONObject(getResponseBody(httpGet));
|
JSONObject gistObj = new JSONObject(getResponseBody(context, httpGet));
|
||||||
JSONObject gistFiles = gistObj.getJSONObject("files");
|
JSONObject gistFiles = gistObj.getJSONObject("files");
|
||||||
Iterator<String> gistIt = gistFiles.keys();
|
Iterator<String> gistIt = gistFiles.keys();
|
||||||
if (!gistIt.hasNext()) {
|
if (!gistIt.hasNext()) {
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
public class TwitterResource extends LinkedTokenResource {
|
public class TwitterResource extends LinkedTokenResource {
|
||||||
|
|
||||||
|
public static final String[] CERT_PINS = new String[] {
|
||||||
|
// antec Class 3 Secure Server CA - G4
|
||||||
|
"513fb9743870b73440418d30930699ff"
|
||||||
|
};
|
||||||
|
|
||||||
final String mHandle;
|
final String mHandle;
|
||||||
final String mTweetId;
|
final String mTweetId;
|
||||||
|
|
||||||
@@ -68,12 +73,12 @@ public class TwitterResource extends LinkedTokenResource {
|
|||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@Override
|
@Override
|
||||||
protected String fetchResource(OperationLog log, int indent) throws IOException, HttpStatusException,
|
protected String fetchResource(Context context, OperationLog log, int indent)
|
||||||
JSONException {
|
throws IOException, HttpStatusException, JSONException {
|
||||||
|
|
||||||
String authToken;
|
String authToken;
|
||||||
try {
|
try {
|
||||||
authToken = getAuthToken();
|
authToken = getAuthToken(context);
|
||||||
} catch (IOException | HttpStatusException | JSONException e) {
|
} catch (IOException | HttpStatusException | JSONException e) {
|
||||||
log.add(LogType.MSG_LV_ERROR_TWITTER_AUTH, indent);
|
log.add(LogType.MSG_LV_ERROR_TWITTER_AUTH, indent);
|
||||||
return null;
|
return null;
|
||||||
@@ -90,7 +95,7 @@ public class TwitterResource extends LinkedTokenResource {
|
|||||||
httpGet.setHeader("Content-Type", "application/json");
|
httpGet.setHeader("Content-Type", "application/json");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String response = getResponseBody(httpGet);
|
String response = getResponseBody(context, httpGet, CERT_PINS);
|
||||||
JSONObject obj = new JSONObject(response);
|
JSONObject obj = new JSONObject(response);
|
||||||
JSONObject user = obj.getJSONObject("user");
|
JSONObject user = obj.getJSONObject("user");
|
||||||
if (!mHandle.equalsIgnoreCase(user.getString("screen_name"))) {
|
if (!mHandle.equalsIgnoreCase(user.getString("screen_name"))) {
|
||||||
@@ -142,11 +147,11 @@ public class TwitterResource extends LinkedTokenResource {
|
|||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public static TwitterResource searchInTwitterStream(
|
public static TwitterResource searchInTwitterStream(
|
||||||
String screenName, String needle, OperationLog log) {
|
Context context, String screenName, String needle, OperationLog log) {
|
||||||
|
|
||||||
String authToken;
|
String authToken;
|
||||||
try {
|
try {
|
||||||
authToken = getAuthToken();
|
authToken = getAuthToken(context);
|
||||||
} catch (IOException | HttpStatusException | JSONException e) {
|
} catch (IOException | HttpStatusException | JSONException e) {
|
||||||
log.add(LogType.MSG_LV_ERROR_TWITTER_AUTH, 1);
|
log.add(LogType.MSG_LV_ERROR_TWITTER_AUTH, 1);
|
||||||
return null;
|
return null;
|
||||||
@@ -166,7 +171,7 @@ public class TwitterResource extends LinkedTokenResource {
|
|||||||
httpGet.setHeader("Content-Type", "application/json");
|
httpGet.setHeader("Content-Type", "application/json");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String response = getResponseBody(httpGet);
|
String response = getResponseBody(context, httpGet, CERT_PINS);
|
||||||
JSONArray array = new JSONArray(response);
|
JSONArray array = new JSONArray(response);
|
||||||
|
|
||||||
for (int i = 0; i < array.length(); i++) {
|
for (int i = 0; i < array.length(); i++) {
|
||||||
@@ -203,7 +208,8 @@ public class TwitterResource extends LinkedTokenResource {
|
|||||||
private static String cachedAuthToken;
|
private static String cachedAuthToken;
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
private static String getAuthToken() throws IOException, HttpStatusException, JSONException {
|
private static String getAuthToken(Context context)
|
||||||
|
throws IOException, HttpStatusException, JSONException {
|
||||||
if (cachedAuthToken != null) {
|
if (cachedAuthToken != null) {
|
||||||
return cachedAuthToken;
|
return cachedAuthToken;
|
||||||
}
|
}
|
||||||
@@ -215,7 +221,7 @@ public class TwitterResource extends LinkedTokenResource {
|
|||||||
httpPost.setHeader("Authorization", "Basic " + base64Encoded);
|
httpPost.setHeader("Authorization", "Basic " + base64Encoded);
|
||||||
httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
|
httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
|
||||||
httpPost.setEntity(new StringEntity("grant_type=client_credentials"));
|
httpPost.setEntity(new StringEntity("grant_type=client_credentials"));
|
||||||
JSONObject rawAuthorization = new JSONObject(getResponseBody(httpPost));
|
JSONObject rawAuthorization = new JSONObject(getResponseBody(context, httpPost, CERT_PINS));
|
||||||
|
|
||||||
// Applications should verify that the value associated with the
|
// Applications should verify that the value associated with the
|
||||||
// token_type key of the returned object is bearer
|
// token_type key of the returned object is bearer
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ public abstract class LinkedIdCreateFinalFragment extends CryptoOperationFragmen
|
|||||||
return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log);
|
return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log);
|
||||||
}
|
}
|
||||||
|
|
||||||
LinkedVerifyResult result = resource.verify(mLinkedIdWizard.mFingerprint);
|
LinkedVerifyResult result = resource.verify(getActivity(), mLinkedIdWizard.mFingerprint);
|
||||||
|
|
||||||
// ux flow: this operation should take at last a second
|
// ux flow: this operation should take at last a second
|
||||||
timer = System.currentTimeMillis() -timer;
|
timer = System.currentTimeMillis() -timer;
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ public class LinkedIdCreateGithubStep2Fragment extends LinkedIdCreateFinalFragme
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
LinkedTokenResource getResource(OperationLog log) {
|
LinkedTokenResource getResource(OperationLog log) {
|
||||||
return GithubResource.searchInGithubStream(mResourceHandle, mResourceString, log);
|
return GithubResource.searchInGithubStream(getActivity(), mResourceHandle, mResourceString, log);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -91,7 +91,8 @@ public class LinkedIdCreateTwitterStep2Fragment extends LinkedIdCreateFinalFragm
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
LinkedTokenResource getResource(OperationLog log) {
|
LinkedTokenResource getResource(OperationLog log) {
|
||||||
return TwitterResource.searchInTwitterStream(mResourceHandle, mResourceString, log);
|
return TwitterResource.searchInTwitterStream(getActivity(),
|
||||||
|
mResourceHandle, mResourceString, log);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user