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

Last change on this file since 1461 was 1461, checked in by bnemhaus, 4 years ago

You cannot load an encrypted frame if you do not have the associated key.

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