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