parse otr-fingerprint in qr codes and nfc. include otr fingerprint in shareable uri where ever possible

This commit is contained in:
iNPUTmice 2014-11-16 00:20:20 +01:00
parent 0fb1772f18
commit 88c3537b68
11 changed files with 119 additions and 69 deletions

View file

@ -280,10 +280,11 @@ public class Account extends AbstractEntity {
return values; return values;
} }
public OtrEngine getOtrEngine(XmppConnectionService context) { public void initOtrEngine(XmppConnectionService context) {
if (otrEngine == null) { this.otrEngine = new OtrEngine(context, this);
otrEngine = new OtrEngine(context, this); }
}
public OtrEngine getOtrEngine() {
return this.otrEngine; return this.otrEngine;
} }
@ -298,23 +299,21 @@ public class Account extends AbstractEntity {
public String getOtrFingerprint() { public String getOtrFingerprint() {
if (this.otrFingerprint == null) { if (this.otrFingerprint == null) {
try { try {
DSAPublicKey pubkey = (DSAPublicKey) this.otrEngine if (this.otrEngine == null) {
.getPublicKey();
if (pubkey == null) {
return null; return null;
} }
StringBuilder builder = new StringBuilder( DSAPublicKey publicKey = (DSAPublicKey) this.otrEngine.getPublicKey();
new OtrCryptoEngineImpl().getFingerprint(pubkey)); if (publicKey == null) {
builder.insert(8, " "); return null;
builder.insert(17, " "); }
builder.insert(26, " "); this.otrFingerprint = new OtrCryptoEngineImpl().getFingerprint(publicKey);
builder.insert(35, " "); return this.otrFingerprint;
this.otrFingerprint = builder.toString();
} catch (final OtrCryptoException ignored) { } catch (final OtrCryptoException ignored) {
return null;
} }
} else {
return this.otrFingerprint;
} }
return this.otrFingerprint;
} }
public String getRosterVersion() { public String getRosterVersion() {
@ -329,11 +328,6 @@ public class Account extends AbstractEntity {
this.rosterVersion = version; this.rosterVersion = version;
} }
public String getOtrFingerprint(XmppConnectionService service) {
this.getOtrEngine(service);
return this.getOtrFingerprint();
}
public void updatePresence(String resource, int status) { public void updatePresence(String resource, int status) {
this.presences.updatePresence(resource, status); this.presences.updatePresence(resource, status);
} }
@ -411,4 +405,13 @@ public class Account extends AbstractEntity {
public boolean inGracePeriod() { public boolean inGracePeriod() {
return SystemClock.elapsedRealtime() < this.mEndGracePeriod; return SystemClock.elapsedRealtime() < this.mEndGracePeriod;
} }
public String getShareableUri() {
String fingerprint = this.getOtrFingerprint();
if (fingerprint != null) {
return "xmpp:" + this.getJid().toBareJid().toString() + "?otr-fingerprint="+fingerprint;
} else {
return "xmpp:" + this.getJid().toBareJid().toString();
}
}
} }

View file

@ -7,6 +7,7 @@ import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -206,24 +207,26 @@ public class Contact implements ListItem {
return systemAccount; return systemAccount;
} }
public Set<String> getOtrFingerprints() { public ArrayList<String> getOtrFingerprints() {
Set<String> set = new HashSet<>(); ArrayList<String> fingerprints = new ArrayList<String>();
try { try {
if (this.keys.has("otr_fingerprints")) { if (this.keys.has("otr_fingerprints")) {
JSONArray fingerprints = this.keys JSONArray prints = this.keys
.getJSONArray("otr_fingerprints"); .getJSONArray("otr_fingerprints");
for (int i = 0; i < fingerprints.length(); ++i) { for (int i = 0; i < prints.length(); ++i) {
set.add(fingerprints.getString(i)); fingerprints.add(prints.getString(i));
} }
} }
} catch (JSONException e) { } catch (final JSONException ignored) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
return set; return fingerprints;
} }
public void addOtrFingerprint(String print) { public boolean addOtrFingerprint(String print) {
if (getOtrFingerprints().contains(print)) {
return false;
}
try { try {
JSONArray fingerprints; JSONArray fingerprints;
if (!this.keys.has("otr_fingerprints")) { if (!this.keys.has("otr_fingerprints")) {
@ -234,8 +237,9 @@ public class Contact implements ListItem {
} }
fingerprints.put(print); fingerprints.put(print);
this.keys.put("otr_fingerprints", fingerprints); this.keys.put("otr_fingerprints", fingerprints);
return true;
} catch (final JSONException ignored) { } catch (final JSONException ignored) {
return false;
} }
} }
@ -396,4 +400,13 @@ public class Contact implements ListItem {
public boolean trusted() { public boolean trusted() {
return getOption(Options.FROM) && getOption(Options.TO); return getOption(Options.FROM) && getOption(Options.TO);
} }
public String getShareableUri() {
if (getOtrFingerprints().size() >= 1) {
String otr = getOtrFingerprints().get(0);
return "xmpp:"+getJid().toBareJid().toString()+"?otr-fingerprint="+otr.replace(" ","");
} else {
return "xmpp:"+getJid().toBareJid().toString();
}
}
} }

View file

@ -240,16 +240,14 @@ public class Conversation extends AbstractEntity {
this.mode = mode; this.mode = mode;
} }
public SessionImpl startOtrSession(XmppConnectionService service, public SessionImpl startOtrSession(String presence, boolean sendStart) {
String presence, boolean sendStart) {
if (this.otrSession != null) { if (this.otrSession != null) {
return this.otrSession; return this.otrSession;
} else { } else {
final SessionID sessionId = new SessionID(this.getContactJid().toBareJid().toString(), final SessionID sessionId = new SessionID(this.getContactJid().toBareJid().toString(),
presence, presence,
"xmpp"); "xmpp");
this.otrSession = new SessionImpl(sessionId, getAccount() this.otrSession = new SessionImpl(sessionId, getAccount().getOtrEngine());
.getOtrEngine(service));
try { try {
if (sendStart) { if (sendStart) {
this.otrSession.startSession(); this.otrSession.startSession();

View file

@ -76,8 +76,7 @@ public class MessageParser extends AbstractParser implements
} }
if (!conversation.hasValidOtrSession()) { if (!conversation.hasValidOtrSession()) {
if (properlyAddressed) { if (properlyAddressed) {
conversation.startOtrSession(mXmppConnectionService, presence, conversation.startOtrSession(presence,false);
false);
} else { } else {
return null; return null;
} }
@ -87,8 +86,7 @@ public class MessageParser extends AbstractParser implements
if (!foreignPresence.equals(presence)) { if (!foreignPresence.equals(presence)) {
conversation.endOtrIfNeeded(); conversation.endOtrIfNeeded();
if (properlyAddressed) { if (properlyAddressed) {
conversation.startOtrSession(mXmppConnectionService, conversation.startOtrSession(presence, false);
presence, false);
} else { } else {
return null; return null;
} }

View file

@ -491,6 +491,7 @@ public class XmppConnectionService extends Service {
this.accounts = databaseBackend.getAccounts(); this.accounts = databaseBackend.getAccounts();
for (Account account : this.accounts) { for (Account account : this.accounts) {
account.initOtrEngine(this);
this.databaseBackend.readRoster(account.getRoster()); this.databaseBackend.readRoster(account.getRoster());
} }
this.mergePhoneContactsWithRoster(); this.mergePhoneContactsWithRoster();
@ -608,8 +609,7 @@ public class XmppConnectionService extends Service {
if (message.getCounterpart() != null) { if (message.getCounterpart() != null) {
if (message.getEncryption() == Message.ENCRYPTION_OTR) { if (message.getEncryption() == Message.ENCRYPTION_OTR) {
if (!conv.hasValidOtrSession()) { if (!conv.hasValidOtrSession()) {
conv.startOtrSession(this, message.getCounterpart().getResourcepart(), conv.startOtrSession(message.getCounterpart().getResourcepart(),true);
true);
message.setStatus(Message.STATUS_WAITING); message.setStatus(Message.STATUS_WAITING);
} else if (conv.hasValidOtrSession() } else if (conv.hasValidOtrSession()
&& conv.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) { && conv.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) {
@ -628,7 +628,7 @@ public class XmppConnectionService extends Service {
} else { } else {
if (message.getEncryption() == Message.ENCRYPTION_OTR) { if (message.getEncryption() == Message.ENCRYPTION_OTR) {
if (!conv.hasValidOtrSession() && (message.getCounterpart() != null)) { if (!conv.hasValidOtrSession() && (message.getCounterpart() != null)) {
conv.startOtrSession(this, message.getCounterpart().getResourcepart(), true); conv.startOtrSession(message.getCounterpart().getResourcepart(), true);
message.setStatus(Message.STATUS_WAITING); message.setStatus(Message.STATUS_WAITING);
} else if (conv.hasValidOtrSession()) { } else if (conv.hasValidOtrSession()) {
if (conv.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) { if (conv.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) {
@ -672,7 +672,7 @@ public class XmppConnectionService extends Service {
} else if (message.getEncryption() == Message.ENCRYPTION_OTR) { } else if (message.getEncryption() == Message.ENCRYPTION_OTR) {
if (!conv.hasValidOtrSession() if (!conv.hasValidOtrSession()
&& message.getCounterpart() != null) { && message.getCounterpart() != null) {
conv.startOtrSession(this, message.getCounterpart().getResourcepart(), false); conv.startOtrSession(message.getCounterpart().getResourcepart(), false);
} }
} }
} }
@ -709,13 +709,11 @@ public class XmppConnectionService extends Service {
if (!message.getConversation().hasValidOtrSession()) { if (!message.getConversation().hasValidOtrSession()) {
if ((message.getCounterpart() != null) if ((message.getCounterpart() != null)
&& (presences.has(message.getCounterpart().getResourcepart()))) { && (presences.has(message.getCounterpart().getResourcepart()))) {
message.getConversation().startOtrSession(this, message.getConversation().startOtrSession(message.getCounterpart().getResourcepart(), true);
message.getCounterpart().getResourcepart(), true);
} else { } else {
if (presences.size() == 1) { if (presences.size() == 1) {
String presence = presences.asStringArray()[0]; String presence = presences.asStringArray()[0];
message.getConversation().startOtrSession(this, message.getConversation().startOtrSession(presence, true);
presence, true);
} }
} }
} else { } else {
@ -1061,6 +1059,7 @@ public class XmppConnectionService extends Service {
} }
public void createAccount(Account account) { public void createAccount(Account account) {
account.initOtrEngine(this);
databaseBackend.createAccount(account); databaseBackend.createAccount(account);
this.accounts.add(account); this.accounts.add(account);
this.reconnectAccount(account, false); this.reconnectAccount(account, false);

View file

@ -154,7 +154,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
@Override @Override
protected String getShareableUri() { protected String getShareableUri() {
if (contact != null) { if (contact != null) {
return "xmpp:" + contact.getJid(); return contact.getShareableUri();
} else { } else {
return ""; return "";
} }
@ -326,10 +326,8 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
keys.removeAllViews(); keys.removeAllViews();
boolean hasKeys = false; boolean hasKeys = false;
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
for (Iterator<String> iterator = contact.getOtrFingerprints() for(final String otrFingerprint : contact.getOtrFingerprints()) {
.iterator(); iterator.hasNext(); ) {
hasKeys = true; hasKeys = true;
final String otrFingerprint = iterator.next();
View view = inflater.inflate(R.layout.contact_key, keys, false); View view = inflater.inflate(R.layout.contact_key, keys, false);
TextView key = (TextView) view.findViewById(R.id.key); TextView key = (TextView) view.findViewById(R.id.key);
TextView keyType = (TextView) view.findViewById(R.id.key_type); TextView keyType = (TextView) view.findViewById(R.id.key_type);

View file

@ -108,7 +108,7 @@ public class ConversationActivity extends XmppActivity implements
protected String getShareableUri() { protected String getShareableUri() {
Conversation conversation = getSelectedConversation(); Conversation conversation = getSelectedConversation();
if (conversation != null) { if (conversation != null) {
return "xmpp:" + conversation.getAccount().getJid().toBareJid(); return conversation.getAccount().getShareableUri();
} else { } else {
return ""; return "";
} }

View file

@ -26,6 +26,7 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate; import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
import eu.siacs.conversations.ui.adapter.KnownHostsAdapter; import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.utils.Validator; import eu.siacs.conversations.utils.Validator;
import eu.siacs.conversations.xmpp.XmppConnection.Features; import eu.siacs.conversations.xmpp.XmppConnection.Features;
@ -270,7 +271,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
@Override @Override
protected String getShareableUri() { protected String getShareableUri() {
if (mAccount!=null) { if (mAccount!=null) {
return "xmpp:"+mAccount.getJid().toBareJid(); return mAccount.getShareableUri();
} else { } else {
return ""; return "";
} }
@ -402,11 +403,10 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
} else { } else {
this.mServerInfoPep.setText(R.string.server_info_unavailable); this.mServerInfoPep.setText(R.string.server_info_unavailable);
} }
final String fingerprint = this.mAccount final String fingerprint = this.mAccount.getOtrFingerprint();
.getOtrFingerprint(xmppConnectionService);
if (fingerprint != null) { if (fingerprint != null) {
this.mOtrFingerprintBox.setVisibility(View.VISIBLE); this.mOtrFingerprintBox.setVisibility(View.VISIBLE);
this.mOtrFingerprint.setText(fingerprint); this.mOtrFingerprint.setText(CryptoHelper.prettifyFingerprint(fingerprint));
this.mOtrFingerprintToClipboardButton this.mOtrFingerprintToClipboardButton
.setVisibility(View.VISIBLE); .setVisibility(View.VISIBLE);
this.mOtrFingerprintToClipboardButton this.mOtrFingerprintToClipboardButton

View file

@ -62,6 +62,7 @@ import eu.siacs.conversations.entities.ListItem;
import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate; import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
import eu.siacs.conversations.ui.adapter.KnownHostsAdapter; import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
import eu.siacs.conversations.ui.adapter.ListItemAdapter; import eu.siacs.conversations.ui.adapter.ListItemAdapter;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.Validator; import eu.siacs.conversations.utils.Validator;
import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jid.Jid;
@ -316,7 +317,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
} }
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
protected void showCreateContactDialog(String prefilledJid) { protected void showCreateContactDialog(final String prefilledJid, final String fingerprint) {
AlertDialog.Builder builder = new AlertDialog.Builder(this); AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.create_contact); builder.setTitle(R.string.create_contact);
View dialogView = getLayoutInflater().inflate( View dialogView = getLayoutInflater().inflate(
@ -328,6 +329,12 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
android.R.layout.simple_list_item_1, mKnownHosts)); android.R.layout.simple_list_item_1, mKnownHosts));
if (prefilledJid != null) { if (prefilledJid != null) {
jid.append(prefilledJid); jid.append(prefilledJid);
if (fingerprint!=null) {
jid.setFocusable(false);
jid.setFocusableInTouchMode(false);
jid.setClickable(false);
jid.setCursorVisible(false);
}
} }
populateAccountSpinner(spinner); populateAccountSpinner(spinner);
builder.setView(dialogView); builder.setView(dialogView);
@ -367,6 +374,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
if (contact.showInRoster()) { if (contact.showInRoster()) {
jid.setError(getString(R.string.contact_already_exists)); jid.setError(getString(R.string.contact_already_exists));
} else { } else {
contact.addOtrFingerprint(fingerprint);
xmppConnectionService.createContact(contact); xmppConnectionService.createContact(contact);
dialog.dismiss(); dialog.dismiss();
switchToConversation(contact); switchToConversation(contact);
@ -511,7 +519,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.action_create_contact: case R.id.action_create_contact:
showCreateContactDialog(null); showCreateContactDialog(null,null);
return true; return true;
case R.id.action_join_conference: case R.id.action_join_conference:
showJoinConferenceDialog(null); showJoinConferenceDialog(null);
@ -615,22 +623,29 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
return false; return false;
} }
private boolean handleJid(String jid) { private boolean handleJid(Invite invite) {
List<Contact> contacts = xmppConnectionService.findContacts(jid); List<Contact> contacts = xmppConnectionService.findContacts(invite.jid);
if (contacts.size() == 0) { if (contacts.size() == 0) {
showCreateContactDialog(jid); showCreateContactDialog(invite.jid,invite.fingerprint);
return false; return false;
} else if (contacts.size() == 1) { } else if (contacts.size() == 1) {
switchToConversation(contacts.get(0)); Contact contact = contacts.get(0);
if (invite.fingerprint != null) {
if (contact.addOtrFingerprint(invite.fingerprint)) {
Log.d(Config.LOGTAG,"added new fingerprint");
xmppConnectionService.syncRosterToDisk(contact.getAccount());
}
}
switchToConversation(contact);
return true; return true;
} else { } else {
if (mMenuSearchView != null) { if (mMenuSearchView != null) {
mMenuSearchView.expandActionView(); mMenuSearchView.expandActionView();
mSearchEditText.setText(""); mSearchEditText.setText("");
mSearchEditText.append(jid); mSearchEditText.append(invite.jid);
filter(jid); filter(invite.jid);
} else { } else {
mInitialJid = jid; mInitialJid = invite.jid;
} }
return true; return true;
} }
@ -743,6 +758,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
private class Invite { private class Invite {
private String jid; private String jid;
private boolean muc; private boolean muc;
private String fingerprint;
Invite(Uri uri) { Invite(Uri uri) {
parse(uri); parse(uri);
@ -761,7 +777,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
if (muc) { if (muc) {
showJoinConferenceDialog(jid); showJoinConferenceDialog(jid);
} else { } else {
return handleJid(jid); return handleJid(this);
} }
} }
return false; return false;
@ -777,6 +793,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
} else { } else {
jid = uri.getSchemeSpecificPart().split("\\?")[0]; jid = uri.getSchemeSpecificPart().split("\\?")[0];
} }
fingerprint = parseFingerprint(uri.getQuery());
} else if ("imto".equals(scheme)) { } else if ("imto".equals(scheme)) {
// sample: imto://xmpp/jid@foo.com // sample: imto://xmpp/jid@foo.com
try { try {
@ -785,5 +802,19 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
} }
} }
} }
String parseFingerprint(String query) {
if (query == null) {
return null;
} else {
final String NEEDLE = "otr-fingerprint=";
int index = query.indexOf(NEEDLE);
if (index >= 0 && query.length() >= (NEEDLE.length() + index + 40)) {
return CryptoHelper.prettifyFingerprint(query.substring(index + NEEDLE.length(), index + NEEDLE.length() + 40));
} else {
return null;
}
}
}
} }
} }

View file

@ -16,6 +16,7 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jid.Jid;
@ -184,7 +185,7 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer
this.mVerificationAreaOne.setVisibility(View.VISIBLE); this.mVerificationAreaOne.setVisibility(View.VISIBLE);
this.mVerificationAreaTwo.setVisibility(View.VISIBLE); this.mVerificationAreaTwo.setVisibility(View.VISIBLE);
this.mErrorNoSession.setVisibility(View.GONE); this.mErrorNoSession.setVisibility(View.GONE);
this.mYourFingerprint.setText(this.mAccount.getOtrFingerprint(xmppConnectionService)); this.mYourFingerprint.setText(CryptoHelper.prettifyFingerprint(this.mAccount.getOtrFingerprint()));
this.mRemoteFingerprint.setText(this.mConversation.getOtrFingerprint()); this.mRemoteFingerprint.setText(this.mConversation.getOtrFingerprint());
this.mRemoteJid.setText(this.mConversation.getContact().getJid().toBareJid().toString()); this.mRemoteJid.setText(this.mConversation.getContact().getJid().toBareJid().toString());
Conversation.Smp smp = mConversation.smp(); Conversation.Smp smp = mConversation.smp();
@ -279,7 +280,7 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer
@Override @Override
protected String getShareableUri() { protected String getShareableUri() {
if (mAccount!=null) { if (mAccount!=null) {
return "xmpp:"+mAccount.getJid().toBareJid(); return mAccount.getShareableUri();
} else { } else {
return ""; return "";
} }

View file

@ -82,4 +82,13 @@ public class CryptoHelper {
public static String saslPrep(final String s) { public static String saslPrep(final String s) {
return saslEscape(Normalizer.normalize(s, Normalizer.Form.NFKC)); return saslEscape(Normalizer.normalize(s, Normalizer.Form.NFKC));
} }
public static String prettifyFingerprint(String fingerprint) {
StringBuilder builder = new StringBuilder(fingerprint);
builder.insert(8, " ");
builder.insert(17, " ");
builder.insert(26, " ");
builder.insert(35, " ");
return builder.toString();
}
} }