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