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