Merge pull request #627 from thi/improve-keybase.io-support

Improve keybase.io support
This commit is contained in:
Dominik Schürmann 2014-05-14 20:19:44 +02:00
commit bbf19248ef
6 changed files with 91 additions and 46 deletions

View File

@ -247,7 +247,7 @@ public class HkpKeyServer extends KeyServer {
// see http://bit.ly/1d4bxbk and http://bit.ly/1gD1wwr
String fingerprintOrKeyId = matcher.group(1);
if (fingerprintOrKeyId.length() > 16) {
entry.setFingerPrintHex(fingerprintOrKeyId.toLowerCase(Locale.US));
entry.setFingerprintHex(fingerprintOrKeyId.toLowerCase(Locale.US));
entry.setKeyIdHex("0x" + fingerprintOrKeyId.substring(fingerprintOrKeyId.length()
- 16, fingerprintOrKeyId.length()));
} else {

View File

@ -45,11 +45,12 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
public String keyIdHex;
public boolean revoked;
public Date date; // TODO: not displayed
public String fingerPrintHex;
public String fingerprintHex;
public int bitStrength;
public String algorithm;
public boolean secretKey;
public String mPrimaryUserId;
private String mExtraData;
private boolean mSelected;
@ -66,7 +67,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
dest.writeLong(keyId);
dest.writeByte((byte) (revoked ? 1 : 0));
dest.writeSerializable(date);
dest.writeString(fingerPrintHex);
dest.writeString(fingerprintHex);
dest.writeString(keyIdHex);
dest.writeInt(bitStrength);
dest.writeString(algorithm);
@ -74,6 +75,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
dest.writeByte((byte) (mSelected ? 1 : 0));
dest.writeInt(mBytes.length);
dest.writeByteArray(mBytes);
dest.writeString(mExtraData);
}
public static final Creator<ImportKeysListEntry> CREATOR = new Creator<ImportKeysListEntry>() {
@ -85,7 +87,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
vr.keyId = source.readLong();
vr.revoked = source.readByte() == 1;
vr.date = (Date) source.readSerializable();
vr.fingerPrintHex = source.readString();
vr.fingerprintHex = source.readString();
vr.keyIdHex = source.readString();
vr.bitStrength = source.readInt();
vr.algorithm = source.readString();
@ -93,6 +95,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
vr.mSelected = source.readByte() == 1;
vr.mBytes = new byte[source.readInt()];
source.readByteArray(vr.mBytes);
vr.mExtraData = source.readString();
return vr;
}
@ -150,12 +153,12 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
this.date = date;
}
public String getFingerPrintHex() {
return fingerPrintHex;
public String getFingerprintHex() {
return fingerprintHex;
}
public void setFingerPrintHex(String fingerPrintHex) {
this.fingerPrintHex = fingerPrintHex;
public void setFingerprintHex(String fingerprintHex) {
this.fingerprintHex = fingerprintHex;
}
public int getBitStrength() {
@ -198,6 +201,14 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
mPrimaryUserId = uid;
}
public String getExtraData() {
return mExtraData;
}
public void setExtraData(String extraData) {
mExtraData = extraData;
}
/**
* Constructor for later querying from keyserver
*/
@ -260,7 +271,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
this.keyIdHex = PgpKeyHelper.convertKeyIdToHex(keyId);
this.revoked = key.isRevoked();
this.fingerPrintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint());
this.fingerprintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint());
this.bitStrength = key.getBitStrength();
final int algorithm = key.getAlgorithm();
this.algorithm = PgpKeyHelper.getAlgorithmInfo(context, algorithm);

View File

