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(final Message message,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_ACCOUNT_NAME, message.getConversation().getAccount().getJid());
123		
124		if (message.getType() == Message.TYPE_TEXT) {
125			params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
126	
127			InputStream is = new ByteArrayInputStream(message.getBody().getBytes());
128			final OutputStream os = new ByteArrayOutputStream();
129			api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
130				
131				@Override
132				public void onReturn(Intent result) {
133					switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
134							OpenPgpApi.RESULT_CODE_ERROR)) {
135					case OpenPgpApi.RESULT_CODE_SUCCESS:
136						StringBuilder encryptedMessageBody = new StringBuilder();
137						String[] lines = os.toString().split("\n");
138						for (int i = 3; i < lines.length - 1; ++i) {
139							encryptedMessageBody.append(lines[i].trim());
140						}
141						message.setEncryptedBody(encryptedMessageBody.toString());
142						callback.success();
143						break;
144					case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
145						callback.userInputRequried((PendingIntent) result
146								.getParcelableExtra(OpenPgpApi.RESULT_INTENT));
147						break;
148					case OpenPgpApi.RESULT_CODE_ERROR:
149						callback.error((OpenPgpError) result
150								.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
151						break;
152					}
153				}
154			});
155		} else if (message.getType() == Message.TYPE_IMAGE) {
156			try {
157				JingleFile inputFile = this.mXmppConnectionService.getFileBackend().getJingleFile(message, true);
158				JingleFile outputFile = this.mXmppConnectionService.getFileBackend().getJingleFile(message, false);
159				outputFile.createNewFile();
160				InputStream is = new FileInputStream(inputFile);
161				OutputStream os = new FileOutputStream(outputFile);
162				api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
163					
164					@Override
165					public void onReturn(Intent result) {
166						switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
167								OpenPgpApi.RESULT_CODE_ERROR)) {
168						case OpenPgpApi.RESULT_CODE_SUCCESS:
169							callback.success();
170							break;
171						case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
172							callback.userInputRequried((PendingIntent) result
173									.getParcelableExtra(OpenPgpApi.RESULT_INTENT));
174							break;
175						case OpenPgpApi.RESULT_CODE_ERROR:
176							callback.error((OpenPgpError) result
177									.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
178							break;
179						}
180					}
181				});
182			} catch (FileNotFoundException e) {
183				Log.d("xmppService","file not found: "+e.getMessage());
184			} catch (IOException e) {
185				Log.d("xmppService","io exception during file encrypt");
186			}
187		}
188	}
189	
190	public long fetchKeyId(Account account, String status, String signature) {
191		if ((signature == null) || (api == null)) {
192			return 0;
193		}
194		if (status == null) {
195			status = "";
196		}
197		StringBuilder pgpSig = new StringBuilder();
198		pgpSig.append("-----BEGIN PGP SIGNED MESSAGE-----");
199		pgpSig.append('\n');
200		pgpSig.append('\n');
201		pgpSig.append(status);
202		pgpSig.append('\n');
203		pgpSig.append("-----BEGIN PGP SIGNATURE-----");
204		pgpSig.append('\n');
205		pgpSig.append('\n');
206		pgpSig.append(signature.replace("\n", "").trim());
207		pgpSig.append('\n');
208		pgpSig.append("-----END PGP SIGNATURE-----");
209		Intent params = new Intent();
210		params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
211		params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
212		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid());
213		InputStream is = new ByteArrayInputStream(pgpSig.toString().getBytes());
214		ByteArrayOutputStream os = new ByteArrayOutputStream();
215		Intent result = api.executeApi(params, is, os);
216		switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
217				OpenPgpApi.RESULT_CODE_ERROR)) {
218		case OpenPgpApi.RESULT_CODE_SUCCESS:
219			OpenPgpSignatureResult sigResult = result
220					.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE);
221			if (sigResult != null) {
222				return sigResult.getKeyId();
223			} else {
224				return 0;
225			}
226		case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
227			Log.d("xmppService","openpgp user interaction requeried");
228			return 0;
229		case OpenPgpApi.RESULT_CODE_ERROR:
230			Log.d("xmppService","openpgp error: "+((OpenPgpError) result
231							.getParcelableExtra(OpenPgpApi.RESULT_ERROR)).getMessage());
232			return 0;
233		}
234		return 0;
235	}
236
237	public void generateSignature(final Account account, String status,
238			final OnPgpEngineResult callback) {
239		Intent params = new Intent();
240		params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
241		params.setAction(OpenPgpApi.ACTION_SIGN);
242		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid());
243		InputStream is = new ByteArrayInputStream(status.getBytes());
244		final OutputStream os = new ByteArrayOutputStream();
245		api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
246
247			@Override
248			public void onReturn(Intent result) {
249				switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
250				case OpenPgpApi.RESULT_CODE_SUCCESS:
251					StringBuilder signatureBuilder = new StringBuilder();
252					String[] lines = os.toString().split("\n");
253					for (int i = 7; i < lines.length - 1; ++i) {
254						signatureBuilder.append(lines[i].trim());
255					}
256					account.setKey("pgp_signature", signatureBuilder.toString());
257					callback.success();
258					return;
259				case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
260					callback.userInputRequried((PendingIntent) result
261							.getParcelableExtra(OpenPgpApi.RESULT_INTENT));
262					return;
263				case OpenPgpApi.RESULT_CODE_ERROR:
264					callback.error((OpenPgpError) result
265							.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
266					return;
267				}
268			}
269		});
270	}
271	
272	public void hasKey(Contact contact, final OnPgpEngineResult callback) {
273		Intent params = new Intent();
274		params.setAction(OpenPgpApi.ACTION_GET_KEY);
275		params.putExtra(OpenPgpApi.EXTRA_KEY_ID, contact.getPgpKeyId());
276		params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, contact.getAccount().getJid());
277		api.executeApiAsync(params, null, null, new IOpenPgpCallback() {
278			
279			@Override
280			public void onReturn(Intent result) {
281				switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
282				case OpenPgpApi.RESULT_CODE_SUCCESS:
283					callback.success();
284					return;
285				case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
286					callback.userInputRequried((PendingIntent) result
287							.getParcelableExtra(OpenPgpApi.RESULT_INTENT));
288					return;
289				case OpenPgpApi.RESULT_CODE_ERROR:
290					callback.error((OpenPgpError) result
291							.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
292					return;
293				}
294			}
295		});
296	}
297	
298	public PendingIntent getIntentForKey(Contact contact) {
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().getJid());
303		Intent result = api.executeApi(params, null, null);
304		return (PendingIntent) result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
305	}
306}