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.entities.Account;
 18import eu.siacs.conversations.entities.Contact;
 19import eu.siacs.conversations.entities.Message;
 20import eu.siacs.conversations.services.XmppConnectionService;
 21import eu.siacs.conversations.xmpp.jingle.JingleFile;
 22
 23import android.app.PendingIntent;
 24import android.content.Intent;
 25import android.graphics.BitmapFactory;
 26import android.util.Log;
 27
 28public class PgpEngine {
 29	private OpenPgpApi api;
 30	private XmppConnectionService mXmppConnectionService;
 31
 32	public PgpEngine(OpenPgpApi api, XmppConnectionService service) {
 33		this.api = api;
 34		this.mXmppConnectionService = service;
 35	}
 36
 37	public void decrypt(final Message message, final OnPgpEngineResult callback) {
 38		Log.d("xmppService","decrypting message "+message.getUuid());
 39		Intent params = new Intent();
 40		params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
 41		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, message
 42				.getConversation().getAccount().getJid());
 43		if (message.getType() == Message.TYPE_TEXT) {
 44			InputStream is = new ByteArrayInputStream(message.getBody().getBytes());
 45			final OutputStream os = new ByteArrayOutputStream();
 46			api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
 47	
 48				@Override
 49				public void onReturn(Intent result) {
 50					switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
 51							OpenPgpApi.RESULT_CODE_ERROR)) {
 52					case OpenPgpApi.RESULT_CODE_SUCCESS:
 53						message.setBody(os.toString());
 54						message.setEncryption(Message.ENCRYPTION_DECRYPTED);
 55						callback.success();
 56						return;
 57					case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
 58						callback.userInputRequried((PendingIntent) result
 59								.getParcelableExtra(OpenPgpApi.RESULT_INTENT));
 60						return;
 61					case OpenPgpApi.RESULT_CODE_ERROR:
 62						callback.error((OpenPgpError) result
 63								.getParcelableExtra(OpenPgpApi.RESULT_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((OpenPgpError) result
101									.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
102							return;
103						default:
104							return;
105						}
106					}
107				});
108			} catch (FileNotFoundException e) {
109				callback.error(new OpenPgpError(0, "file not found: "+e.getMessage()));
110			} catch (IOException e) {
111				callback.error(new OpenPgpError(0, "io exception: "+e.getMessage()));
112			}
113			
114		}
115	}
116
117	public void encrypt(Account account, final Message message,
118			final OnPgpEngineResult callback) {
119		long[] keys = { message.getConversation().getContact().getPgpKeyId() };
120		Intent params = new Intent();
121		params.setAction(OpenPgpApi.ACTION_ENCRYPT);
122		params.putExtra(OpenPgpApi.EXTRA_KEY_IDS, keys);
123		params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
124		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid());
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((OpenPgpError) result
149							.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
150					break;
151				}
152			}
153		});
154		
155	}
156	
157	public void encrypt(final Message message, final OnPgpEngineResult callback) {
158		try {
159			Log.d("xmppService","calling to encrypt file");
160			JingleFile inputFile = this.mXmppConnectionService.getFileBackend().getJingleFile(message, true);
161			JingleFile outputFile = this.mXmppConnectionService.getFileBackend().getJingleFile(message, false);
162			outputFile.createNewFile();
163			long[] keys = { message.getConversation().getContact().getPgpKeyId() };
164			Intent params = new Intent();
165			params.setAction(OpenPgpApi.ACTION_ENCRYPT);
166			params.putExtra(OpenPgpApi.EXTRA_KEY_IDS, keys);
167			params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, message.getConversation().getAccount().getJid());
168			InputStream is = new FileInputStream(inputFile);
169			OutputStream os = new FileOutputStream(outputFile);
170			api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
171				
172				@Override
173				public void onReturn(Intent result) {
174					switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
175							OpenPgpApi.RESULT_CODE_ERROR)) {
176					case OpenPgpApi.RESULT_CODE_SUCCESS:
177						callback.success();
178						break;
179					case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
180						callback.userInputRequried((PendingIntent) result
181								.getParcelableExtra(OpenPgpApi.RESULT_INTENT));
182						break;
183					case OpenPgpApi.RESULT_CODE_ERROR:
184						callback.error((OpenPgpError) result
185								.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
186						break;
187					}
188				}
189			});
190		} catch (FileNotFoundException e) {
191			Log.d("xmppService","file not found: "+e.getMessage());
192		} catch (IOException e) {
193			Log.d("xmppService","io exception during file encrypt");
194		}
195	}
196
197	public long fetchKeyId(Account account, String status, String signature) {
198		if ((signature == null) || (api == null)) {
199			return 0;
200		}
201		if (status == null) {
202			status = "";
203		}
204		StringBuilder pgpSig = new StringBuilder();
205		pgpSig.append("-----BEGIN PGP SIGNED MESSAGE-----");
206		pgpSig.append('\n');
207		pgpSig.append('\n');
208		pgpSig.append(status);
209		pgpSig.append('\n');
210		pgpSig.append("-----BEGIN PGP SIGNATURE-----");
211		pgpSig.append('\n');
212		pgpSig.append('\n');
213		pgpSig.append(signature.replace("\n", "").trim());
214		pgpSig.append('\n');
215		pgpSig.append("-----END PGP SIGNATURE-----");
216		Intent params = new Intent();
217		params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
218		params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
219		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid());
220		InputStream is = new ByteArrayInputStream(pgpSig.toString().getBytes());
221		ByteArrayOutputStream os = new ByteArrayOutputStream();
222		Intent result = api.executeApi(params, is, os);
223		switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
224				OpenPgpApi.RESULT_CODE_ERROR)) {
225		case OpenPgpApi.RESULT_CODE_SUCCESS:
226			OpenPgpSignatureResult sigResult = result
227					.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE);
228			if (sigResult != null) {
229				return sigResult.getKeyId();
230			} else {
231				return 0;
232			}
233		case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
234			Log.d("xmppService","openpgp user interaction requeried");
235			return 0;
236		case OpenPgpApi.RESULT_CODE_ERROR:
237			Log.d("xmppService","openpgp error: "+((OpenPgpError) result
238							.getParcelableExtra(OpenPgpApi.RESULT_ERROR)).getMessage());
239			return 0;
240		}
241		return 0;
242	}
243
244	public void generateSignature(final Account account, String status,
245			final OnPgpEngineResult callback) {
246		Intent params = new Intent();
247		params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
248		params.setAction(OpenPgpApi.ACTION_SIGN);
249		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid());
250		InputStream is = new ByteArrayInputStream(status.getBytes());
251		final OutputStream os = new ByteArrayOutputStream();
252		api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
253
254			@Override
255			public void onReturn(Intent result) {
256				switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
257				case OpenPgpApi.RESULT_CODE_SUCCESS:
258					StringBuilder signatureBuilder = new StringBuilder();
259					String[] lines = os.toString().split("\n");
260					for (int i = 7; i < lines.length - 1; ++i) {
261						signatureBuilder.append(lines[i].trim());
262					}
263					account.setKey("pgp_signature", signatureBuilder.toString());
264					callback.success();
265					return;
266				case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
267					callback.userInputRequried((PendingIntent) result
268							.getParcelableExtra(OpenPgpApi.RESULT_INTENT));
269					return;
270				case OpenPgpApi.RESULT_CODE_ERROR:
271					callback.error((OpenPgpError) result
272							.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
273					return;
274				}
275			}
276		});
277	}
278	
279	public void hasKey(Contact contact, final OnPgpEngineResult callback) {
280		Intent params = new Intent();
281		params.setAction(OpenPgpApi.ACTION_GET_KEY);
282		params.putExtra(OpenPgpApi.EXTRA_KEY_ID, contact.getPgpKeyId());
283		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, contact.getAccount().getJid());
284		InputStream is = new ByteArrayInputStream(new byte[0]);
285		OutputStream os = new ByteArrayOutputStream();
286		api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
287			
288			@Override
289			public void onReturn(Intent result) {
290				switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
291				case OpenPgpApi.RESULT_CODE_SUCCESS:
292					callback.success();
293					return;
294				case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
295					callback.userInputRequried((PendingIntent) result
296							.getParcelableExtra(OpenPgpApi.RESULT_INTENT));
297					return;
298				case OpenPgpApi.RESULT_CODE_ERROR:
299					callback.error((OpenPgpError) result
300							.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
301					return;
302				}
303			}
304		});
305	}
306}