mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-12 07:56:16 +00:00
Merge pull request #3719 from wschaeferB/3752-EncryptedPartitionsSupport
3752 encrypted partitions support
This commit is contained in:
commit
eb01a3e899
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
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;
|
||||
import org.apache.tika.parser.AutoDetectParser;
|
||||
import org.apache.tika.parser.ParseContext;
|
||||
import org.apache.tika.sax.BodyContentHandler;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
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.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;
|
||||
import org.xml.sax.ContentHandler;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* File ingest module to detect encryption and password protection.
|
||||
*/
|
||||
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;
|
||||
@ -154,18 +143,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -315,72 +295,12 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter
|
||||
/*
|
||||
* Qualify the entropy.
|
||||
*/
|
||||
calculatedEntropy = calculateEntropy(file);
|
||||
calculatedEntropy = EncryptionDetectionTools.calculateEntropy(file);
|
||||
if (calculatedEntropy >= minimumEntropy) {
|
||||
possiblyEncrypted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -107,11 +107,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);
|
||||
}
|
||||
}
|
||||
|
@ -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