source: trunk/src/org/expeditee/encryption/io/EncryptedExpReader.java@ 1414

Last change on this file since 1414 was 1414, checked in by bln4, 5 years ago

More amendments to the way surrogates are handled. Including renaming classic to primary.

File size: 13.3 KB
Line 
1package org.expeditee.encryption.io;
2
3import java.io.BufferedReader;
4import java.io.FileInputStream;
5import java.io.FileReader;
6import java.io.IOException;
7import java.io.InputStreamReader;
8import java.io.Reader;
9import java.lang.reflect.Method;
10import java.security.InvalidKeyException;
11import java.security.NoSuchAlgorithmException;
12import java.util.ArrayList;
13import java.util.Arrays;
14import java.util.Base64;
15import java.util.List;
16import java.util.function.BiConsumer;
17import java.util.function.Predicate;
18
19import javax.crypto.BadPaddingException;
20import javax.crypto.Cipher;
21import javax.crypto.IllegalBlockSizeException;
22import javax.crypto.NoSuchPaddingException;
23import javax.crypto.SecretKey;
24import javax.crypto.spec.SecretKeySpec;
25
26import org.expeditee.encryption.CryptographyConstants;
27import org.expeditee.encryption.items.surrogates.EncryptionDetail;
28import org.expeditee.encryption.items.surrogates.Label;
29import org.expeditee.encryption.items.surrogates.Label.LabelResult;
30import org.expeditee.gui.Frame;
31import org.expeditee.gui.FrameIO;
32import org.expeditee.io.Conversion;
33import org.expeditee.io.DefaultFrameWriter;
34import org.expeditee.io.ExpReader;
35import org.expeditee.items.Item;
36import org.expeditee.items.Text;
37import org.expeditee.settings.identity.secrets.KeyList;
38
39public class EncryptedExpReader extends ExpReader implements CryptographyConstants {
40
41 private static final String ENCRYPTED_EXP_FLAG = "EncryptedExp";
42 private static final String labelProfile = "Profile";
43 private static final String labelNone = "None";
44 private SecretKey personalKey;
45 private boolean accessDenied = false;
46
47 public static boolean isEncryptedExpediteeFile(final String path) throws IOException {
48 final BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(path), "UTF-8"));
49 final String firstLine = in.readLine();
50 in.close();
51 return firstLine.startsWith(ENCRYPTED_EXP_FLAG);
52 }
53
54 public EncryptedExpReader(String frameName) {
55 super(frameName);
56 }
57
58 public int getVersionEnc(String fullpath) {
59 try {
60 BufferedReader reader = new EncryptedLineReader(new BufferedReader(new FileReader(fullpath)));
61 String next = "";
62 // First read the header lines until we get the version number
63 while (reader.ready() && !(next = reader.readLine()).equals("Z")) {
64 if (isValidLine(next)) {
65 Character tag = getTag(next);
66 String value = getValue(next);
67 if (tag.equals('V')) {
68 reader.close();
69 return Integer.parseInt(value);
70 }
71 }
72 }
73 reader.close();
74 } catch (Exception e) {
75 }
76 return -1;
77 }
78
79 @Override
80 public Frame readFrame(final String fullPath) throws IOException {
81 Reader in = new InputStreamReader(new FileInputStream(fullPath), "UTF-8");
82 return readFrame(new EncryptedLineReader(in));
83 }
84
85 @Override
86 public Frame readFrame(BufferedReader reader) throws IOException {
87 if (accessDenied) { return null; }
88
89 _reader = reader;
90 String next = "";
91 Frame newFrame = new Frame();
92 List<DelayedAction> delayedActions = new ArrayList<DelayedAction>();
93 newFrame.setName(_frameName);
94
95 try {
96 // First read all the header lines
97 next = readTheHeader(newFrame);
98
99 // Now read all the items
100 next = readTheItems(newFrame, delayedActions);
101
102 // Read the lines
103 next = readTheLines(newFrame);
104
105 // Read the constraints
106 next = readTheConstraints();
107
108 for(DelayedAction action: delayedActions) {
109 action.exec();
110 }
111
112 // Read the stats
113 next = readTheStats(newFrame);
114 } catch (Exception e) {
115 e.printStackTrace();
116 System.out.println("Error reading frame file line: " + next + " " + e.getMessage());
117 }
118
119 _reader.close();
120 FrameIO.setSavedProperties(newFrame);
121 newFrame.setChanged(false);
122
123 return newFrame;
124 }
125
126// @Override
127// protected String readTheItems(Frame newFrame, List<DelayedAction> delayedActions) throws IOException {
128// String next = null;
129// Item currentItem = null;
130//
131// Predicate<String> endOfSection = s -> s.equals(EncryptedExpWriter.TERMINATOR + "") || s.equals(EncryptedExpWriter.TERMINATOR_WITH_CONTINUATION);
132// while (_reader.ready() && !endOfSection.test(next = _reader.readLine())) {
133// if (!isValidLine(next)) {
134// continue;
135// }
136//
137// String tag = getTagEnc(next);
138// if (next.startsWith(DefaultFrameWriter.TYPE_AND_ID_STR + " ")) {
139// currentItem = newItem(next);
140// _linePoints.put(currentItem.getID(), currentItem);
141// newFrame.addItem(currentItem);
142// currentItem.setEncryptionDetailForTag(tag + "", EncryptionDetail.UnencryptedOnSave);
143// } else if (next.startsWith("SurrogateFor")) {
144// int parentID = Integer.parseInt(next.split(" ")[1]);
145// newFrame.getItemWithID(parentID).addToSurrogates(currentItem);
146// newFrame.addToSurrogates(currentItem);
147// } else if (currentItem != null && actionShouldBeDelayed(tag.charAt(0))) {
148// delayedActions.add(new DelayedAction(currentItem, next));
149// } else if (currentItem != null) {
150// processBodyLine(currentItem, next);
151// } else {
152// System.err.println("Error while reading in frame (ExpReader): Found body line but no current item to apply it to.");
153// }
154// }
155//
156// if (next.equals(EncryptedExpWriter.TERMINATOR_WITH_CONTINUATION)) {
157// next = readTheItems(newFrame, delayedActions);
158// }
159//
160// return next;
161// }
162
163 protected String readTheItems(Frame newFrame, List<DelayedAction> delayedActions) throws IOException {
164 // Read the primary item.
165 BiConsumer<Item, String> addToBody = (item, line) -> newFrame.addItem(item);
166 String next = readLineAfterLine(false, addToBody, delayedActions);
167
168 // Read the surrogate items.
169 if (next.equals(EncryptedExpWriter.TERMINATOR_WITH_CONTINUATION)) {
170 BiConsumer<Item, String> addToSurrogates = (item, line) -> {
171 int parentID = Integer.parseInt(line.split(" ")[1]);
172 newFrame.getItemWithID(parentID).addToSurrogates(item);
173 newFrame.addToSurrogates(item);
174 };
175 next = readLineAfterLine(true, addToSurrogates, delayedActions);
176 }
177 return next;
178 }
179
180 private String readLineAfterLine(boolean isSurrogate, BiConsumer<Item, String> storeResult, List<DelayedAction> delayedActions) throws IOException {
181 String next = null;
182 Item currentItem = null;
183
184 Predicate<String> endOfSection = s -> s.equals(EncryptedExpWriter.TERMINATOR + "") || s.equals(EncryptedExpWriter.TERMINATOR_WITH_CONTINUATION);
185 while (_reader.ready() && !endOfSection.test(next = _reader.readLine())) {
186 if (!isValidLine(next)) {
187 continue;
188 }
189
190 String tag = getTagEnc(next);
191 if (next.startsWith(DefaultFrameWriter.TYPE_AND_ID_STR + " ")) {
192 currentItem = newItem(next);
193 _linePoints.put(currentItem.getID(), currentItem);
194 if (!isSurrogate) {
195 storeResult.accept(currentItem, next);
196 }
197 currentItem.setEncryptionDetailForTag(tag + "", EncryptionDetail.UnencryptedOnSave);
198 } else if (next.startsWith("SurrogateFor")) {
199 if (isSurrogate) {
200 storeResult.accept(currentItem, next);
201 }
202 } else if (currentItem != null && actionShouldBeDelayed(tag.charAt(0))) {
203 delayedActions.add(new DelayedAction(currentItem, next));
204 } else if (currentItem != null) {
205 processBodyLine(currentItem, next);
206 } else {
207 System.err.println("Error while reading in frame (ExpReader): Found body line but no current item to apply it to.");
208 }
209 }
210
211 return next;
212 }
213
214// private String readLineAfterLine(Consumer<Item> storeResult, List<DelayedAction> delayedActions) throws IOException {
215// String next = null;
216// Item currentItem = null;
217//
218// Predicate<String> endOfSection = s -> {
219// return s.equals(EncryptedExpWriter.TERMINATOR + "") || s.equals(EncryptedExpWriter.TERMINATOR_WITH_CONTINUATION);
220// };
221// while (_reader.ready() && !endOfSection.test(next = _reader.readLine())) {
222// if (!isValidLine(next)) {
223// continue;
224// }
225//
226// String tag = getTagEnc(next);
227// if (next.startsWith(DefaultFrameWriter.TYPE_AND_ID_STR + " ")) {
228// currentItem = newItem(next);
229// _linePoints.put(currentItem.getID(), currentItem);
230// currentItem.setEncryptionDetailForTag(tag + "", EncryptionDetail.UnencryptedOnSave);
231// storeResult.accept(currentItem);
232// } else if (next.startsWith("SurrogateFor")) {
233// int parentID = Integer.parseInt(next.split(" ")[1]);
234// newFrame.getItemWithID(parentID).addToSurrogates(currentItem);
235// new Delayed
236// }
237// }
238//
239// return next;
240// }
241
242 @Override
243 protected void processBodyLine(Item item, String line) {
244 // separate the tag from the value
245 String tag = getTagEnc(line);
246 String value = getValue(line);
247
248 if (item.isSurrogate() && item.isTagInherited(tag)) {
249 return;
250 }
251
252 // Attempt to decrypt the line if necessary.
253 if (isEncryptedLine(line)) {
254 LabelResult res = Label.resolveKey(item.getEncryptionLabel());
255 if (res == LabelResult.SuccessResolveLabelToKey) {
256 item.setEncryptionDetailForTag(tag, EncryptionDetail.ReencryptOnSave);
257 SecretKey key = new SecretKeySpec(res.key, SymmetricAlgorithm);
258 byte[] decryptedBytes = DecryptSymmetric(Base64.getDecoder().decode(value), key);
259 value = new String(decryptedBytes);
260 } else {
261 EncryptionDetail encryptionDetail = EncryptionDetail.UseUndecipheredValueOnSave.setUndecipheredValue(getValue(line));
262 item.setEncryptionDetailForTag(tag, encryptionDetail);
263 return;
264 }
265 } else {
266 item.setEncryptionDetailForTag(tag, EncryptionDetail.UnencryptedOnSave);
267 }
268
269 // Process the line
270 Method toRun = tag.startsWith("_") ? _ItemTagsExt.get(tag) : _ItemTags.get(tag.charAt(0));
271 if (toRun == null) {
272 System.out.println("Error accessing tag method: " + tag);
273 }
274 Object[] vals = Conversion.Convert(toRun, value);
275 try {
276 if (vals != null) {
277 toRun.invoke(item, vals);
278 }
279 } catch (Exception e) {
280 System.out.println("Error running tag method: " + tag);
281 e.printStackTrace();
282 }
283 }
284
285 protected static String getValue(String line) {
286 String[] split = line.split(" ");
287 if (split.length >= 2) {
288 return line.substring(split[0].length()).trim();
289 } else {
290 return null;
291 }
292 }
293
294 private static String getTagEnc(String line) {
295 char charAtZero = line.charAt(0);
296 if (charAtZero == '_') {
297 return line.split(" ")[0];
298 } else {
299 return charAtZero + "";
300 }
301 }
302
303 private static boolean isEncryptedLine(String line) {
304 if (line.startsWith("S") || line.startsWith("_el")) {
305 return false;
306 }
307 if (line.length() > 2) {
308 return line.charAt(1) == 'E';
309 } else {
310 return false;
311 }
312 }
313
314 private static byte[] DecryptSymmetric(final byte[] toDecrypt, final SecretKey key) {
315 try {
316 final Cipher cipher = Cipher.getInstance(SymmetricAlgorithm + SymmetricAlgorithmParameters);
317 cipher.init(Cipher.DECRYPT_MODE, key);
318 final byte[] decryptedBytes = cipher.doFinal(toDecrypt);
319 int indexOfZero = decryptedBytes.length - 1;
320 for (int i = decryptedBytes.length - 1; i >= 0; i--) {
321 if (decryptedBytes[i] != (byte) 0) {
322 indexOfZero = i + 1;
323 break;
324 }
325 }
326 if (indexOfZero < 0) { return decryptedBytes; }
327 else { return Arrays.copyOf(decryptedBytes, indexOfZero); }
328 } catch (final NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
329 | IllegalBlockSizeException | BadPaddingException e) {
330 e.printStackTrace();
331 return null;
332 }
333 }
334
335 private class EncryptedLineReader extends BufferedReader {
336 private boolean noneMode = false;
337
338 public EncryptedLineReader(Reader in) {
339 super(in);
340 }
341
342 @Override
343 /**
344 * Reads a line from an encrypted exp file that uses an encryption specified by the first line of the file.
345 * Returns that line to process, null if the currently logged in users doesn't own the appropriate key (access denied).
346 */
347 public String readLine() throws IOException {
348 String line = super.readLine();
349
350 if (line.isEmpty()) {
351 return "";
352 }
353
354 if (noneMode) {
355 return line;
356 }
357
358 if (line.startsWith(ENCRYPTED_EXP_FLAG)) {
359 String label = line.replace(ENCRYPTED_EXP_FLAG, "");
360 // if using Profile label, use personal key
361 if (label.equals(labelProfile)) {
362 Text text = KeyList.PersonalKey.get();
363 byte[] keyBytes = Base64.getDecoder().decode(text.getData().get(0));
364 personalKey = new SecretKeySpec(keyBytes, SymmetricAlgorithm);
365 return readLine();
366 } else if (label.equals(labelNone)) {
367 noneMode = true;
368 return readLine();
369 } else {
370 personalKey = resolveLabel(label);
371 if (personalKey == null) {
372 return null;
373 } else {
374 return readLine();
375 }
376 }
377 }
378
379 // decrypt line and return result
380 byte[] toDecrypt = Base64.getDecoder().decode(line);
381 byte[] decrypted = DecryptSymmetric(toDecrypt, personalKey);
382 if (decrypted == null) {
383 accessDenied = true;
384 return null; // access denied
385 } else {
386 String decryptedLine = new String(decrypted);
387 if (decryptedLine.startsWith("Z")) {
388 return decryptedLine.trim();
389 } else {
390 return decryptedLine;
391 }
392 }
393 }
394
395 private SecretKeySpec resolveLabel(String label) {
396 LabelResult res = Label.resolveKey(label);
397 if (res == LabelResult.SuccessResolveLabelToKey) {
398 byte[] keyBytes = res.key;
399 return new SecretKeySpec(keyBytes, SymmetricAlgorithm);
400 }
401 return null;
402 }
403 }
404}
Note: See TracBrowser for help on using the repository browser.