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 Log.d("xmppService","openpgp user interaction requeried");
225 return 0;
226 case OpenPgpApi.RESULT_CODE_ERROR:
227 Log.d("xmppService","openpgp error: "+((OpenPgpError) result
228 .getParcelableExtra(OpenPgpApi.RESULT_ERROR)).getMessage());
229 return 0;
230 }
231 return 0;
232 }
233
234 public void generateSignature(final Account account, String status,
235 final UiCallback callback) {
236 Intent params = new Intent();
237 params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
238 params.setAction(OpenPgpApi.ACTION_SIGN);
239 params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid());
240 InputStream is = new ByteArrayInputStream(status.getBytes());
241 final OutputStream os = new ByteArrayOutputStream();
242 api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
243
244 @Override
245 public void onReturn(Intent result) {
246 switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
247 case OpenPgpApi.RESULT_CODE_SUCCESS:
248 StringBuilder signatureBuilder = new StringBuilder();
249 String[] lines = os.toString().split("\n");
250 for (int i = 7; i < lines.length - 1; ++i) {
251 signatureBuilder.append(lines[i].trim());
252 }
253 account.setKey("pgp_signature", signatureBuilder.toString());
254 callback.success();
255 return;
256 case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
257 callback.userInputRequried((PendingIntent) result
258 .getParcelableExtra(OpenPgpApi.RESULT_INTENT));
259 return;
260 case OpenPgpApi.RESULT_CODE_ERROR:
261 callback.error(R.string.openpgp_error);
262 return;
263 }
264 }
265 });
266 }
267
268 public void hasKey(Contact contact, final UiCallback callback) {
269 Intent params = new Intent();
270 params.setAction(OpenPgpApi.ACTION_GET_KEY);
271 params.putExtra(OpenPgpApi.EXTRA_KEY_ID, contact.getPgpKeyId());
272 params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, contact.getAccount().getJid());
273 api.executeApiAsync(params, null, null, new IOpenPgpCallback() {
274
275 @Override
276 public void onReturn(Intent result) {
277 switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
278 case OpenPgpApi.RESULT_CODE_SUCCESS:
279 callback.success();
280 return;
281 case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
282 callback.userInputRequried((PendingIntent) result
283 .getParcelableExtra(OpenPgpApi.RESULT_INTENT));
284 return;
285 case OpenPgpApi.RESULT_CODE_ERROR:
286 callback.error(R.string.openpgp_error);
287 return;
288 }
289 }
290 });
291 }
292
293 public PendingIntent getIntentForKey(Contact contact) {
294 Intent params = new Intent();
295 params.setAction(OpenPgpApi.ACTION_GET_KEY);
296 params.putExtra(OpenPgpApi.EXTRA_KEY_ID, contact.getPgpKeyId());
297 params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, contact.getAccount().getJid());
298 Intent result = api.executeApi(params, null, null);
299 return (PendingIntent) result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
300 }
301}