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) {