mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-12 16:06:15 +00:00
Merge pull request #3719 from wschaeferB/3752-EncryptedPartitionsSupport
3752 encrypted partitions support
This commit is contained in:
commit
eb01a3e899
@ -386,9 +386,16 @@ public class DataResultFilterNode extends FilterNode {
|
|||||||
NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewFileInDir.text"), c));
|
NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewFileInDir.text"), c));
|
||||||
}
|
}
|
||||||
// action to go to the source file of the artifact
|
// action to go to the source file of the artifact
|
||||||
|
// 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(
|
actionsList.add(new ViewContextAction(
|
||||||
NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewSrcFileInDir.text"), ban));
|
NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewSrcFileInDir.text"), ban));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Content c = ban.getLookup().lookup(File.class);
|
Content c = ban.getLookup().lookup(File.class);
|
||||||
Node n = null;
|
Node n = null;
|
||||||
boolean md5Action = false;
|
boolean md5Action = false;
|
||||||
|
@ -0,0 +1,179 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2018 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data source module to detect encryption.
|
||||||
|
*/
|
||||||
|
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 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 {
|
||||||
|
if (dataSource instanceof Image) {
|
||||||
|
List<VolumeSystem> volumeSystems = ((Image) dataSource).getVolumeSystems();
|
||||||
|
for (VolumeSystem volumeSystem : volumeSystems) {
|
||||||
|
for (Volume volume : volumeSystem.getVolumes()) {
|
||||||
|
if (isVolumeEncrypted(volume)) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a blackboard artifact.
|
||||||
|
*
|
||||||
|
* @param The volume to be processed.
|
||||||
|
*
|
||||||
|
* @return 'OK' if the volume 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("<br/>\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 Volume input is encrypted. Initial
|
||||||
|
* qualifications require that the Volume not have a file system.
|
||||||
|
*
|
||||||
|
* @param volume Volume to be checked.
|
||||||
|
*
|
||||||
|
* @return True if the Volume is encrypted.
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
if (volume.getFileSystems().isEmpty()) {
|
||||||
|
calculatedEntropy = EncryptionDetectionTools.calculateEntropy(volume);
|
||||||
|
if (calculatedEntropy >= minimumEntropy) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
96
Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java
Executable file → Normal file
96
Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java
Executable file → Normal file
@ -18,18 +18,18 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.modules.encryptiondetection;
|
package org.sleuthkit.autopsy.modules.encryptiondetection;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.logging.Level;
|
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.EncryptedDocumentException;
|
||||||
import org.apache.tika.exception.TikaException;
|
import org.apache.tika.exception.TikaException;
|
||||||
import org.apache.tika.metadata.Metadata;
|
import org.apache.tika.metadata.Metadata;
|
||||||
import org.apache.tika.parser.AutoDetectParser;
|
import org.apache.tika.parser.AutoDetectParser;
|
||||||
import org.apache.tika.parser.ParseContext;
|
import org.apache.tika.parser.ParseContext;
|
||||||
import org.apache.tika.sax.BodyContentHandler;
|
import org.apache.tika.sax.BodyContentHandler;
|
||||||
import org.openide.util.NbBundle;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
import org.sleuthkit.autopsy.casemodule.services.Blackboard;
|
import org.sleuthkit.autopsy.casemodule.services.Blackboard;
|
||||||
@ -43,31 +43,20 @@ import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
|||||||
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
|
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
import org.sleuthkit.datamodel.ReadContentInputStream;
|
|
||||||
import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
|
import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
import org.sleuthkit.datamodel.TskData;
|
import org.sleuthkit.datamodel.TskData;
|
||||||
import org.xml.sax.ContentHandler;
|
import org.xml.sax.ContentHandler;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* File ingest module to detect encryption and password protection.
|
* File ingest module to detect encryption and password protection.
|
||||||
*/
|
*/
|
||||||
final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter {
|
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 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 IngestServices services = IngestServices.getInstance();
|
||||||
private final Logger logger = services.getLogger(EncryptionDetectionModuleFactory.getModuleName());
|
private final Logger logger = services.getLogger(EncryptionDetectionModuleFactory.getModuleName());
|
||||||
private FileTypeDetector fileTypeDetector;
|
private FileTypeDetector fileTypeDetector;
|
||||||
@ -154,18 +143,9 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter
|
|||||||
* @throws IngestModule.IngestModuleException If the input is empty,
|
* @throws IngestModule.IngestModuleException If the input is empty,
|
||||||
* invalid, or out of range.
|
* 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 {
|
private void validateSettings() throws IngestModule.IngestModuleException {
|
||||||
if (minimumEntropy < MINIMUM_ENTROPY_INPUT_RANGE_MIN || minimumEntropy > MINIMUM_ENTROPY_INPUT_RANGE_MAX) {
|
EncryptionDetectionTools.validateMinEntropyValue(minimumEntropy);
|
||||||
throw new IngestModule.IngestModuleException(Bundle.EncryptionDetectionFileIngestModule_errorMessage_minimumEntropyInput());
|
EncryptionDetectionTools.validateMinFileSizeValue(minimumFileSize);
|
||||||
}
|
|
||||||
|
|
||||||
if (minimumFileSize < MINIMUM_FILE_SIZE_INPUT_RANGE_MIN) {
|
|
||||||
throw new IngestModule.IngestModuleException(Bundle.EncryptionDetectionFileIngestModule_errorMessage_minimumFileSizeInput());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -315,72 +295,12 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter
|
|||||||
/*
|
/*
|
||||||
* Qualify the entropy.
|
* Qualify the entropy.
|
||||||
*/
|
*/
|
||||||
calculatedEntropy = calculateEntropy(file);
|
calculatedEntropy = EncryptionDetectionTools.calculateEntropy(file);
|
||||||
if (calculatedEntropy >= minimumEntropy) {
|
if (calculatedEntropy >= minimumEntropy) {
|
||||||
possiblyEncrypted = true;
|
possiblyEncrypted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return possiblyEncrypted;
|
return possiblyEncrypted;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the entropy of the file. The result is used to qualify the file
|
|
||||||
* as possibly encrypted.
|
|
||||||
*
|
|
||||||
* @param file The file to be calculated against.
|
|
||||||
*
|
|
||||||
* @return The entropy of the file.
|
|
||||||
*
|
|
||||||
* @throws ReadContentInputStreamException If there is a failure reading
|
|
||||||
* from the InputStream.
|
|
||||||
* @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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,10 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
|
|||||||
final class EncryptionDetectionIngestJobSettings implements IngestModuleIngestJobSettings {
|
final class EncryptionDetectionIngestJobSettings implements IngestModuleIngestJobSettings {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
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 double minimumEntropy;
|
||||||
private int minimumFileSize;
|
private int minimumFileSize;
|
||||||
private boolean fileSizeMultipleEnforced;
|
private boolean fileSizeMultipleEnforced;
|
||||||
@ -36,10 +39,10 @@ final class EncryptionDetectionIngestJobSettings implements IngestModuleIngestJo
|
|||||||
* Instantiate the ingest job settings with default values.
|
* Instantiate the ingest job settings with default values.
|
||||||
*/
|
*/
|
||||||
EncryptionDetectionIngestJobSettings() {
|
EncryptionDetectionIngestJobSettings() {
|
||||||
this.minimumEntropy = EncryptionDetectionFileIngestModule.DEFAULT_CONFIG_MINIMUM_ENTROPY;
|
this.minimumEntropy = DEFAULT_CONFIG_MINIMUM_ENTROPY;
|
||||||
this.minimumFileSize = EncryptionDetectionFileIngestModule.DEFAULT_CONFIG_MINIMUM_FILE_SIZE;
|
this.minimumFileSize = DEFAULT_CONFIG_MINIMUM_FILE_SIZE;
|
||||||
this.fileSizeMultipleEnforced = EncryptionDetectionFileIngestModule.DEFAULT_CONFIG_FILE_SIZE_MULTIPLE_ENFORCED;
|
this.fileSizeMultipleEnforced = DEFAULT_CONFIG_FILE_SIZE_MULTIPLE_ENFORCED;
|
||||||
this.slackFilesAllowed = EncryptionDetectionFileIngestModule.DEFAULT_CONFIG_SLACK_FILES_ALLOWED;
|
this.slackFilesAllowed = DEFAULT_CONFIG_SLACK_FILES_ALLOWED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -107,11 +107,14 @@ public class EncryptionDetectionModuleFactory implements IngestModuleFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isDataSourceIngestModuleFactory() {
|
public boolean isDataSourceIngestModuleFactory() {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataSourceIngestModule createDataSourceIngestModule(IngestModuleIngestJobSettings settings) {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2018 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class containing common methods concerning the Encryption Detection module.
|
||||||
|
*/
|
||||||
|
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."
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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.
|
||||||
|
*/
|
||||||
|
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 content. The result is used to qualify the
|
||||||
|
* content as possibly encrypted.
|
||||||
|
*
|
||||||
|
* @param content The content to be calculated against.
|
||||||
|
*
|
||||||
|
* @return The entropy of the content.
|
||||||
|
*
|
||||||
|
* @throws ReadContentInputStreamException If there is a failure reading
|
||||||
|
* from the InputStream.
|
||||||
|
* @throws IOException If there is a failure closing or
|
||||||
|
* reading from the InputStream.
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
InputStream in = null;
|
||||||
|
BufferedInputStream bin = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
in = new ReadContentInputStream(content);
|
||||||
|
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 = content.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private constructor for Encryption Detection Tools class.
|
||||||
|
*/
|
||||||
|
private EncryptionDetectionTools() {
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user