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

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