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