From ba0379b7f157f77f47d826e4f33029bfb1dcd771 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 23 Apr 2018 16:08:27 -0400 Subject: [PATCH 1/7] 3752 Encryption Detection data source level module added --- .../directorytree/ViewContextAction.java | 4 +- ...yptionDetectionDataSourceIngestModule.java | 181 ++++++++++++++++++ .../EncryptionDetectionFileIngestModule.java | 87 +-------- .../EncryptionDetectionIngestJobSettings.java | 13 +- .../EncryptionDetectionModuleFactory.java | 9 +- .../EncryptionDetectionTools.java | 116 +++++++++++ 6 files changed, 317 insertions(+), 93 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionDataSourceIngestModule.java create mode 100644 Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionTools.java diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java index 792252605a..06628d6136 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java @@ -59,7 +59,7 @@ public class ViewContextAction extends AbstractAction { private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(ViewContextAction.class.getName()); - private final Content content; + private Content content; /** * An action that displays the context for the source content of an artifact @@ -79,6 +79,8 @@ public class ViewContextAction extends AbstractAction { || (TskData.TSK_DB_FILES_TYPE_ENUM.SLACK == file.getType() && UserPreferences.hideSlackFilesInDataSourcesTree())) { this.setEnabled(false); } + } else { + this.content = artifactNode.getLookup().lookup(Content.class); } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionDataSourceIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionDataSourceIngestModule.java new file mode 100644 index 0000000000..cb9524df8c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionDataSourceIngestModule.java @@ -0,0 +1,181 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.modules.encryptiondetection; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.casemodule.services.Blackboard; +import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; +import org.sleuthkit.autopsy.ingest.IngestModule; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.ingest.DataSourceIngestModule; +import org.sleuthkit.autopsy.ingest.IngestJobContext; +import org.sleuthkit.autopsy.ingest.IngestMessage; +import org.sleuthkit.autopsy.ingest.IngestServices; +import org.sleuthkit.autopsy.ingest.ModuleDataEvent; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.Image; +import org.sleuthkit.datamodel.ReadContentInputStream; +import org.sleuthkit.datamodel.Volume; +import org.sleuthkit.datamodel.VolumeSystem; + +/** + * Sample data source ingest module that doesn't do much. Demonstrates per + * ingest job module settings, checking for job cancellation, updating the + * DataSourceIngestModuleProgress object, and use of a subset of the available + * ingest services. + */ +class EncryptionDetectionDataSourceIngestModule implements DataSourceIngestModule { + + private final IngestServices services = IngestServices.getInstance(); + private final Logger logger = services.getLogger(EncryptionDetectionModuleFactory.getModuleName()); + private Blackboard blackboard; + private double calculatedEntropy; + + private final double minimumEntropy; + + EncryptionDetectionDataSourceIngestModule(EncryptionDetectionIngestJobSettings settings) { + minimumEntropy = settings.getMinimumEntropy(); + } + + @Override + public void startUp(IngestJobContext context) throws IngestModule.IngestModuleException { + try { + validateSettings(); + blackboard = Case.getOpenCase().getServices().getBlackboard(); + } catch (NoCurrentCaseException ex) { + throw new IngestModule.IngestModuleException("Exception while getting open case.", ex); + } + } + + @Override + public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress progressBar) { + + try { + System.out.println("PROCESS DS"); + if (dataSource instanceof Image) { + List volumeSystems = ((Image) dataSource).getVolumeSystems(); + for (VolumeSystem volumeSystem : volumeSystems) { + for (Volume volume : volumeSystem.getVolumes()) { + if (volume.getFileSystems().isEmpty()) { + if (isDataSourceEncrypted(volume)) { + System.out.println("VOLUME ENCRYPTED"); + return flagVolume(volume); + } + } + } + } + } + } catch (ReadContentInputStream.ReadContentInputStreamException ex) { + logger.log(Level.WARNING, String.format("Unable to read data source '%s'", dataSource.getName()), ex); + return IngestModule.ProcessResult.ERROR; + } catch (IOException | TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Unable to process data source '%s'", dataSource.getName()), ex); + return IngestModule.ProcessResult.ERROR; + } + + return IngestModule.ProcessResult.OK; + } + + private void validateSettings() throws IngestModule.IngestModuleException { + EncryptionDetectionTools.validateMinEntropyValue(minimumEntropy); + } + + /** + * Create a blackboard artifact. + * + * @param The file to be processed. + * + * @return 'OK' if the file was processed successfully, or 'ERROR' if there + * was a problem. + */ + private IngestModule.ProcessResult flagVolume(Volume volume) { + try { + BlackboardArtifact artifact = volume.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED); + + try { + /* + * Index the artifact for keyword search. + */ + blackboard.indexArtifact(artifact); + } catch (Blackboard.BlackboardException ex) { + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS + } + + /* + * Send an event to update the view with the new result. + */ + services.fireModuleDataEvent(new ModuleDataEvent(EncryptionDetectionModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED, Collections.singletonList(artifact))); + + /* + * Make an ingest inbox message. + */ + StringBuilder detailsSb = new StringBuilder(); + detailsSb.append("File: ").append(volume.getParent().getUniquePath()).append(volume.getName()).append("
\n"); + detailsSb.append("Entropy: ").append(calculatedEntropy); + + services.postMessage(IngestMessage.createDataMessage(EncryptionDetectionModuleFactory.getModuleName(), + "Encryption Detected Match: " + volume.getName(), + detailsSb.toString(), + volume.getName(), + artifact)); + + return IngestModule.ProcessResult.OK; + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Failed to create blackboard artifact for '%s'.", volume.getName()), ex); //NON-NLS + return IngestModule.ProcessResult.ERROR; + } + } + + /** + * This method checks if the AbstractFile input is encrypted. Initial + * qualifications require that it be an actual file that is not known, meets + * file size requirements, and has a MIME type of + * 'application/octet-stream'. + * + * @param file AbstractFile to be checked. + * + * @return True if the AbstractFile is encrypted. + */ + private boolean isDataSourceEncrypted(Content dataSource) throws ReadContentInputStream.ReadContentInputStreamException, IOException, TskCoreException { + /* + * Criteria for the checks in this method are partially based on + * http://www.forensicswiki.org/wiki/TrueCrypt#Detection + */ + + boolean possiblyEncrypted = true; + + if (possiblyEncrypted) { + System.out.println("CALCULATE ENTROPY"); + calculatedEntropy = EncryptionDetectionTools.calculateEntropy(dataSource); + if (calculatedEntropy >= minimumEntropy) { + System.out.println("ENTROPY INDICATED ENCRYPTED DS"); + return true; + } + } + + return false; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java index 7b48606ea0..08b314a7aa 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java @@ -18,9 +18,7 @@ */ package org.sleuthkit.autopsy.modules.encryptiondetection; -import java.io.BufferedInputStream; import java.io.IOException; -import java.io.InputStream; import java.util.Collections; import java.util.logging.Level; import org.openide.util.NbBundle; @@ -37,7 +35,6 @@ import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.ReadContentInputStream; import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -47,19 +44,7 @@ import org.sleuthkit.datamodel.TskData; */ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter { - static final double DEFAULT_CONFIG_MINIMUM_ENTROPY = 7.5; - static final int DEFAULT_CONFIG_MINIMUM_FILE_SIZE = 5242880; // 5MB; - static final boolean DEFAULT_CONFIG_FILE_SIZE_MULTIPLE_ENFORCED = true; - static final boolean DEFAULT_CONFIG_SLACK_FILES_ALLOWED = true; - - static final double MINIMUM_ENTROPY_INPUT_RANGE_MIN = 6.0; - static final double MINIMUM_ENTROPY_INPUT_RANGE_MAX = 8.0; - static final int MINIMUM_FILE_SIZE_INPUT_RANGE_MIN = 1; - private static final int FILE_SIZE_MODULUS = 512; - private static final double ONE_OVER_LOG2 = 1.4426950408889634073599246810019; // (1 / log(2)) - private static final int BYTE_OCCURENCES_BUFFER_SIZE = 256; - private final IngestServices services = IngestServices.getInstance(); private final Logger logger = services.getLogger(EncryptionDetectionModuleFactory.getModuleName()); private FileTypeDetector fileTypeDetector; @@ -121,18 +106,9 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter * @throws IngestModule.IngestModuleException If the input is empty, * invalid, or out of range. */ - @NbBundle.Messages({ - "EncryptionDetectionFileIngestModule.errorMessage.minimumEntropyInput=Minimum entropy input must be a number between 6.0 and 8.0.", - "EncryptionDetectionFileIngestModule.errorMessage.minimumFileSizeInput=Minimum file size input must be an integer (in megabytes) of 1 or greater." - }) private void validateSettings() throws IngestModule.IngestModuleException { - if (minimumEntropy < MINIMUM_ENTROPY_INPUT_RANGE_MIN || minimumEntropy > MINIMUM_ENTROPY_INPUT_RANGE_MAX) { - throw new IngestModule.IngestModuleException(Bundle.EncryptionDetectionFileIngestModule_errorMessage_minimumEntropyInput()); - } - - if (minimumFileSize < MINIMUM_FILE_SIZE_INPUT_RANGE_MIN) { - throw new IngestModule.IngestModuleException(Bundle.EncryptionDetectionFileIngestModule_errorMessage_minimumFileSizeInput()); - } + EncryptionDetectionTools.validateMinEntropyValue(minimumEntropy); + EncryptionDetectionTools.validateMinFileSizeValue(minimumFileSize); } /** @@ -230,7 +206,7 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter } if (possiblyEncrypted) { - calculatedEntropy = calculateEntropy(file); + calculatedEntropy = EncryptionDetectionTools.calculateEntropy(file); if (calculatedEntropy >= minimumEntropy) { return true; } @@ -238,61 +214,4 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter return false; } - - /** - * Calculate the entropy of the file. The result is used to qualify the file - * as an encrypted file. - * - * @param file The file to be calculated against. - * - * @return The entropy of the file. - * - * @throws IOException If there is a failure closing or reading from the - * InputStream. - */ - private double calculateEntropy(AbstractFile file) throws ReadContentInputStreamException, IOException { - /* - * Logic in this method is based on - * https://github.com/willjasen/entropy/blob/master/entropy.java - */ - - InputStream in = null; - BufferedInputStream bin = null; - - try { - in = new ReadContentInputStream(file); - bin = new BufferedInputStream(in); - - /* - * Determine the number of times each byte value appears. - */ - int[] byteOccurences = new int[BYTE_OCCURENCES_BUFFER_SIZE]; - int readByte; - while ((readByte = bin.read()) != -1) { - byteOccurences[readByte]++; - } - - /* - * Calculate the entropy based on the byte occurence counts. - */ - long dataLength = file.getSize() - 1; - double entropyAccumulator = 0; - for (int i = 0; i < BYTE_OCCURENCES_BUFFER_SIZE; i++) { - if (byteOccurences[i] > 0) { - double byteProbability = (double) byteOccurences[i] / (double) dataLength; - entropyAccumulator += (byteProbability * Math.log(byteProbability) * ONE_OVER_LOG2); - } - } - - return -entropyAccumulator; - - } finally { - if (in != null) { - in.close(); - } - if (bin != null) { - bin.close(); - } - } - } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettings.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettings.java index 2aa6ad860d..5acb97a518 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettings.java +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettings.java @@ -26,7 +26,10 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; final class EncryptionDetectionIngestJobSettings implements IngestModuleIngestJobSettings { private static final long serialVersionUID = 1L; - + private static final double DEFAULT_CONFIG_MINIMUM_ENTROPY = 7.5; + private static final int DEFAULT_CONFIG_MINIMUM_FILE_SIZE = 5242880; // 5MB; + private static final boolean DEFAULT_CONFIG_FILE_SIZE_MULTIPLE_ENFORCED = true; + private static final boolean DEFAULT_CONFIG_SLACK_FILES_ALLOWED = true; private double minimumEntropy; private int minimumFileSize; private boolean fileSizeMultipleEnforced; @@ -36,10 +39,10 @@ final class EncryptionDetectionIngestJobSettings implements IngestModuleIngestJo * Instantiate the ingest job settings with default values. */ EncryptionDetectionIngestJobSettings() { - this.minimumEntropy = EncryptionDetectionFileIngestModule.DEFAULT_CONFIG_MINIMUM_ENTROPY; - this.minimumFileSize = EncryptionDetectionFileIngestModule.DEFAULT_CONFIG_MINIMUM_FILE_SIZE; - this.fileSizeMultipleEnforced = EncryptionDetectionFileIngestModule.DEFAULT_CONFIG_FILE_SIZE_MULTIPLE_ENFORCED; - this.slackFilesAllowed = EncryptionDetectionFileIngestModule.DEFAULT_CONFIG_SLACK_FILES_ALLOWED; + this.minimumEntropy = DEFAULT_CONFIG_MINIMUM_ENTROPY; + this.minimumFileSize = DEFAULT_CONFIG_MINIMUM_FILE_SIZE; + this.fileSizeMultipleEnforced = DEFAULT_CONFIG_FILE_SIZE_MULTIPLE_ENFORCED; + this.slackFilesAllowed = DEFAULT_CONFIG_SLACK_FILES_ALLOWED; } /** diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionModuleFactory.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionModuleFactory.java index 27549f648f..1e0fd8115a 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionModuleFactory.java +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionModuleFactory.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017 Basis Technology Corp. + * Copyright 2017-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -106,11 +106,14 @@ public class EncryptionDetectionModuleFactory implements IngestModuleFactory { @Override public boolean isDataSourceIngestModuleFactory() { - return false; + return true; } @Override public DataSourceIngestModule createDataSourceIngestModule(IngestModuleIngestJobSettings settings) { - throw new UnsupportedOperationException(); + if (!(settings instanceof EncryptionDetectionIngestJobSettings)) { + throw new IllegalArgumentException("Expected settings argument to be an instance of EncryptionDetectionIngestJobSettings."); + } + return new EncryptionDetectionDataSourceIngestModule((EncryptionDetectionIngestJobSettings) settings); } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionTools.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionTools.java new file mode 100644 index 0000000000..c42c6af1ec --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionTools.java @@ -0,0 +1,116 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.modules.encryptiondetection; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.ingest.IngestModule; +import org.sleuthkit.datamodel.ReadContentInputStream; +import org.sleuthkit.datamodel.Content; + +/** + * + */ +final class EncryptionDetectionTools { + + private static final double ONE_OVER_LOG2 = 1.4426950408889634073599246810019; // (1 / log(2)) + private static final int BYTE_OCCURENCES_BUFFER_SIZE = 256; + static final double MINIMUM_ENTROPY_INPUT_RANGE_MIN = 6.0; + static final double MINIMUM_ENTROPY_INPUT_RANGE_MAX = 8.0; + + static final int MINIMUM_FILE_SIZE_INPUT_RANGE_MIN = 1; + + @NbBundle.Messages({ + "EncryptionDetectionTools.errorMessage.minimumEntropyInput=Minimum entropy input must be a number between 6.0 and 8.0.", + "EncryptionDetectionTools.errorMessage.minimumFileSizeInput=Minimum file size input must be an integer (in megabytes) of 1 or greater." + }) + static void validateMinEntropyValue(double minimumEntropy) throws IngestModule.IngestModuleException { + if (minimumEntropy < MINIMUM_ENTROPY_INPUT_RANGE_MIN || minimumEntropy > MINIMUM_ENTROPY_INPUT_RANGE_MAX) { + throw new IngestModule.IngestModuleException(Bundle.EncryptionDetectionTools_errorMessage_minimumEntropyInput()); + } + } + + static void validateMinFileSizeValue(int minimumFileSize) throws IngestModule.IngestModuleException { + if (minimumFileSize < MINIMUM_FILE_SIZE_INPUT_RANGE_MIN) { + throw new IngestModule.IngestModuleException(Bundle.EncryptionDetectionTools_errorMessage_minimumFileSizeInput()); + } + } + + /** + * Calculate the entropy of the file. The result is used to qualify the file + * as an encrypted file. + * + * @param file The file to be calculated against. + * + * @return The entropy of the file. + * + * @throws IOException If there is a failure closing or reading from the + * InputStream. + */ + static double calculateEntropy(Content file) throws ReadContentInputStream.ReadContentInputStreamException, IOException { + /* + * Logic in this method is based on + * https://github.com/willjasen/entropy/blob/master/entropy.java + */ + + InputStream in = null; + BufferedInputStream bin = null; + + try { + in = new ReadContentInputStream(file); + bin = new BufferedInputStream(in); + + /* + * Determine the number of times each byte value appears. + */ + int[] byteOccurences = new int[BYTE_OCCURENCES_BUFFER_SIZE]; + int readByte; + while ((readByte = bin.read()) != -1) { + byteOccurences[readByte]++; + } + + /* + * Calculate the entropy based on the byte occurence counts. + */ + long dataLength = file.getSize() - 1; + double entropyAccumulator = 0; + for (int i = 0; i < BYTE_OCCURENCES_BUFFER_SIZE; i++) { + if (byteOccurences[i] > 0) { + double byteProbability = (double) byteOccurences[i] / (double) dataLength; + entropyAccumulator += (byteProbability * Math.log(byteProbability) * ONE_OVER_LOG2); + } + } + System.out.println("ENTROPY VALUE: " + -entropyAccumulator); + return -entropyAccumulator; + + } finally { + if (in != null) { + in.close(); + } + if (bin != null) { + bin.close(); + } + } + } + + private EncryptionDetectionTools() { + } +} From c2bc250d60f598a7a020106a91887e5394034d5f Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 23 Apr 2018 16:58:05 -0400 Subject: [PATCH 2/7] 3752 minor adjustment to ViewContextAction --- .../sleuthkit/autopsy/directorytree/ViewContextAction.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java index 06628d6136..99d54e61a9 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java @@ -59,7 +59,7 @@ public class ViewContextAction extends AbstractAction { private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(ViewContextAction.class.getName()); - private Content content; + private final Content content; /** * An action that displays the context for the source content of an artifact @@ -72,8 +72,9 @@ public class ViewContextAction extends AbstractAction { */ public ViewContextAction(String displayName, BlackboardArtifactNode artifactNode) { super(displayName); - this.content = artifactNode.getLookup().lookup(AbstractFile.class); - if (this.content != null) { + Content fileContent = artifactNode.getLookup().lookup(AbstractFile.class); + if (fileContent != null) { + this.content = fileContent; AbstractFile file = (AbstractFile) content; if ((TskData.FileKnown.KNOWN == file.getKnown() && UserPreferences.hideKnownFilesInDataSourcesTree()) || (TskData.TSK_DB_FILES_TYPE_ENUM.SLACK == file.getType() && UserPreferences.hideSlackFilesInDataSourcesTree())) { From f7fad3184effd84d4e888b2a808bcc6990af1bf1 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 23 Apr 2018 17:47:54 -0400 Subject: [PATCH 3/7] 3752 adjust ViewContextAction for encrypted volumes --- .../autopsy/directorytree/DataResultFilterNode.java | 9 ++++++++- .../autopsy/directorytree/ViewContextAction.java | 7 ++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index f51c94aaca..3aef1e5a2a 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -386,8 +386,15 @@ public class DataResultFilterNode extends FilterNode { NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewFileInDir.text"), c)); } // action to go to the source file of the artifact - actionsList.add(new ViewContextAction( + // action to go to the source file of the artifact + Content fileContent = ban.getLookup().lookup(AbstractFile.class); + if (fileContent == null) { + Content content = ban.getLookup().lookup(Content.class); + actionsList.add(new ViewContextAction("View Source Content in Directory", content)); + } else { + actionsList.add(new ViewContextAction( NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewSrcFileInDir.text"), ban)); + } } Content c = ban.getLookup().lookup(File.class); Node n = null; diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java index 99d54e61a9..792252605a 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java @@ -72,16 +72,13 @@ public class ViewContextAction extends AbstractAction { */ public ViewContextAction(String displayName, BlackboardArtifactNode artifactNode) { super(displayName); - Content fileContent = artifactNode.getLookup().lookup(AbstractFile.class); - if (fileContent != null) { - this.content = fileContent; + this.content = artifactNode.getLookup().lookup(AbstractFile.class); + if (this.content != null) { AbstractFile file = (AbstractFile) content; if ((TskData.FileKnown.KNOWN == file.getKnown() && UserPreferences.hideKnownFilesInDataSourcesTree()) || (TskData.TSK_DB_FILES_TYPE_ENUM.SLACK == file.getType() && UserPreferences.hideSlackFilesInDataSourcesTree())) { this.setEnabled(false); } - } else { - this.content = artifactNode.getLookup().lookup(Content.class); } } From c36a1e6191bf6fa0f2a84a71dbd2051e276b711a Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 23 Apr 2018 18:05:59 -0400 Subject: [PATCH 4/7] 3752 clean up - make comments and names more accurate --- ...yptionDetectionDataSourceIngestModule.java | 27 ++++++++++--------- .../EncryptionDetectionTools.java | 25 ++++++++++------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionDataSourceIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionDataSourceIngestModule.java index cb9524df8c..4160ef1c49 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionDataSourceIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionDataSourceIngestModule.java @@ -42,10 +42,7 @@ import org.sleuthkit.datamodel.Volume; import org.sleuthkit.datamodel.VolumeSystem; /** - * Sample data source ingest module that doesn't do much. Demonstrates per - * ingest job module settings, checking for job cancellation, updating the - * DataSourceIngestModuleProgress object, and use of a subset of the available - * ingest services. + * Data source module to detect encryption. */ class EncryptionDetectionDataSourceIngestModule implements DataSourceIngestModule { @@ -55,7 +52,13 @@ class EncryptionDetectionDataSourceIngestModule implements DataSourceIngestModul private double calculatedEntropy; private final double minimumEntropy; - + + /** + * Create a EncryptionDetectionDataSourceIngestModule object that will detect + * volumes that are encrypted and create blackboard artifacts as appropriate. + * The supplied EncryptionDetectionIngestJobSettings object is used to + * configure the module. + */ EncryptionDetectionDataSourceIngestModule(EncryptionDetectionIngestJobSettings settings) { minimumEntropy = settings.getMinimumEntropy(); } @@ -80,7 +83,7 @@ class EncryptionDetectionDataSourceIngestModule implements DataSourceIngestModul for (VolumeSystem volumeSystem : volumeSystems) { for (Volume volume : volumeSystem.getVolumes()) { if (volume.getFileSystems().isEmpty()) { - if (isDataSourceEncrypted(volume)) { + if (isVolumeEncrypted(volume)) { System.out.println("VOLUME ENCRYPTED"); return flagVolume(volume); } @@ -106,9 +109,9 @@ class EncryptionDetectionDataSourceIngestModule implements DataSourceIngestModul /** * Create a blackboard artifact. * - * @param The file to be processed. + * @param The volume to be processed. * - * @return 'OK' if the file was processed successfully, or 'ERROR' if there + * @return 'OK' if the volume was processed successfully, or 'ERROR' if there * was a problem. */ private IngestModule.ProcessResult flagVolume(Volume volume) { @@ -157,9 +160,9 @@ class EncryptionDetectionDataSourceIngestModule implements DataSourceIngestModul * * @param file AbstractFile to be checked. * - * @return True if the AbstractFile is encrypted. + * @return True if the Volume is encrypted. */ - private boolean isDataSourceEncrypted(Content dataSource) throws ReadContentInputStream.ReadContentInputStreamException, IOException, TskCoreException { + private boolean isVolumeEncrypted(Volume volume) throws ReadContentInputStream.ReadContentInputStreamException, IOException, TskCoreException { /* * Criteria for the checks in this method are partially based on * http://www.forensicswiki.org/wiki/TrueCrypt#Detection @@ -168,10 +171,8 @@ class EncryptionDetectionDataSourceIngestModule implements DataSourceIngestModul boolean possiblyEncrypted = true; if (possiblyEncrypted) { - System.out.println("CALCULATE ENTROPY"); - calculatedEntropy = EncryptionDetectionTools.calculateEntropy(dataSource); + calculatedEntropy = EncryptionDetectionTools.calculateEntropy(volume); if (calculatedEntropy >= minimumEntropy) { - System.out.println("ENTROPY INDICATED ENCRYPTED DS"); return true; } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionTools.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionTools.java index c42c6af1ec..075af04726 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionTools.java +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionTools.java @@ -37,17 +37,25 @@ final class EncryptionDetectionTools { static final double MINIMUM_ENTROPY_INPUT_RANGE_MAX = 8.0; static final int MINIMUM_FILE_SIZE_INPUT_RANGE_MIN = 1; - + @NbBundle.Messages({ "EncryptionDetectionTools.errorMessage.minimumEntropyInput=Minimum entropy input must be a number between 6.0 and 8.0.", "EncryptionDetectionTools.errorMessage.minimumFileSizeInput=Minimum file size input must be an integer (in megabytes) of 1 or greater." }) + /** + * Check if the minimum entropy setting is in the accepted range for this + * module. + */ static void validateMinEntropyValue(double minimumEntropy) throws IngestModule.IngestModuleException { if (minimumEntropy < MINIMUM_ENTROPY_INPUT_RANGE_MIN || minimumEntropy > MINIMUM_ENTROPY_INPUT_RANGE_MAX) { throw new IngestModule.IngestModuleException(Bundle.EncryptionDetectionTools_errorMessage_minimumEntropyInput()); } } + /** + * Check if the minimum file size setting is in the accepted range for this + * module. + */ static void validateMinFileSizeValue(int minimumFileSize) throws IngestModule.IngestModuleException { if (minimumFileSize < MINIMUM_FILE_SIZE_INPUT_RANGE_MIN) { throw new IngestModule.IngestModuleException(Bundle.EncryptionDetectionTools_errorMessage_minimumFileSizeInput()); @@ -55,17 +63,17 @@ final class EncryptionDetectionTools { } /** - * Calculate the entropy of the file. The result is used to qualify the file - * as an encrypted file. + * Calculate the entropy of the content. The result is used to qualify the + * content as an encrypted content. * - * @param file The file to be calculated against. + * @param content The content to be calculated against. * - * @return The entropy of the file. + * @return The entropy of the content. * * @throws IOException If there is a failure closing or reading from the * InputStream. */ - static double calculateEntropy(Content file) throws ReadContentInputStream.ReadContentInputStreamException, IOException { + static double calculateEntropy(Content content) throws ReadContentInputStream.ReadContentInputStreamException, IOException { /* * Logic in this method is based on * https://github.com/willjasen/entropy/blob/master/entropy.java @@ -75,7 +83,7 @@ final class EncryptionDetectionTools { BufferedInputStream bin = null; try { - in = new ReadContentInputStream(file); + in = new ReadContentInputStream(content); bin = new BufferedInputStream(in); /* @@ -90,7 +98,7 @@ final class EncryptionDetectionTools { /* * Calculate the entropy based on the byte occurence counts. */ - long dataLength = file.getSize() - 1; + long dataLength = content.getSize() - 1; double entropyAccumulator = 0; for (int i = 0; i < BYTE_OCCURENCES_BUFFER_SIZE; i++) { if (byteOccurences[i] > 0) { @@ -98,7 +106,6 @@ final class EncryptionDetectionTools { entropyAccumulator += (byteProbability * Math.log(byteProbability) * ONE_OVER_LOG2); } } - System.out.println("ENTROPY VALUE: " + -entropyAccumulator); return -entropyAccumulator; } finally { From 520ad092b97e1c6c4171b2143aa44e7024a662e2 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Tue, 24 Apr 2018 14:05:04 -0400 Subject: [PATCH 5/7] 3752 fix imports and method access from merge conflicts with 3748 --- .../EncryptionDetectionFileIngestModule.java | 5 +++++ .../encryptiondetection/EncryptionDetectionTools.java | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java index db8205ca40..0da14af61f 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java @@ -21,6 +21,9 @@ package org.sleuthkit.autopsy.modules.encryptiondetection; import java.io.IOException; import java.util.Collections; import java.util.logging.Level; +import org.sleuthkit.datamodel.ReadContentInputStream; +import java.io.BufferedInputStream; +import java.io.InputStream; import org.apache.tika.exception.EncryptedDocumentException; import org.apache.tika.exception.TikaException; import org.apache.tika.metadata.Metadata; @@ -46,6 +49,8 @@ import org.sleuthkit.datamodel.TskData; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; + + /** * File ingest module to detect encryption and password protection. */ diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionTools.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionTools.java index cdb2935764..c776a8d7d9 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionTools.java +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionTools.java @@ -78,7 +78,7 @@ final class EncryptionDetectionTools { * @throws IOException If there is a failure closing or * reading from the InputStream. */ - private double calculateEntropy(Content content) throws ReadContentInputStream.ReadContentInputStreamException, IOException { + static double calculateEntropy(Content content) throws ReadContentInputStream.ReadContentInputStreamException, IOException { /* * Logic in this method is based on * https://github.com/willjasen/entropy/blob/master/entropy.java From 9e1bbf7fa5ed94ed4ec591eea0e015709410aa4b Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Tue, 24 Apr 2018 14:18:43 -0400 Subject: [PATCH 6/7] 3752 remove sys outs from debugging --- .../EncryptionDetectionDataSourceIngestModule.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionDataSourceIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionDataSourceIngestModule.java index 4160ef1c49..08d85ec4d5 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionDataSourceIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionDataSourceIngestModule.java @@ -77,14 +77,12 @@ class EncryptionDetectionDataSourceIngestModule implements DataSourceIngestModul public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress progressBar) { try { - System.out.println("PROCESS DS"); if (dataSource instanceof Image) { List volumeSystems = ((Image) dataSource).getVolumeSystems(); for (VolumeSystem volumeSystem : volumeSystems) { for (Volume volume : volumeSystem.getVolumes()) { if (volume.getFileSystems().isEmpty()) { if (isVolumeEncrypted(volume)) { - System.out.println("VOLUME ENCRYPTED"); return flagVolume(volume); } } From 30020ec60231fea3046ed1fa49c0506b225def96 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 26 Apr 2018 17:15:33 -0400 Subject: [PATCH 7/7] 3752 fix issues found in review. comments, and other minor things --- ...yptionDetectionDataSourceIngestModule.java | 47 +++++++++---------- .../EncryptionDetectionTools.java | 23 +++++---- 2 files changed, 37 insertions(+), 33 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionDataSourceIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionDataSourceIngestModule.java index 08d85ec4d5..a269b2bdd2 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionDataSourceIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionDataSourceIngestModule.java @@ -44,20 +44,19 @@ import org.sleuthkit.datamodel.VolumeSystem; /** * Data source module to detect encryption. */ -class EncryptionDetectionDataSourceIngestModule implements DataSourceIngestModule { +final class EncryptionDetectionDataSourceIngestModule implements DataSourceIngestModule { private final IngestServices services = IngestServices.getInstance(); private final Logger logger = services.getLogger(EncryptionDetectionModuleFactory.getModuleName()); private Blackboard blackboard; private double calculatedEntropy; - private final double minimumEntropy; - + /** - * Create a EncryptionDetectionDataSourceIngestModule object that will detect - * volumes that are encrypted and create blackboard artifacts as appropriate. - * The supplied EncryptionDetectionIngestJobSettings object is used to - * configure the module. + * Create an EncryptionDetectionDataSourceIngestModule object that will + * detect volumes that are encrypted and create blackboard artifacts as + * appropriate. The supplied EncryptionDetectionIngestJobSettings object is + * used to configure the module. */ EncryptionDetectionDataSourceIngestModule(EncryptionDetectionIngestJobSettings settings) { minimumEntropy = settings.getMinimumEntropy(); @@ -81,10 +80,8 @@ class EncryptionDetectionDataSourceIngestModule implements DataSourceIngestModul List volumeSystems = ((Image) dataSource).getVolumeSystems(); for (VolumeSystem volumeSystem : volumeSystems) { for (Volume volume : volumeSystem.getVolumes()) { - if (volume.getFileSystems().isEmpty()) { - if (isVolumeEncrypted(volume)) { - return flagVolume(volume); - } + if (isVolumeEncrypted(volume)) { + return flagVolume(volume); } } } @@ -100,6 +97,14 @@ class EncryptionDetectionDataSourceIngestModule implements DataSourceIngestModul return IngestModule.ProcessResult.OK; } + /** + * Validate the relevant settings for the + * EncryptionDetectionDataSourceIngestModule + * + * @throws IngestModule.IngestModuleException If the input is empty, + * invalid, or out of range. + * + */ private void validateSettings() throws IngestModule.IngestModuleException { EncryptionDetectionTools.validateMinEntropyValue(minimumEntropy); } @@ -109,8 +114,8 @@ class EncryptionDetectionDataSourceIngestModule implements DataSourceIngestModul * * @param The volume to be processed. * - * @return 'OK' if the volume was processed successfully, or 'ERROR' if there - * was a problem. + * @return 'OK' if the volume was processed successfully, or 'ERROR' if + * there was a problem. */ private IngestModule.ProcessResult flagVolume(Volume volume) { try { @@ -133,7 +138,7 @@ class EncryptionDetectionDataSourceIngestModule implements DataSourceIngestModul /* * Make an ingest inbox message. */ - StringBuilder detailsSb = new StringBuilder(); + StringBuilder detailsSb = new StringBuilder(""); detailsSb.append("File: ").append(volume.getParent().getUniquePath()).append(volume.getName()).append("
\n"); detailsSb.append("Entropy: ").append(calculatedEntropy); @@ -151,12 +156,10 @@ class EncryptionDetectionDataSourceIngestModule implements DataSourceIngestModul } /** - * This method checks if the AbstractFile input is encrypted. Initial - * qualifications require that it be an actual file that is not known, meets - * file size requirements, and has a MIME type of - * 'application/octet-stream'. + * This method checks if the Volume input is encrypted. Initial + * qualifications require that the Volume not have a file system. * - * @param file AbstractFile to be checked. + * @param volume Volume to be checked. * * @return True if the Volume is encrypted. */ @@ -165,16 +168,12 @@ class EncryptionDetectionDataSourceIngestModule implements DataSourceIngestModul * Criteria for the checks in this method are partially based on * http://www.forensicswiki.org/wiki/TrueCrypt#Detection */ - - boolean possiblyEncrypted = true; - - if (possiblyEncrypted) { + if (volume.getFileSystems().isEmpty()) { calculatedEntropy = EncryptionDetectionTools.calculateEntropy(volume); if (calculatedEntropy >= minimumEntropy) { return true; } } - return false; } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionTools.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionTools.java index c776a8d7d9..99dbc0aaeb 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionTools.java +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionTools.java @@ -27,7 +27,7 @@ import org.sleuthkit.datamodel.ReadContentInputStream; import org.sleuthkit.datamodel.Content; /** - * + * Class containing common methods concerning the Encryption Detection module. */ final class EncryptionDetectionTools { @@ -35,12 +35,10 @@ final class EncryptionDetectionTools { private static final int BYTE_OCCURENCES_BUFFER_SIZE = 256; static final double MINIMUM_ENTROPY_INPUT_RANGE_MIN = 6.0; static final double MINIMUM_ENTROPY_INPUT_RANGE_MAX = 8.0; - static final int MINIMUM_FILE_SIZE_INPUT_RANGE_MIN = 1; @NbBundle.Messages({ - "EncryptionDetectionTools.errorMessage.minimumEntropyInput=Minimum entropy input must be a number between 6.0 and 8.0.", - "EncryptionDetectionTools.errorMessage.minimumFileSizeInput=Minimum file size input must be an integer (in megabytes) of 1 or greater." + "EncryptionDetectionTools.errorMessage.minimumEntropyInput=Minimum entropy input must be a number between 6.0 and 8.0." }) /** * Check if the minimum entropy setting is in the accepted range for this @@ -52,6 +50,9 @@ final class EncryptionDetectionTools { } } + @NbBundle.Messages({ + "EncryptionDetectionTools.errorMessage.minimumFileSizeInput=Minimum file size input must be an integer (in megabytes) of 1 or greater." + }) /** * Check if the minimum file size setting is in the accepted range for this * module. @@ -62,12 +63,10 @@ final class EncryptionDetectionTools { } } - private EncryptionDetectionTools() { - } - + /** - * Calculate the entropy of the content. The result is used to qualify the content - * as possibly encrypted. + * Calculate the entropy of the content. The result is used to qualify the + * content as possibly encrypted. * * @param content The content to be calculated against. * @@ -123,4 +122,10 @@ final class EncryptionDetectionTools { } } } + + /** + * Private constructor for Encryption Detection Tools class. + */ + private EncryptionDetectionTools() { + } }