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						message.ready = true;
165						callback.success(message);
166						break;
167					case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
168						callback.userInputRequried((PendingIntent) result
169								.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
170								message);
171						break;
172					case OpenPgpApi.RESULT_CODE_ERROR:
173						callback.error(R.string.openpgp_error, message);
174						break;
175					}
176				}
177			});
178		} else if (message.getType() == Message.TYPE_IMAGE) {
179			try {
180				JingleFile inputFile = this.mXmppConnectionService
181						.getFileBackend().getJingleFile(message, true);
182				JingleFile outputFile = this.mXmppConnectionService
183						.getFileBackend().getJingleFile(message, false);
184				outputFile.createNewFile();
185				InputStream is = new FileInputStream(inputFile);
186				OutputStream os = new FileOutputStream(outputFile);
187				api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
188
189					@Override
190					public void onReturn(Intent result) {
191						switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
192								OpenPgpApi.RESULT_CODE_ERROR)) {
193						case OpenPgpApi.RESULT_CODE_SUCCESS:
194							message.ready = true;
195							callback.success(message);
196							break;
197						case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
198							callback.userInputRequried(
199									(PendingIntent) result
200											.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
201									message);
202							break;
203						case OpenPgpApi.RESULT_CODE_ERROR:
204							callback.error(R.string.openpgp_error, message);
205							break;
206						}
207					}
208				});
209			} catch (FileNotFoundException e) {
210				Log.d("xmppService", "file not found: " + e.getMessage());
211			} catch (IOException e) {
212				Log.d("xmppService", "io exception during file encrypt");
213			}
214		}
215	}
216
217	public long fetchKeyId(Account account, String status, String signature) {
218		if ((signature == null) || (api == null)) {
219			return 0;
220		}
221		if (status == null) {
222			status = "";
223		}
224		StringBuilder pgpSig = new StringBuilder();
225		pgpSig.append("-----BEGIN PGP SIGNED MESSAGE-----");
226		pgpSig.append('\n');
227		pgpSig.append('\n');
228		pgpSig.append(status);
229		pgpSig.append('\n');
230		pgpSig.append("-----BEGIN PGP SIGNATURE-----");
231		pgpSig.append('\n');
232		pgpSig.append('\n');
233		pgpSig.append(signature.replace("\n", "").trim());
234		pgpSig.append('\n');
235		pgpSig.append("-----END PGP SIGNATURE-----");
236		Intent params = new Intent();
237		params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
238		params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
239		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid());
240		InputStream is = new ByteArrayInputStream(pgpSig.toString().getBytes());
241		ByteArrayOutputStream os = new ByteArrayOutputStream();
242		Intent result = api.executeApi(params, is, os);
243		switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
244				OpenPgpApi.RESULT_CODE_ERROR)) {
245		case OpenPgpApi.RESULT_CODE_SUCCESS:
246			OpenPgpSignatureResult sigResult = result
247					.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE);
248			if (sigResult != null) {
249				return sigResult.getKeyId();
250			} else {
251				return 0;
252			}
253		case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
254			return 0;
255		case OpenPgpApi.RESULT_CODE_ERROR:
256			Log.d("xmppService",
257					"openpgp error: "
258							+ ((OpenPgpError) result
259									.getParcelableExtra(OpenPgpApi.RESULT_ERROR))
260									.getMessage());
261			return 0;
262		}
263		return 0;
264	}
265
266	public void generateSignature(final Account account, String status,
267			final UiCallback<Account> callback) {
268		Intent params = new Intent();
269		params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
270		params.setAction(OpenPgpApi.ACTION_SIGN);
271		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid());
272		InputStream is = new ByteArrayInputStream(status.getBytes());
273		final OutputStream os = new ByteArrayOutputStream();
274		api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
275
276			@Override
277			public void onReturn(Intent result) {
278				switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
279				case OpenPgpApi.RESULT_CODE_SUCCESS:
280					StringBuilder signatureBuilder = new StringBuilder();
281					String[] lines = os.toString().split("\n");
282					for (int i = 7; i < lines.length - 1; ++i) {
283						signatureBuilder.append(lines[i].trim());
284					}
285					account.setKey("pgp_signature", signatureBuilder.toString());
286					callback.success(account);
287					return;
288				case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
289					callback.userInputRequried((PendingIntent) result
290							.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
291							account);
292					return;
293				case OpenPgpApi.RESULT_CODE_ERROR:
294					callback.error(R.string.openpgp_error, account);
295					return;
296				}
297			}
298		});
299	}
300
301	public void hasKey(final Contact contact, final UiCallback<Contact> callback) {
302		Intent params = new Intent();
303		params.setAction(OpenPgpApi.ACTION_GET_KEY);
304		params.putExtra(OpenPgpApi.EXTRA_KEY_ID, contact.getPgpKeyId());
305		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, contact.getAccount()
306				.getJid());
307		api.executeApiAsync(params, null, null, new IOpenPgpCallback() {
308
309			@Override
310			public void onReturn(Intent result) {
311				switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
312				case OpenPgpApi.RESULT_CODE_SUCCESS:
313					callback.success(contact);
314					return;
315				case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
316					callback.userInputRequried((PendingIntent) result
317							.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
318							contact);
319					return;
320				case OpenPgpApi.RESULT_CODE_ERROR:
321					callback.error(R.string.openpgp_error, contact);
322					return;
323				}
324			}
325		});
326	}
327
328	public PendingIntent getIntentForKey(Contact contact) {
329		Intent params = new Intent();
330		params.setAction(OpenPgpApi.ACTION_GET_KEY);
331		params.putExtra(OpenPgpApi.EXTRA_KEY_ID, contact.getPgpKeyId());
332		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, contact.getAccount()
333				.getJid());
334		Intent result = api.executeApi(params, null, null);
335		return (PendingIntent) result
336				.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
337	}
338
339	public PendingIntent getIntentForKey(Account account, long pgpKeyId) {
340		Intent params = new Intent();
341		params.setAction(OpenPgpApi.ACTION_GET_KEY);
342		params.putExtra(OpenPgpApi.EXTRA_KEY_ID, pgpKeyId);
343		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid());
344		Intent result = api.executeApi(params, null, null);
345		return (PendingIntent) result
346				.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
347	}
348}