19 package org.sleuthkit.autopsy.modules.encryptiondetection;
 
   21 import java.io.IOException;
 
   22 import java.util.Arrays;
 
   23 import java.util.List;
 
   24 import java.util.logging.Level;
 
   25 import org.openide.util.NbBundle.Messages;
 
   48 final class EncryptionDetectionDataSourceIngestModule 
implements DataSourceIngestModule {
 
   50     private final IngestServices services = IngestServices.getInstance();
 
   51     private final Logger logger = services.getLogger(EncryptionDetectionModuleFactory.getModuleName());
 
   52     private Blackboard blackboard;
 
   53     private double calculatedEntropy;
 
   54     private final double minimumEntropy;
 
   55     private IngestJobContext context;
 
   64     EncryptionDetectionDataSourceIngestModule(EncryptionDetectionIngestJobSettings settings) {
 
   65         minimumEntropy = settings.getMinimumEntropy();
 
   69     public void startUp(IngestJobContext context) 
throws IngestModule.IngestModuleException {
 
   71         blackboard = Case.getCurrentCase().getSleuthkitCase().getBlackboard();
 
   72         this.context = context;
 
   76         "EncryptionDetectionDataSourceIngestModule.artifactComment.bitlocker=Bitlocker encryption detected.",
 
   77         "EncryptionDetectionDataSourceIngestModule.artifactComment.suspected=Suspected encryption due to high entropy (%f).",
 
   78         "EncryptionDetectionDataSourceIngestModule.processing.message=Checking image for encryption." 
   81     public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress progressBar) {
 
   84             if (dataSource instanceof Image) {
 
   86                 if (((Image) dataSource).getPaths().length == 0) {
 
   87                     logger.log(Level.SEVERE, String.format(
"Unable to process data source '%s' - image has no paths", dataSource.getName()));
 
   88                     return IngestModule.ProcessResult.ERROR;
 
   91                 List<VolumeSystem> volumeSystems = ((Image) dataSource).getVolumeSystems();
 
   92                 progressBar.switchToDeterminate(volumeSystems.size());
 
   93                 int numVolSystemsChecked = 0;
 
   94                 progressBar.progress(Bundle.EncryptionDetectionDataSourceIngestModule_processing_message(), 0);
 
   95                 for (VolumeSystem volumeSystem : volumeSystems) {
 
   97                     if (context.dataSourceIngestIsCancelled()) {
 
   98                         return ProcessResult.OK;
 
  101                     for (Volume volume : volumeSystem.getVolumes()) {
 
  103                         if (context.dataSourceIngestIsCancelled()) {
 
  104                             return ProcessResult.OK;
 
  106                         if (BitlockerDetection.isBitlockerVolume(volume)) {
 
  107                             return flagVolume(volume, BlackboardArtifact.Type.TSK_ENCRYPTION_DETECTED, Score.SCORE_NOTABLE, 
 
  108                                     Bundle.EncryptionDetectionDataSourceIngestModule_artifactComment_bitlocker());
 
  111                         if (context.dataSourceIngestIsCancelled()) {
 
  112                             return ProcessResult.OK;
 
  114                         if (isVolumeEncrypted(volume)) {
 
  115                             return flagVolume(volume, BlackboardArtifact.Type.TSK_ENCRYPTION_SUSPECTED, Score.SCORE_LIKELY_NOTABLE,
 
  116                                     String.format(Bundle.EncryptionDetectionDataSourceIngestModule_artifactComment_suspected(), calculatedEntropy));
 
  120                     numVolSystemsChecked++;
 
  121                     progressBar.progress(Bundle.EncryptionDetectionDataSourceIngestModule_processing_message(), numVolSystemsChecked);
 
  124         } 
catch (ReadContentInputStream.ReadContentInputStreamException ex) {
 
  125             logger.log(Level.WARNING, String.format(
"Unable to read data source '%s'", dataSource.getName()), ex);
 
  126             return IngestModule.ProcessResult.ERROR;
 
  127         } 
catch (IOException | TskCoreException ex) {
 
  128             logger.log(Level.SEVERE, String.format(
"Unable to process data source '%s'", dataSource.getName()), ex);
 
  129             return IngestModule.ProcessResult.ERROR;
 
  132         return IngestModule.ProcessResult.OK;
 
  143     private void validateSettings() throws IngestModule.IngestModuleException {
 
  144         EncryptionDetectionTools.validateMinEntropyValue(minimumEntropy);
 
  159     private IngestModule.ProcessResult flagVolume(Volume volume, BlackboardArtifact.Type artifactType, Score score, String comment) {
 
  161         if (context.dataSourceIngestIsCancelled()) {
 
  162             return ProcessResult.OK;
 
  166             BlackboardArtifact artifact = volume.newAnalysisResult(artifactType, score, null, null, comment, 
 
  167                     Arrays.asList(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, EncryptionDetectionModuleFactory.getModuleName(), comment)))
 
  168                     .getAnalysisResult();
 
  175                 blackboard.postArtifact(artifact, EncryptionDetectionModuleFactory.getModuleName(), context.getJobId());
 
  176             } 
catch (Blackboard.BlackboardException ex) {
 
  177                 logger.log(Level.SEVERE, 
"Unable to index blackboard artifact " + artifact.getArtifactID(), ex); 
 
  183             StringBuilder detailsSb = 
new StringBuilder(
"");
 
  184             detailsSb.append(
"File: ");
 
  185             Content parentFile = volume.getParent();
 
  186             if (parentFile != null) {
 
  187                 detailsSb.append(volume.getParent().getUniquePath());
 
  189             detailsSb.append(volume.getName());
 
  190             if (artifactType.equals(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED)) {
 
  191                 detailsSb.append(
"<br/>\nEntropy: ").append(calculatedEntropy);
 
  194             services.postMessage(IngestMessage.createDataMessage(EncryptionDetectionModuleFactory.getModuleName(),
 
  195                     artifactType.getDisplayName() + 
" Match: " + volume.getName(),
 
  196                     detailsSb.toString(),
 
  200             return IngestModule.ProcessResult.OK;
 
  201         } 
catch (TskCoreException ex) {
 
  202             logger.log(Level.SEVERE, String.format(
"Failed to create blackboard artifact for '%s'.", volume.getName()), ex); 
 
  203             return IngestModule.ProcessResult.ERROR;
 
  215     private boolean isVolumeEncrypted(Volume volume) 
throws ReadContentInputStream.ReadContentInputStreamException, IOException, TskCoreException {
 
  220         if (volume.getFileSystems().isEmpty()) {
 
  221             calculatedEntropy = EncryptionDetectionTools.calculateEntropy(volume, context);
 
  222             if (calculatedEntropy >= minimumEntropy) {