@ -41,6 +41,11 @@ public class KeybaseKeyServer extends KeyServer {
InsufficientQuery {
ArrayList<ImportKeysListEntry> results = new ArrayList<ImportKeysListEntry>();
if (query.startsWith("0x")) {
// cut off "0x" if a user is searching for a key id
query = query.substring(2);
}
JSONObject fromQuery = getFromKeybase("_/api/1.0/user/autocomplete.json?q=", query);
try {
@ -50,23 +55,32 @@ public class KeybaseKeyServer extends KeyServer {
// only list them if they have a key
if (JWalk.optObject(match, "components", "key_fingerprint") != null) {
String keybaseId = JWalk.getString(match, "components", "username", "val");
String fingerprint = JWalk.getString(match, "components", "key_fingerprint", "val");
fingerprint = fingerprint.replace(" ", "").toUpperCase();
if (keybaseId.equals(query) || fingerprint.startsWith(query.toUpperCase())) {
results.add(makeEntry(match));
} else {
results.add(makeEntry(match));
}
}
}
} catch (Exception e) {
Log.e(Constants.TAG, "keybase result parsing error", e);
throw new QueryException("Unexpected structure in keybase search result: " + e.getMessage());
}
return results;
}
private JSONObject getUser(String keybaseID) throws QueryException {
private JSONObject getUser(String keybaseId) throws QueryException {
try {
return getFromKeybase("_/api/1.0/user/lookup.json?username=", keybaseID);
return getFromKeybase("_/api/1.0/user/lookup.json?username=", keybaseId);
} catch (Exception e) {
String detail = "";
if (keybaseID != null) {
detail = ". Query was for user '" + keybaseID + "'";
if (keybaseId != null) {
detail = ". Query was for user '" + keybaseId + "'";
}
throw new QueryException(e.getMessage() + detail);
}
@ -74,35 +88,50 @@ public class KeybaseKeyServer extends KeyServer {
private ImportKeysListEntry makeEntry(JSONObject match) throws QueryException, JSONException {
String keybaseID = JWalk.getString(match, "components", "username", "val");
String key_fingerprint = JWalk.getString(match, "components", "key_fingerprint", "val");
key_fingerprint = key_fingerprint.replace(" ", "").toUpperCase();
match = getUser(keybaseID);
final ImportKeysListEntry entry = new ImportKeysListEntry();
String keybaseId = JWalk.getString(match, "components", "username", "val");
String fullName = JWalk.getString(match, "components", "full_name", "val");
String fingerprint = JWalk.getString(match, "components", "key_fingerprint", "val");
fingerprint = fingerprint.replace(" ", "").toUpperCase();
// in anticipation of a full fingerprint, only use the last 16 chars as 64-bit key id
entry.setKeyIdHex("0x" + fingerprint.substring(Math.max(0, fingerprint.length() - 16)));
// store extra info, so we can query for the keybase id directly
entry.setExtraData(keybaseId);
// TODO: Fix; have suggested keybase provide this value to avoid search-time crypto calls
entry.setBitStrength(4096);
entry.setAlgorithm("RSA");
entry.setKeyIdHex("0x" + key_fingerprint);
entry.setRevoked(false);
//entry.setBitStrength(4096);
//entry.setAlgorithm("RSA");
// ctime
final long creationDate = JWalk.getLong(match, "them", "public_keys", "primary", "ctime");
final GregorianCalendar tmpGreg = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
tmpGreg.setTimeInMillis(creationDate * 1000);
entry.setDate(tmpGreg.getTime());
entry.setFingerprintHex(fingerprint);
// key bits
// we have to fetch the user object to construct the search-result list, so we might as
// well (weakly) remember the key, in case they try to import it
mKeyCache.put(keybaseID, JWalk.getString(match,"them", "public_keys", "primary", "bundle"));
// key data
// currently there's no need to query the user right away, and it should be avoided, so the
// user doesn't experience lag and doesn't download many keys unnecessarily, but should we
// require to do it at soe point:
// (weakly) remember the key, in case the user tries to import it
//mKeyCache.put(keybaseId, JWalk.getString(match, "them", "public_keys", "primary", "bundle"));
// String displayName = JWalk.getString(match, "them", "profile", "full_name");
ArrayList<String> userIds = new ArrayList<String>();
String name = "keybase.io/" + keybaseID + " <" + keybaseID + "@keybase.io>";
String name = fullName + " <keybase.io/" + keybaseId + ">";
userIds.add(name);
userIds.add(keybaseID);
try {
userIds.add("github.com/" + JWalk.getString(match, "components", "github", "val"));
} catch (JSONException e) {
// ignore
}
try {
userIds.add("twitter.com/" + JWalk.getString(match, "components", "twitter", "val"));
} catch (JSONException e) {
// ignore
}
try {
JSONArray array = JWalk.getArray(match, "components", "websites");
JSONObject website = array.getJSONObject(0);
userIds.add(JWalk.getString(website, "val"));
} catch (JSONException e) {
// ignore
}
entry.setUserIds(userIds);
entry.setPrimaryUserId(name);
return entry;

View File

@ -749,8 +749,8 @@ public class KeychainIntentService extends IntentService
KeybaseKeyServer server = new KeybaseKeyServer();
for (ImportKeysListEntry entry : entries) {
// the keybase handle is in userId(1)
String keybaseID = entry.getUserIds().get(1);
byte[] downloadedKeyBytes = server.get(keybaseID).getBytes();
String keybaseId = entry.getExtraData();
byte[] downloadedKeyBytes = server.get(keybaseId).getBytes();
// create PGPKeyRing object based on downloaded armored key
PGPKeyRing downloadedKey = null;
@ -802,8 +802,8 @@ public class KeychainIntentService extends IntentService
for (ImportKeysListEntry entry : entries) {
// if available use complete fingerprint for get request
byte[] downloadedKeyBytes;
if (entry.getFingerPrintHex() != null) {
downloadedKeyBytes = server.get("0x" + entry.getFingerPrintHex()).getBytes();
if (entry.getFingerprintHex() != null) {
downloadedKeyBytes = server.get("0x" + entry.getFingerprintHex()).getBytes();
} else {
downloadedKeyBytes = server.get(entry.getKeyIdHex()).getBytes();
}
@ -829,10 +829,10 @@ public class KeychainIntentService extends IntentService
}
// verify downloaded key by comparing fingerprints
if (entry.getFingerPrintHex() != null) {
if (entry.getFingerprintHex() != null) {
String downloadedKeyFp = PgpKeyHelper.convertFingerprintToHex(
downloadedKey.getPublicKey().getFingerprint());
if (downloadedKeyFp.equals(entry.getFingerPrintHex())) {
if (downloadedKeyFp.equals(entry.getFingerprintHex())) {
Log.d(Constants.TAG, "fingerprint of downloaded key is the same as " +
"the requested fingerprint!");
} else {

View File

@ -146,14 +146,19 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
holder.keyId.setText(entry.keyIdHex);
if (entry.fingerPrintHex != null) {
if (entry.fingerprintHex != null) {
holder.fingerprint.setVisibility(View.VISIBLE);
holder.fingerprint.setText(PgpKeyHelper.colorizeFingerprint(entry.fingerPrintHex));
holder.fingerprint.setText(PgpKeyHelper.colorizeFingerprint(entry.fingerprintHex));
} else {
holder.fingerprint.setVisibility(View.GONE);
}
if (entry.bitStrength != 0 && entry.algorithm != null) {
holder.algorithm.setText("" + entry.bitStrength + "/" + entry.algorithm);
holder.algorithm.setVisibility(View.VISIBLE);
} else {
holder.algorithm.setVisibility(View.INVISIBLE);
}
if (entry.revoked) {
holder.status.setVisibility(View.VISIBLE);

View File

@ -108,7 +108,7 @@ public class ImportKeysListServerLoader
* set fingerprint explicitly after query
* to enforce a check when the key is imported by KeychainIntentService
*/
uniqueEntry.setFingerPrintHex(fingerprint);
uniqueEntry.setFingerprintHex(fingerprint);
uniqueEntry.setSelected(true);
mEntryList.add(uniqueEntry);
}