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

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

Recoding of the Labels class to improve surrogate mode functionality. Surrogate mode is now maintained when you navigate from one frame to another, even if that frame has a different set of labels. Completely different sets of labels cause Expeditee to exit surrogate mode with a message to the user.

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