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}