19 package org.sleuthkit.autopsy.modules.encryptiondetection;
 
   21 import java.io.IOException;
 
   22 import java.util.Collections;
 
   23 import java.util.logging.Level;
 
   25 import java.io.BufferedInputStream;
 
   26 import java.io.InputStream;
 
   27 import org.apache.tika.exception.EncryptedDocumentException;
 
   28 import org.apache.tika.exception.TikaException;
 
   29 import org.apache.tika.metadata.Metadata;
 
   30 import org.apache.tika.parser.AutoDetectParser;
 
   31 import org.apache.tika.parser.ParseContext;
 
   32 import org.apache.tika.sax.BodyContentHandler;
 
   46 import org.
sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
 
   49 import org.xml.sax.ContentHandler;
 
   50 import org.xml.sax.SAXException;
 
   57 final class EncryptionDetectionFileIngestModule 
extends FileIngestModuleAdapter {
 
   59     private static final int FILE_SIZE_MODULUS = 512;
 
   60     private final IngestServices services = IngestServices.getInstance();
 
   61     private final Logger logger = services.getLogger(EncryptionDetectionModuleFactory.getModuleName());
 
   62     private FileTypeDetector fileTypeDetector;
 
   63     private Blackboard blackboard;
 
   64     private double calculatedEntropy;
 
   66     private final double minimumEntropy;
 
   67     private final int minimumFileSize;
 
   68     private final boolean fileSizeMultipleEnforced;
 
   69     private final boolean slackFilesAllowed;
 
   78     EncryptionDetectionFileIngestModule(EncryptionDetectionIngestJobSettings settings) {
 
   79         minimumEntropy = settings.getMinimumEntropy();
 
   80         minimumFileSize = settings.getMinimumFileSize();
 
   81         fileSizeMultipleEnforced = settings.isFileSizeMultipleEnforced();
 
   82         slackFilesAllowed = settings.isSlackFilesAllowed();
 
   86     public void startUp(IngestJobContext context) 
throws IngestModule.IngestModuleException {
 
   89             blackboard = Case.getOpenCase().getServices().getBlackboard();
 
   90             fileTypeDetector = 
new FileTypeDetector();
 
   91         } 
catch (FileTypeDetector.FileTypeDetectorInitException ex) {
 
   92             throw new IngestModule.IngestModuleException(
"Failed to create file type detector", ex);
 
   93         } 
catch (NoCurrentCaseException ex) {
 
   94             throw new IngestModule.IngestModuleException(
"Exception while getting open case.", ex);
 
   99     public IngestModule.ProcessResult process(AbstractFile file) {
 
  105             if (!file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
 
  106                     && !file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS)
 
  107                     && !file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR)
 
  108                     && !file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL_DIR)
 
  109                     && (!file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK) || slackFilesAllowed)) {
 
  113                 if (!file.getKnown().equals(TskData.FileKnown.KNOWN)) {
 
  117                     String mimeType = fileTypeDetector.getMIMEType(file);
 
  118                     if (mimeType.equals(
"application/octet-stream")) {
 
  119                         if (isFileEncryptionSuspected(file)) {
 
  120                             return flagFile(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED);
 
  123                         if (isFilePasswordProtected(file)) {
 
  124                             return flagFile(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED);
 
  129         } 
catch (ReadContentInputStreamException | SAXException | TikaException ex) {
 
  130             logger.log(Level.WARNING, String.format(
"Unable to read file '%s'", file.getParentPath() + file.getName()), ex);
 
  131             return IngestModule.ProcessResult.ERROR;
 
  132         } 
catch (IOException ex) {
 
  133             logger.log(Level.SEVERE, String.format(
"Unable to process file '%s'", file.getParentPath() + file.getName()), ex);
 
  134             return IngestModule.ProcessResult.ERROR;
 
  137         return IngestModule.ProcessResult.OK;
 
  146     private void validateSettings() throws IngestModule.IngestModuleException {
 
  147         EncryptionDetectionTools.validateMinEntropyValue(minimumEntropy);
 
  148         EncryptionDetectionTools.validateMinFileSizeValue(minimumFileSize);
 
  160     private IngestModule.ProcessResult flagFile(AbstractFile file, BlackboardArtifact.ARTIFACT_TYPE artifactType) {
 
  162             BlackboardArtifact artifact = file.newArtifact(artifactType);
 
  168                 blackboard.indexArtifact(artifact);
 
  169             } 
catch (Blackboard.BlackboardException ex) {
 
  170                 logger.log(Level.SEVERE, 
"Unable to index blackboard artifact " + artifact.getArtifactID(), ex); 
 
  176             services.fireModuleDataEvent(
new ModuleDataEvent(EncryptionDetectionModuleFactory.getModuleName(), artifactType, Collections.singletonList(artifact)));
 
  181             StringBuilder detailsSb = 
new StringBuilder();
 
  182             detailsSb.append(
"File: ").append(file.getParentPath()).append(file.getName());
 
  183             if (artifactType.equals(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED)) {
 
  184                 detailsSb.append(
"<br/>\n").append(
"Entropy: ").append(calculatedEntropy);
 
  187             services.postMessage(IngestMessage.createDataMessage(EncryptionDetectionModuleFactory.getModuleName(),
 
  188                     artifactType.getDisplayName() + 
" Match: " + file.getName(),
 
  189                     detailsSb.toString(),
 
  193             return IngestModule.ProcessResult.OK;
 
  194         } 
catch (TskCoreException ex) {
 
  195             logger.log(Level.SEVERE, String.format(
"Failed to create blackboard artifact for '%s'.", file.getParentPath() + file.getName()), ex); 
 
  196             return IngestModule.ProcessResult.ERROR;
 
  216     private boolean isFilePasswordProtected(AbstractFile file) 
throws ReadContentInputStreamException, IOException, SAXException, TikaException {
 
  218         boolean passwordProtected = 
false;
 
  220         switch (file.getMIMEType()) {
 
  221             case "application/x-ooxml-protected":
 
  226                 passwordProtected = 
true;
 
  229             case "application/msword":
 
  230             case "application/vnd.ms-excel":
 
  231             case "application/vnd.ms-powerpoint":
 
  232             case "application/pdf":
 
  237                 InputStream in = null;
 
  238                 BufferedInputStream bin = null;
 
  241                     in = 
new ReadContentInputStream(file);
 
  242                     bin = 
new BufferedInputStream(in);
 
  243                     ContentHandler handler = 
new BodyContentHandler(-1);
 
  244                     Metadata metadata = 
new Metadata();
 
  245                     metadata.add(Metadata.RESOURCE_NAME_KEY, file.getName());
 
  246                     AutoDetectParser parser = 
new AutoDetectParser();
 
  247                     parser.parse(bin, handler, metadata, 
new ParseContext());
 
  248                 } 
catch (EncryptedDocumentException ex) {
 
  252                     passwordProtected = 
true;
 
  263         return passwordProtected;
 
  281     private boolean isFileEncryptionSuspected(AbstractFile file) 
throws ReadContentInputStreamException, IOException {
 
  287         boolean possiblyEncrypted = 
false;
 
  292         long contentSize = file.getSize();
 
  293         if (contentSize >= minimumFileSize) {
 
  294             if (!fileSizeMultipleEnforced || (contentSize % FILE_SIZE_MODULUS) == 0) {
 
  298                 calculatedEntropy = EncryptionDetectionTools.calculateEntropy(file);
 
  299                 if (calculatedEntropy >= minimumEntropy) {
 
  300                     possiblyEncrypted = 
true;
 
  304         return possiblyEncrypted;