mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-24 01:32:16 -05:00
Merge pull request #627 from thi/improve-keybase.io-support
Improve keybase.io support
This commit is contained in:
commit
bbf19248ef
@ -247,7 +247,7 @@ public class HkpKeyServer extends KeyServer {
|
|||||||
// see http://bit.ly/1d4bxbk and http://bit.ly/1gD1wwr
|
// see http://bit.ly/1d4bxbk and http://bit.ly/1gD1wwr
|
||||||
String fingerprintOrKeyId = matcher.group(1);
|
String fingerprintOrKeyId = matcher.group(1);
|
||||||
if (fingerprintOrKeyId.length() > 16) {
|
if (fingerprintOrKeyId.length() > 16) {
|
||||||
entry.setFingerPrintHex(fingerprintOrKeyId.toLowerCase(Locale.US));
|
entry.setFingerprintHex(fingerprintOrKeyId.toLowerCase(Locale.US));
|
||||||
entry.setKeyIdHex("0x" + fingerprintOrKeyId.substring(fingerprintOrKeyId.length()
|
entry.setKeyIdHex("0x" + fingerprintOrKeyId.substring(fingerprintOrKeyId.length()
|
||||||
- 16, fingerprintOrKeyId.length()));
|
- 16, fingerprintOrKeyId.length()));
|
||||||
} else {
|
} else {
|
||||||
|
@ -45,11 +45,12 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
|
|||||||
public String keyIdHex;
|
public String keyIdHex;
|
||||||
public boolean revoked;
|
public boolean revoked;
|
||||||
public Date date; // TODO: not displayed
|
public Date date; // TODO: not displayed
|
||||||
public String fingerPrintHex;
|
public String fingerprintHex;
|
||||||
public int bitStrength;
|
public int bitStrength;
|
||||||
public String algorithm;
|
public String algorithm;
|
||||||
public boolean secretKey;
|
public boolean secretKey;
|
||||||
public String mPrimaryUserId;
|
public String mPrimaryUserId;
|
||||||
|
private String mExtraData;
|
||||||
|
|
||||||
private boolean mSelected;
|
private boolean mSelected;
|
||||||
|
|
||||||
@ -66,7 +67,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
|
|||||||
dest.writeLong(keyId);
|
dest.writeLong(keyId);
|
||||||
dest.writeByte((byte) (revoked ? 1 : 0));
|
dest.writeByte((byte) (revoked ? 1 : 0));
|
||||||
dest.writeSerializable(date);
|
dest.writeSerializable(date);
|
||||||
dest.writeString(fingerPrintHex);
|
dest.writeString(fingerprintHex);
|
||||||
dest.writeString(keyIdHex);
|
dest.writeString(keyIdHex);
|
||||||
dest.writeInt(bitStrength);
|
dest.writeInt(bitStrength);
|
||||||
dest.writeString(algorithm);
|
dest.writeString(algorithm);
|
||||||
@ -74,6 +75,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
|
|||||||
dest.writeByte((byte) (mSelected ? 1 : 0));
|
dest.writeByte((byte) (mSelected ? 1 : 0));
|
||||||
dest.writeInt(mBytes.length);
|
dest.writeInt(mBytes.length);
|
||||||
dest.writeByteArray(mBytes);
|
dest.writeByteArray(mBytes);
|
||||||
|
dest.writeString(mExtraData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Creator<ImportKeysListEntry> CREATOR = new Creator<ImportKeysListEntry>() {
|
public static final Creator<ImportKeysListEntry> CREATOR = new Creator<ImportKeysListEntry>() {
|
||||||
@ -85,7 +87,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
|
|||||||
vr.keyId = source.readLong();
|
vr.keyId = source.readLong();
|
||||||
vr.revoked = source.readByte() == 1;
|
vr.revoked = source.readByte() == 1;
|
||||||
vr.date = (Date) source.readSerializable();
|
vr.date = (Date) source.readSerializable();
|
||||||
vr.fingerPrintHex = source.readString();
|
vr.fingerprintHex = source.readString();
|
||||||
vr.keyIdHex = source.readString();
|
vr.keyIdHex = source.readString();
|
||||||
vr.bitStrength = source.readInt();
|
vr.bitStrength = source.readInt();
|
||||||
vr.algorithm = source.readString();
|
vr.algorithm = source.readString();
|
||||||
@ -93,6 +95,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
|
|||||||
vr.mSelected = source.readByte() == 1;
|
vr.mSelected = source.readByte() == 1;
|
||||||
vr.mBytes = new byte[source.readInt()];
|
vr.mBytes = new byte[source.readInt()];
|
||||||
source.readByteArray(vr.mBytes);
|
source.readByteArray(vr.mBytes);
|
||||||
|
vr.mExtraData = source.readString();
|
||||||
|
|
||||||
return vr;
|
return vr;
|
||||||
}
|
}
|
||||||
@ -150,12 +153,12 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
|
|||||||
this.date = date;
|
this.date = date;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFingerPrintHex() {
|
public String getFingerprintHex() {
|
||||||
return fingerPrintHex;
|
return fingerprintHex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFingerPrintHex(String fingerPrintHex) {
|
public void setFingerprintHex(String fingerprintHex) {
|
||||||
this.fingerPrintHex = fingerPrintHex;
|
this.fingerprintHex = fingerprintHex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getBitStrength() {
|
public int getBitStrength() {
|
||||||
@ -198,6 +201,14 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
|
|||||||
mPrimaryUserId = uid;
|
mPrimaryUserId = uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getExtraData() {
|
||||||
|
return mExtraData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExtraData(String extraData) {
|
||||||
|
mExtraData = extraData;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for later querying from keyserver
|
* Constructor for later querying from keyserver
|
||||||
*/
|
*/
|
||||||
@ -260,7 +271,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
|
|||||||
this.keyIdHex = PgpKeyHelper.convertKeyIdToHex(keyId);
|
this.keyIdHex = PgpKeyHelper.convertKeyIdToHex(keyId);
|
||||||
|
|
||||||
this.revoked = key.isRevoked();
|
this.revoked = key.isRevoked();
|
||||||
this.fingerPrintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint());
|
this.fingerprintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint());
|
||||||
this.bitStrength = key.getBitStrength();
|
this.bitStrength = key.getBitStrength();
|
||||||
final int algorithm = key.getAlgorithm();
|
final int algorithm = key.getAlgorithm();
|
||||||
this.algorithm = PgpKeyHelper.getAlgorithmInfo(context, algorithm);
|
this.algorithm = PgpKeyHelper.getAlgorithmInfo(context, algorithm);
|
||||||
|
@ -41,6 +41,11 @@ public class KeybaseKeyServer extends KeyServer {
|
|||||||
InsufficientQuery {
|
InsufficientQuery {
|
||||||
ArrayList<ImportKeysListEntry> results = new ArrayList<ImportKeysListEntry>();
|
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);
|
JSONObject fromQuery = getFromKeybase("_/api/1.0/user/autocomplete.json?q=", query);
|
||||||
try {
|
try {
|
||||||
|
|
||||||
@ -50,23 +55,32 @@ public class KeybaseKeyServer extends KeyServer {
|
|||||||
|
|
||||||
// only list them if they have a key
|
// only list them if they have a key
|
||||||
if (JWalk.optObject(match, "components", "key_fingerprint") != null) {
|
if (JWalk.optObject(match, "components", "key_fingerprint") != null) {
|
||||||
results.add(makeEntry(match));
|
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) {
|
} catch (Exception e) {
|
||||||
|
Log.e(Constants.TAG, "keybase result parsing error", e);
|
||||||
throw new QueryException("Unexpected structure in keybase search result: " + e.getMessage());
|
throw new QueryException("Unexpected structure in keybase search result: " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
private JSONObject getUser(String keybaseID) throws QueryException {
|
private JSONObject getUser(String keybaseId) throws QueryException {
|
||||||
try {
|
try {
|
||||||
return getFromKeybase("_/api/1.0/user/lookup.json?username=", keybaseID);
|
return getFromKeybase("_/api/1.0/user/lookup.json?username=", keybaseId);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
String detail = "";
|
String detail = "";
|
||||||
if (keybaseID != null) {
|
if (keybaseId != null) {
|
||||||
detail = ". Query was for user '" + keybaseID + "'";
|
detail = ". Query was for user '" + keybaseId + "'";
|
||||||
}
|
}
|
||||||
throw new QueryException(e.getMessage() + detail);
|
throw new QueryException(e.getMessage() + detail);
|
||||||
}
|
}
|
||||||
@ -74,35 +88,50 @@ public class KeybaseKeyServer extends KeyServer {
|
|||||||
|
|
||||||
private ImportKeysListEntry makeEntry(JSONObject match) throws QueryException, JSONException {
|
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();
|
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
|
// TODO: Fix; have suggested keybase provide this value to avoid search-time crypto calls
|
||||||
entry.setBitStrength(4096);
|
//entry.setBitStrength(4096);
|
||||||
entry.setAlgorithm("RSA");
|
//entry.setAlgorithm("RSA");
|
||||||
entry.setKeyIdHex("0x" + key_fingerprint);
|
|
||||||
entry.setRevoked(false);
|
|
||||||
|
|
||||||
// ctime
|
entry.setFingerprintHex(fingerprint);
|
||||||
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());
|
|
||||||
|
|
||||||
// key bits
|
// key data
|
||||||
// we have to fetch the user object to construct the search-result list, so we might as
|
// currently there's no need to query the user right away, and it should be avoided, so the
|
||||||
// well (weakly) remember the key, in case they try to import it
|
// user doesn't experience lag and doesn't download many keys unnecessarily, but should we
|
||||||
mKeyCache.put(keybaseID, JWalk.getString(match,"them", "public_keys", "primary", "bundle"));
|
// 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>();
|
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(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.setUserIds(userIds);
|
||||||
entry.setPrimaryUserId(name);
|
entry.setPrimaryUserId(name);
|
||||||
return entry;
|
return entry;
|
||||||
|
@ -749,8 +749,8 @@ public class KeychainIntentService extends IntentService
|
|||||||
KeybaseKeyServer server = new KeybaseKeyServer();
|
KeybaseKeyServer server = new KeybaseKeyServer();
|
||||||
for (ImportKeysListEntry entry : entries) {
|
for (ImportKeysListEntry entry : entries) {
|
||||||
// the keybase handle is in userId(1)
|
// the keybase handle is in userId(1)
|
||||||
String keybaseID = entry.getUserIds().get(1);
|
String keybaseId = entry.getExtraData();
|
||||||
byte[] downloadedKeyBytes = server.get(keybaseID).getBytes();
|
byte[] downloadedKeyBytes = server.get(keybaseId).getBytes();
|
||||||
|
|
||||||
// create PGPKeyRing object based on downloaded armored key
|
// create PGPKeyRing object based on downloaded armored key
|
||||||
PGPKeyRing downloadedKey = null;
|
PGPKeyRing downloadedKey = null;
|
||||||
@ -802,8 +802,8 @@ public class KeychainIntentService extends IntentService
|
|||||||
for (ImportKeysListEntry entry : entries) {
|
for (ImportKeysListEntry entry : entries) {
|
||||||
// if available use complete fingerprint for get request
|
// if available use complete fingerprint for get request
|
||||||
byte[] downloadedKeyBytes;
|
byte[] downloadedKeyBytes;
|
||||||
if (entry.getFingerPrintHex() != null) {
|
if (entry.getFingerprintHex() != null) {
|
||||||
downloadedKeyBytes = server.get("0x" + entry.getFingerPrintHex()).getBytes();
|
downloadedKeyBytes = server.get("0x" + entry.getFingerprintHex()).getBytes();
|
||||||
} else {
|
} else {
|
||||||
downloadedKeyBytes = server.get(entry.getKeyIdHex()).getBytes();
|
downloadedKeyBytes = server.get(entry.getKeyIdHex()).getBytes();
|
||||||
}
|
}
|
||||||
@ -829,10 +829,10 @@ public class KeychainIntentService extends IntentService
|
|||||||
}
|
}
|
||||||
|
|
||||||
// verify downloaded key by comparing fingerprints
|
// verify downloaded key by comparing fingerprints
|
||||||
if (entry.getFingerPrintHex() != null) {
|
if (entry.getFingerprintHex() != null) {
|
||||||
String downloadedKeyFp = PgpKeyHelper.convertFingerprintToHex(
|
String downloadedKeyFp = PgpKeyHelper.convertFingerprintToHex(
|
||||||
downloadedKey.getPublicKey().getFingerprint());
|
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 " +
|
Log.d(Constants.TAG, "fingerprint of downloaded key is the same as " +
|
||||||
"the requested fingerprint!");
|
"the requested fingerprint!");
|
||||||
} else {
|
} else {
|
||||||
|
@ -146,14 +146,19 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
|
|||||||
|
|
||||||
holder.keyId.setText(entry.keyIdHex);
|
holder.keyId.setText(entry.keyIdHex);
|
||||||
|
|
||||||
if (entry.fingerPrintHex != null) {
|
if (entry.fingerprintHex != null) {
|
||||||
holder.fingerprint.setVisibility(View.VISIBLE);
|
holder.fingerprint.setVisibility(View.VISIBLE);
|
||||||
holder.fingerprint.setText(PgpKeyHelper.colorizeFingerprint(entry.fingerPrintHex));
|
holder.fingerprint.setText(PgpKeyHelper.colorizeFingerprint(entry.fingerprintHex));
|
||||||
} else {
|
} else {
|
||||||
holder.fingerprint.setVisibility(View.GONE);
|
holder.fingerprint.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.algorithm.setText("" + entry.bitStrength + "/" + entry.algorithm);
|
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) {
|
if (entry.revoked) {
|
||||||
holder.status.setVisibility(View.VISIBLE);
|
holder.status.setVisibility(View.VISIBLE);
|
||||||
|
@ -108,7 +108,7 @@ public class ImportKeysListServerLoader
|
|||||||
* set fingerprint explicitly after query
|
* set fingerprint explicitly after query
|
||||||
* to enforce a check when the key is imported by KeychainIntentService
|
* to enforce a check when the key is imported by KeychainIntentService
|
||||||
*/
|
*/
|
||||||
uniqueEntry.setFingerPrintHex(fingerprint);
|
uniqueEntry.setFingerprintHex(fingerprint);
|
||||||
uniqueEntry.setSelected(true);
|
uniqueEntry.setSelected(true);
|
||||||
mEntryList.add(uniqueEntry);
|
mEntryList.add(uniqueEntry);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user