PgpEngine.java

  1package eu.siacs.conversations.crypto;
  2
  3import java.io.ByteArrayInputStream;
  4import java.io.ByteArrayOutputStream;
  5import java.io.FileInputStream;
  6import java.io.FileNotFoundException;
  7import java.io.FileOutputStream;
  8import java.io.IOException;
  9import java.io.InputStream;
 10import java.io.OutputStream;
 11
 12import org.openintents.openpgp.OpenPgpError;
 13import org.openintents.openpgp.OpenPgpSignatureResult;
 14import org.openintents.openpgp.util.OpenPgpApi;
 15import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpCallback;
 16
 17import eu.siacs.conversations.R;
 18import eu.siacs.conversations.entities.Account;
 19import eu.siacs.conversations.entities.Contact;
 20import eu.siacs.conversations.entities.Conversation;
 21import eu.siacs.conversations.entities.Message;
 22import eu.siacs.conversations.services.XmppConnectionService;
 23import eu.siacs.conversations.ui.UiCallback;
 24import eu.siacs.conversations.xmpp.jingle.JingleFile;
 25import android.app.PendingIntent;
 26import android.content.Intent;
 27import android.graphics.BitmapFactory;
 28import android.util.Log;
 29
 30public class PgpEngine {
 31	private OpenPgpApi api;
 32	private XmppConnectionService mXmppConnectionService;
 33
 34	public PgpEngine(OpenPgpApi api, XmppConnectionService service) {
 35		this.api = api;
 36		this.mXmppConnectionService = service;
 37	}
 38
 39	public void decrypt(final Message message,
 40			final UiCallback<Message> callback) {
 41		Log.d("xmppService", "decrypting message " + message.getUuid());
 42		Intent params = new Intent();
 43		params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
 44		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, message
 45				.getConversation().getAccount().getJid());
 46		if (message.getType() == Message.TYPE_TEXT) {
 47			InputStream is = new ByteArrayInputStream(message.getBody()
 48					.getBytes());
 49			final OutputStream os = new ByteArrayOutputStream();
 50			api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
 51
 52				@Override
 53				public void onReturn(Intent result) {
 54					switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
 55							OpenPgpApi.RESULT_CODE_ERROR)) {
 56					case OpenPgpApi.RESULT_CODE_SUCCESS:
 57						message.setBody(os.toString());
 58						message.setEncryption(Message.ENCRYPTION_DECRYPTED);
 59						callback.success(message);
 60						return;
 61					case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
 62						callback.userInputRequried((PendingIntent) result
 63								.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
 64								message);
 65						return;
 66					case OpenPgpApi.RESULT_CODE_ERROR:
 67						callback.error(R.string.openpgp_error, message);
 68						return;
 69					default:
 70						return;
 71					}
 72				}
 73			});
 74		} else if (message.getType() == Message.TYPE_IMAGE) {
 75			try {
 76				final JingleFile inputFile = this.mXmppConnectionService
 77						.getFileBackend().getJingleFile(message, false);
 78				final JingleFile outputFile = this.mXmppConnectionService
 79						.getFileBackend().getJingleFile(message, true);
 80				outputFile.createNewFile();
 81				InputStream is = new FileInputStream(inputFile);
 82				OutputStream os = new FileOutputStream(outputFile);
 83				api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
 84
 85					@Override
 86					public void onReturn(Intent result) {
 87						switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
 88								OpenPgpApi.RESULT_CODE_ERROR)) {
 89						case OpenPgpApi.RESULT_CODE_SUCCESS:
 90							BitmapFactory.Options options = new BitmapFactory.Options();
 91							options.inJustDecodeBounds = true;
 92							BitmapFactory.decodeFile(
 93									outputFile.getAbsolutePath(), options);
 94							int imageHeight = options.outHeight;
 95							int imageWidth = options.outWidth;
 96							message.setBody("" + outputFile.getSize() + ","
 97									+ imageWidth + "," + imageHeight);
 98							message.setEncryption(Message.ENCRYPTION_DECRYPTED);
 99							PgpEngine.this.mXmppConnectionService
100									.updateMessage(message);
101							PgpEngine.this.mXmppConnectionService.updateUi(
102									message.getConversation(), false);
103							callback.success(message);
104							return;
105						case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
106							callback.userInputRequried(
107									(PendingIntent) result
108											.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
109									message);
110							return;
111						case OpenPgpApi.RESULT_CODE_ERROR:
112							callback.error(R.string.openpgp_error, message);
113							return;
114						default:
115							return;
116						}
117					}
118				});
119			} catch (FileNotFoundException e) {
120				callback.error(R.string.error_decrypting_file, message);
121			} catch (IOException e) {
122				callback.error(R.string.error_decrypting_file, message);
123			}
124
125		}
126	}
127
128	public void encrypt(final Message message,
129			final UiCallback<Message> callback) {
130
131		Intent params = new Intent();
132		params.setAction(OpenPgpApi.ACTION_ENCRYPT);
133		if (message.getConversation().getMode() == Conversation.MODE_SINGLE) {
134			long[] keys = { message.getConversation().getContact()
135					.getPgpKeyId() };
136			params.putExtra(OpenPgpApi.EXTRA_KEY_IDS, keys);
137		} else {
138			params.putExtra(OpenPgpApi.EXTRA_KEY_IDS, message.getConversation()
139					.getMucOptions().getPgpKeyIds());
140		}
141		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, message
142				.getConversation().getAccount().getJid());
143
144		if (message.getType() == Message.TYPE_TEXT) {
145			params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
146
147			InputStream is = new ByteArrayInputStream(message.getBody()
148					.getBytes());
149			final OutputStream os = new ByteArrayOutputStream();
150			api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
151
152				@Override
153				public void onReturn(Intent result) {
154					switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
155							OpenPgpApi.RESULT_CODE_ERROR)) {
156					case OpenPgpApi.RESULT_CODE_SUCCESS:
157						StringBuilder encryptedMessageBody = new StringBuilder();
158						String[] lines = os.toString().split("\n");
159						for (int i = 3; i < lines.length - 1; ++i) {
160							encryptedMessageBody.append(lines[i].trim());
161						}
162						message.setEncryptedBody(encryptedMessageBody
163								.toString());
164						callback.success(message);
165						break;
166					case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
167						callback.userInputRequried((PendingIntent) result
168								.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
169								message);
170						break;
171					case OpenPgpApi.RESULT_CODE_ERROR:
172						callback.error(R.string.openpgp_error, message);
173						break;
174					}
175				}
176			});
177		} else if (message.getType() == Message.TYPE_IMAGE) {
178			try {
179				JingleFile inputFile = this.mXmppConnectionService
180						.getFileBackend().getJingleFile(message, true);
181				JingleFile outputFile = this.mXmppConnectionService
182						.getFileBackend().getJingleFile(message, false);
183				outputFile.createNewFile();
184				InputStream is = new FileInputStream(inputFile);
185				OutputStream os = new FileOutputStream(outputFile);
186				api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
187
188					@Override
189					public void onReturn(Intent result) {
190						switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
191								OpenPgpApi.RESULT_CODE_ERROR)) {
192						case OpenPgpApi.RESULT_CODE_SUCCESS:
193							callback.success(message);
194							break;
195						case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
196							callback.userInputRequried(
197									(PendingIntent) result
198											.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
199									message);
200							break;
201						case OpenPgpApi.RESULT_CODE_ERROR:
202							callback.error(R.string.openpgp_error, message);
203							break;
204						}
205					}
206				});
207			} catch (FileNotFoundException e) {
208				Log.d("xmppService", "file not found: " + e.getMessage());
209			} catch (IOException e) {
210				Log.d("xmppService", "io exception during file encrypt");
211			}
212		}
213	}
214
215	public long fetchKeyId(Account account, String status, String signature) {
216		if ((signature == null) || (api == null)) {
217			return 0;
218		}
219		if (status == null) {
220			status = "";
221		}
222		StringBuilder pgpSig = new StringBuilder();
223		pgpSig.append("-----BEGIN PGP SIGNED MESSAGE-----");
224		pgpSig.append('\n');
225		pgpSig.append('\n');
226		pgpSig.append(status);
227		pgpSig.append('\n');
228		pgpSig.append("-----BEGIN PGP SIGNATURE-----");
229		pgpSig.append('\n');
230		pgpSig.append('\n');
231		pgpSig.append(signature.replace("\n", "").trim());
232		pgpSig.append('\n');
233		pgpSig.append("-----END PGP SIGNATURE-----");
234		Intent params = new Intent();
235		params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
236		params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
237		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid());
238		InputStream is = new ByteArrayInputStream(pgpSig.toString().getBytes());
239		ByteArrayOutputStream os = new ByteArrayOutputStream();
240		Intent result = api.executeApi(params, is, os);
241		switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
242				OpenPgpApi.RESULT_CODE_ERROR)) {
243		case OpenPgpApi.RESULT_CODE_SUCCESS:
244			OpenPgpSignatureResult sigResult = result
245					.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE);
246			if (sigResult != null) {
247				return sigResult.getKeyId();
248			} else {
249				return 0;
250			}
251		case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
252			return 0;
253		case OpenPgpApi.RESULT_CODE_ERROR:
254			Log.d("xmppService",
255					"openpgp error: "
256							+ ((OpenPgpError) result
257									.getParcelableExtra(OpenPgpApi.RESULT_ERROR))
258									.getMessage());
259			return 0;
260		}
261		return 0;
262	}
263
264	public void generateSignature(final Account account, String status,
265			final UiCallback<Account> callback) {
266		Intent params = new Intent();
267		params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
268		params.setAction(OpenPgpApi.ACTION_SIGN);
269		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid());
270		InputStream is = new ByteArrayInputStream(status.getBytes());
271		final OutputStream os = new ByteArrayOutputStream();
272		api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
273
274			@Override
275			public void onReturn(Intent result) {
276				switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
277				case OpenPgpApi.RESULT_CODE_SUCCESS:
278					StringBuilder signatureBuilder = new StringBuilder();
279					String[] lines = os.toString().split("\n");
280					for (int i = 7; i < lines.length - 1; ++i) {
281						signatureBuilder.append(lines[i].trim());
282					}
283					account.setKey("pgp_signature", signatureBuilder.toString());
284					callback.success(account);
285					return;
286				case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
287					callback.userInputRequried((PendingIntent) result
288							.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
289							account);
290					return;
291				case OpenPgpApi.RESULT_CODE_ERROR:
292					callback.error(R.string.openpgp_error, account);
293					return;
294				}
295			}
296		});
297	}
298
299	public void hasKey(final Contact contact, final UiCallback<Contact> callback) {
300		Intent params = new Intent();
301		params.setAction(OpenPgpApi.ACTION_GET_KEY);
302		params.putExtra(OpenPgpApi.EXTRA_KEY_ID, contact.getPgpKeyId());
303		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, contact.getAccount()
304				.getJid());
305		api.executeApiAsync(params, null, null, new IOpenPgpCallback() {
306
307			@Override
308			public void onReturn(Intent result) {
309				switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
310				case OpenPgpApi.RESULT_CODE_SUCCESS:
311					callback.success(contact);
312					return;
313				case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
314					callback.userInputRequried((PendingIntent) result
315							.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
316							contact);
317					return;
318				case OpenPgpApi.RESULT_CODE_ERROR:
319					callback.error(R.string.openpgp_error, contact);
320					return;
321				}
322			}
323		});
324	}
325
326	public PendingIntent getIntentForKey(Contact contact) {
327		Intent params = new Intent();
328		params.setAction(OpenPgpApi.ACTION_GET_KEY);
329		params.putExtra(OpenPgpApi.EXTRA_KEY_ID, contact.getPgpKeyId());
330		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, contact.getAccount()
331				.getJid());
332		Intent result = api.executeApi(params, null, null);
333		return (PendingIntent) result
334				.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
335	}
336
337	public PendingIntent getIntentForKey(Account account, long pgpKeyId) {
338		Intent params = new Intent();
339		params.setAction(OpenPgpApi.ACTION_GET_KEY);
340		params.putExtra(OpenPgpApi.EXTRA_KEY_ID, pgpKeyId);
341		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid());
342		Intent result = api.executeApi(params, null, null);
343		return (PendingIntent) result
344				.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
345	}
346}