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

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

org.expeditee.auth.account.Password =>
org.expeditee.encryption.EncryptedExpReader =>

When performing account recovery, the username of the account you are trying to recover now actually needs to match the recovered key.
Also, the order that the password pieces are entered no longer matters.

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