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 InputStream is = new ByteArrayInputStream(new byte[0]);
278 OutputStream os = new ByteArrayOutputStream();
279 api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
280
281 @Override
282 public void onReturn(Intent result) {
283 switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
284 case OpenPgpApi.RESULT_CODE_SUCCESS:
285 callback.success();
286 return;
287 case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
288 callback.userInputRequried((PendingIntent) result
289 .getParcelableExtra(OpenPgpApi.RESULT_INTENT));
290 return;
291 case OpenPgpApi.RESULT_CODE_ERROR:
292 callback.error((OpenPgpError) result
293 .getParcelableExtra(OpenPgpApi.RESULT_ERROR));
294 return;
295 }
296 }
297 });
298 }
299}