OtrEngine.java

  1package eu.siacs.conversations.crypto;
  2
  3import java.math.BigInteger;
  4import java.security.KeyFactory;
  5import java.security.KeyPair;
  6import java.security.KeyPairGenerator;
  7import java.security.NoSuchAlgorithmException;
  8import java.security.PrivateKey;
  9import java.security.PublicKey;
 10import java.security.spec.DSAPrivateKeySpec;
 11import java.security.spec.DSAPublicKeySpec;
 12import java.security.spec.InvalidKeySpecException;
 13
 14import org.json.JSONException;
 15import org.json.JSONObject;
 16
 17import android.util.Log;
 18
 19import eu.siacs.conversations.Config;
 20import eu.siacs.conversations.entities.Account;
 21import eu.siacs.conversations.entities.Conversation;
 22import eu.siacs.conversations.entities.Message;
 23import eu.siacs.conversations.services.XmppConnectionService;
 24import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
 25
 26import net.java.otr4j.OtrEngineHost;
 27import net.java.otr4j.OtrException;
 28import net.java.otr4j.OtrPolicy;
 29import net.java.otr4j.OtrPolicyImpl;
 30import net.java.otr4j.session.InstanceTag;
 31import net.java.otr4j.session.SessionID;
 32import net.java.otr4j.session.SessionImpl;
 33import net.java.otr4j.session.SessionStatus;
 34
 35public class OtrEngine implements OtrEngineHost {
 36
 37	private Account account;
 38	private OtrPolicy otrPolicy;
 39	private KeyPair keyPair;
 40	private XmppConnectionService mXmppConnectionService;
 41
 42	public OtrEngine(XmppConnectionService service, Account account) {
 43		this.account = account;
 44		this.otrPolicy = new OtrPolicyImpl();
 45		this.otrPolicy.setAllowV1(false);
 46		this.otrPolicy.setAllowV2(true);
 47		this.otrPolicy.setAllowV3(true);
 48		this.keyPair = loadKey(account.getKeys());
 49		this.mXmppConnectionService = service;
 50	}
 51
 52	private KeyPair loadKey(JSONObject keys) {
 53		if (keys == null) {
 54			return null;
 55		}
 56		try {
 57			BigInteger x = new BigInteger(keys.getString("otr_x"), 16);
 58			BigInteger y = new BigInteger(keys.getString("otr_y"), 16);
 59			BigInteger p = new BigInteger(keys.getString("otr_p"), 16);
 60			BigInteger q = new BigInteger(keys.getString("otr_q"), 16);
 61			BigInteger g = new BigInteger(keys.getString("otr_g"), 16);
 62			KeyFactory keyFactory = KeyFactory.getInstance("DSA");
 63			DSAPublicKeySpec pubKeySpec = new DSAPublicKeySpec(y, p, q, g);
 64			DSAPrivateKeySpec privateKeySpec = new DSAPrivateKeySpec(x, p, q, g);
 65			PublicKey publicKey = keyFactory.generatePublic(pubKeySpec);
 66			PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
 67			return new KeyPair(publicKey, privateKey);
 68		} catch (JSONException e) {
 69			return null;
 70		} catch (NoSuchAlgorithmException e) {
 71			return null;
 72		} catch (InvalidKeySpecException e) {
 73			return null;
 74		}
 75	}
 76
 77	private void saveKey() {
 78		PublicKey publicKey = keyPair.getPublic();
 79		PrivateKey privateKey = keyPair.getPrivate();
 80		KeyFactory keyFactory;
 81		try {
 82			keyFactory = KeyFactory.getInstance("DSA");
 83			DSAPrivateKeySpec privateKeySpec = keyFactory.getKeySpec(
 84					privateKey, DSAPrivateKeySpec.class);
 85			DSAPublicKeySpec publicKeySpec = keyFactory.getKeySpec(publicKey,
 86					DSAPublicKeySpec.class);
 87			this.account.setKey("otr_x", privateKeySpec.getX().toString(16));
 88			this.account.setKey("otr_g", privateKeySpec.getG().toString(16));
 89			this.account.setKey("otr_p", privateKeySpec.getP().toString(16));
 90			this.account.setKey("otr_q", privateKeySpec.getQ().toString(16));
 91			this.account.setKey("otr_y", publicKeySpec.getY().toString(16));
 92		} catch (NoSuchAlgorithmException e) {
 93			e.printStackTrace();
 94		} catch (InvalidKeySpecException e) {
 95			e.printStackTrace();
 96		}
 97
 98	}
 99
100	@Override
101	public void askForSecret(SessionID arg0, InstanceTag arg1, String arg2) {
102		// TODO Auto-generated method stub
103
104	}
105
106	@Override
107	public void finishedSessionMessage(SessionID arg0, String arg1)
108			throws OtrException {
109
110	}
111
112	@Override
113	public String getFallbackMessage(SessionID arg0) {
114		return "I would like to start a private (OTR encrypted) conversation but your client doesn’t seem to support that";
115	}
116
117	@Override
118	public byte[] getLocalFingerprintRaw(SessionID arg0) {
119		// TODO Auto-generated method stub
120		return null;
121	}
122
123	public PublicKey getPublicKey() {
124		if (this.keyPair == null) {
125			return null;
126		}
127		return this.keyPair.getPublic();
128	}
129
130	@Override
131	public KeyPair getLocalKeyPair(SessionID arg0) throws OtrException {
132		if (this.keyPair == null) {
133			KeyPairGenerator kg;
134			try {
135				kg = KeyPairGenerator.getInstance("DSA");
136				this.keyPair = kg.genKeyPair();
137				this.saveKey();
138				mXmppConnectionService.databaseBackend.updateAccount(account);
139			} catch (NoSuchAlgorithmException e) {
140				Log.d(Config.LOGTAG,
141						"error generating key pair " + e.getMessage());
142			}
143		}
144		return this.keyPair;
145	}
146
147	@Override
148	public String getReplyForUnreadableMessage(SessionID arg0) {
149		// TODO Auto-generated method stub
150		return null;
151	}
152
153	@Override
154	public OtrPolicy getSessionPolicy(SessionID arg0) {
155		return otrPolicy;
156	}
157
158	@Override
159	public void injectMessage(SessionID session, String body)
160			throws OtrException {
161		MessagePacket packet = new MessagePacket();
162		packet.setFrom(account.getFullJid());
163		if (session.getUserID().isEmpty()) {
164			packet.setTo(session.getAccountID());
165		} else {
166			packet.setTo(session.getAccountID() + "/" + session.getUserID());
167		}
168		packet.setBody(body);
169		packet.addChild("private", "urn:xmpp:carbons:2");
170		packet.addChild("no-copy", "urn:xmpp:hints");
171		packet.setType(MessagePacket.TYPE_CHAT);
172		account.getXmppConnection().sendMessagePacket(packet);
173	}
174
175	@Override
176	public void messageFromAnotherInstanceReceived(SessionID id) {
177		String jid = id.getAccountID();
178		Conversation conversation = mXmppConnectionService
179				.findOrCreateConversation(account, jid, false);
180		Message error = new Message(conversation, null, Message.ENCRYPTION_OTR);
181		conversation.getMessages().add(error);
182		error.setStatus(Message.STATUS_RECEPTION_FAILED);
183		mXmppConnectionService.databaseBackend.createMessage(error);
184		SessionImpl session = conversation.getOtrSession();
185		if (session != null
186				&& session.getSessionStatus() != SessionStatus.ENCRYPTED) {
187			try {
188				session.startSession();
189			} catch (OtrException e) {
190			}
191		}
192	}
193
194	@Override
195	public void multipleInstancesDetected(SessionID arg0) {
196		// TODO Auto-generated method stub
197
198	}
199
200	@Override
201	public void requireEncryptedMessage(SessionID arg0, String arg1)
202			throws OtrException {
203		// TODO Auto-generated method stub
204
205	}
206
207	@Override
208	public void showError(SessionID arg0, String arg1) throws OtrException {
209		// TODO Auto-generated method stub
210
211	}
212
213	@Override
214	public void smpAborted(SessionID arg0) throws OtrException {
215		// TODO Auto-generated method stub
216
217	}
218
219	@Override
220	public void smpError(SessionID arg0, int arg1, boolean arg2)
221			throws OtrException {
222		throw new OtrException(new Exception("smp error"));
223	}
224
225	@Override
226	public void unencryptedMessageReceived(SessionID arg0, String arg1)
227			throws OtrException {
228		throw new OtrException(new Exception("unencrypted message received"));
229	}
230
231	@Override
232	public void unreadableMessageReceived(SessionID arg0) throws OtrException {
233		throw new OtrException(new Exception("unreadable message received"));
234	}
235
236	@Override
237	public void unverify(SessionID arg0, String arg1) {
238		// TODO Auto-generated method stub
239
240	}
241
242	@Override
243	public void verify(SessionID arg0, String arg1, boolean arg2) {
244		// TODO Auto-generated method stub
245
246	}
247
248}