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.updateConversationUi();
102							callback.success(message);
103							return;
104						case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
105							callback.userInputRequried(
106									(PendingIntent) result
107											.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
108									message);
109							return;
110						case OpenPgpApi.RESULT_CODE_ERROR:
111							callback.error(R.string.openpgp_error, message);
112							return;
113						default:
114							return;
115						}
116					}
117				});
118			} catch (FileNotFoundException e) {
119				callback.error(R.string.error_decrypting_file, message);
120			} catch (IOException e) {
121				callback.error(R.string.error_decrypting_file, message);
122			}
123
124		}
125	}
126
127	public void encrypt(final Message message,
128			final UiCallback<Message> callback) {
129
130		Intent params = new Intent();
131		params.setAction(OpenPgpApi.ACTION_ENCRYPT);
132		if (message.getConversation().getMode() == Conversation.MODE_SINGLE) {
133			long[] keys = { message.getConversation().getContact()
134					.getPgpKeyId() };
135			params.putExtra(OpenPgpApi.EXTRA_KEY_IDS, keys);
136		} else {
137			params.putExtra(OpenPgpApi.EXTRA_KEY_IDS, message.getConversation()
138					.getMucOptions().getPgpKeyIds());
139		}
140		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, message
141				.getConversation().getAccount().getJid());
142
143		if (message.getType() == Message.TYPE_TEXT) {
144			params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
145
146			InputStream is = new ByteArrayInputStream(message.getBody()
147					.getBytes());
148			final OutputStream os = new ByteArrayOutputStream();
149			api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
150
151				@Override
152				public void onReturn(Intent result) {
153					switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
154							OpenPgpApi.RESULT_CODE_ERROR)) {
155					case OpenPgpApi.RESULT_CODE_SUCCESS:
156						StringBuilder encryptedMessageBody = new StringBuilder();
157						String[] lines = os.toString().split("\n");
158						for (int i = 3; i < lines.length - 1; ++i) {
159							encryptedMessageBody.append(lines[i].trim());
160						}
161						message.setEncryptedBody(encryptedMessageBody
162								.toString());
163						callback.success(message);
164						break;
165					case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
166						callback.userInputRequried((PendingIntent) result
167								.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
168								message);
169						break;
170					case OpenPgpApi.RESULT_CODE_ERROR:
171						callback.error(R.string.openpgp_error, message);
172						break;
173					}
174				}
175			});
176		} else if (message.getType() == Message.TYPE_IMAGE) {
177			try {
178				JingleFile inputFile = this.mXmppConnectionService
179						.getFileBackend().getJingleFile(message, true);
180				JingleFile outputFile = this.mXmppConnectionService
181						.getFileBackend().getJingleFile(message, false);
182				outputFile.createNewFile();
183				InputStream is = new FileInputStream(inputFile);
184				OutputStream os = new FileOutputStream(outputFile);
185				api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
186
187					@Override
188					public void onReturn(Intent result) {
189						switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
190								OpenPgpApi.RESULT_CODE_ERROR)) {
191						case OpenPgpApi.RESULT_CODE_SUCCESS:
192							callback.success(message);
193							break;
194						case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
195							callback.userInputRequried(
196									(PendingIntent) result
197											.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
198									message);
199							break;
200						case OpenPgpApi.RESULT_CODE_ERROR:
201							callback.error(R.string.openpgp_error, message);
202							break;
203						}
204					}
205				});
206			} catch (FileNotFoundException e) {
207				Log.d("xmppService", "file not found: " + e.getMessage());
208			} catch (IOException e) {
209				Log.d("xmppService", "io exception during file encrypt");
210			}
211		}
212	}
213
214	public long fetchKeyId(Account account, String status, String signature) {
215		if ((signature == null) || (api == null)) {
216			return 0;
217		}
218		if (status == null) {
219			status = "";
220		}
221		StringBuilder pgpSig = new StringBuilder();
222		pgpSig.append("-----BEGIN PGP SIGNED MESSAGE-----");
223		pgpSig.append('\n');
224		pgpSig.append('\n');
225		pgpSig.append(status);
226		pgpSig.append('\n');
227		pgpSig.append("-----BEGIN PGP SIGNATURE-----");
228		pgpSig.append('\n');
229		pgpSig.append('\n');
230		pgpSig.append(signature.replace("\n", "").trim());
231		pgpSig.append('\n');
232		pgpSig.append("-----END PGP SIGNATURE-----");
233		Intent params = new Intent();
234		params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
235		params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
236		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid());
237		InputStream is = new ByteArrayInputStream(pgpSig.toString().getBytes());
238		ByteArrayOutputStream os = new ByteArrayOutputStream();
239		Intent result = api.executeApi(params, is, os);
240		switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
241				OpenPgpApi.RESULT_CODE_ERROR)) {
242		case OpenPgpApi.RESULT_CODE_SUCCESS:
243			OpenPgpSignatureResult sigResult = result
244					.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE);
245			if (sigResult != null) {
246				return sigResult.getKeyId();
247			} else {
248				return 0;
249			}
250		case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
251			return 0;
252		case OpenPgpApi.RESULT_CODE_ERROR:
253			Log.d("xmppService",
254					"openpgp error: "
255							+ ((OpenPgpError) result
256									.getParcelableExtra(OpenPgpApi.RESULT_ERROR))
257									.getMessage());
258			return 0;
259		}
260		return 0;
261	}
262
263	public void generateSignature(final Account account, String status,
264			final UiCallback<Account> callback) {
265		Intent params = new Intent();
266		params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
267		params.setAction(OpenPgpApi.ACTION_SIGN);
268		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid());
269		InputStream is = new ByteArrayInputStream(status.getBytes());
270		final OutputStream os = new ByteArrayOutputStream();
271		api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
272
273			@Override
274			public void onReturn(Intent result) {
275				switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
276				case OpenPgpApi.RESULT_CODE_SUCCESS:
277					StringBuilder signatureBuilder = new StringBuilder();
278					String[] lines = os.toString().split("\n");
279					for (int i = 7; i < lines.length - 1; ++i) {
280						signatureBuilder.append(lines[i].trim());
281					}
282					account.setKey("pgp_signature", signatureBuilder.toString());
283					callback.success(account);
284					return;
285				case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
286					callback.userInputRequried((PendingIntent) result
287							.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
288							account);
289					return;
290				case OpenPgpApi.RESULT_CODE_ERROR:
291					callback.error(R.string.openpgp_error, account);
292					return;
293				}
294			}
295		});
296	}
297
298	public void hasKey(final Contact contact, final UiCallback<Contact> callback) {
299		Intent params = new Intent();
300		params.setAction(OpenPgpApi.ACTION_GET_KEY);
301		params.putExtra(OpenPgpApi.EXTRA_KEY_ID, contact.getPgpKeyId());
302		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, contact.getAccount()
303				.getJid());
304		api.executeApiAsync(params, null, null, new IOpenPgpCallback() {
305
306			@Override
307			public void onReturn(Intent result) {
308				switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
309				case OpenPgpApi.RESULT_CODE_SUCCESS:
310					callback.success(contact);
311					return;
312				case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
313					callback.userInputRequried((PendingIntent) result
314							.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
315							contact);
316					return;
317				case OpenPgpApi.RESULT_CODE_ERROR:
318					callback.error(R.string.openpgp_error, contact);
319					return;
320				}
321			}
322		});
323	}
324
325	public PendingIntent getIntentForKey(Contact contact) {
326		Intent params = new Intent();
327		params.setAction(OpenPgpApi.ACTION_GET_KEY);
328		params.putExtra(OpenPgpApi.EXTRA_KEY_ID, contact.getPgpKeyId());
329		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, contact.getAccount()
330				.getJid());
331		Intent result = api.executeApi(params, null, null);
332		return (PendingIntent) result
333				.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
334	}
335
336	public PendingIntent getIntentForKey(Account account, long pgpKeyId) {
337		Intent params = new Intent();
338		params.setAction(OpenPgpApi.ACTION_GET_KEY);
339		params.putExtra(OpenPgpApi.EXTRA_KEY_ID, pgpKeyId);
340		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid());
341		Intent result = api.executeApi(params, null, null);
342		return (PendingIntent) result
343				.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
344	}
345}