From ed444f466141c70f552ae0591b1ad99fbdeb7a58 Mon Sep 17 00:00:00 2001
From: Greg DiCristofaro <gregd@basistech.com>
Date: Thu, 24 Aug 2023 15:22:40 -0400
Subject: [PATCH] better feedback on unsuccessful license

---
 .../autopsy/ctapi/json/LicenseResponse.java   |  9 +++++-
 .../ctapi/util/LicenseDecryptorUtil.java      | 19 ++++++++---
 .../ctcloud/Bundle.properties-MERGED          |  2 ++
 .../ctcloud/CTMalwareScannerOptionsPanel.java | 32 ++++++++++++++++---
 4 files changed, 53 insertions(+), 9 deletions(-)

diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseResponse.java
index a3a8247884..5a85778b60 100644
--- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseResponse.java
+++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseResponse.java
@@ -32,18 +32,21 @@ public class LicenseResponse {
     private final Boolean hostChanged;
     private final Long hostChangesRemaining;
     private final BoostLicenseResponse boostLicense;
+    private final String errorMsg;
 
     @JsonCreator
     public LicenseResponse(
             @JsonProperty("success") Boolean success,
             @JsonProperty("hostChanged") Boolean hostChanged,
             @JsonProperty("hostChangesRemaining") Long hostChangesRemaining,
-            @JsonProperty("boostLicense") BoostLicenseResponse boostLicense
+            @JsonProperty("boostLicense") BoostLicenseResponse boostLicense,
+            @JsonProperty("errorMsg") String errorMsg
     ) {
         this.success = success;
         this.hostChanged = hostChanged;
         this.hostChangesRemaining = hostChangesRemaining;
         this.boostLicense = boostLicense;
+        this.errorMsg = errorMsg;
     }
 
     public Boolean isSuccess() {
@@ -61,4 +64,8 @@ public Long getHostChangesRemaining() {
     public BoostLicenseResponse getBoostLicense() {
         return boostLicense;
     }
+
+    public String getErrorMsg() {
+        return errorMsg;
+    }
 }
diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/LicenseDecryptorUtil.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/LicenseDecryptorUtil.java
index f62b57d795..26ebe793a4 100644
--- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/LicenseDecryptorUtil.java
+++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/LicenseDecryptorUtil.java
@@ -34,6 +34,7 @@
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.KeySpec;
 import java.security.spec.X509EncodedKeySpec;
+import java.text.MessageFormat;
 import java.util.Base64;
 import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
@@ -42,6 +43,7 @@
 import javax.crypto.SecretKey;
 import javax.crypto.spec.IvParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
+import org.apache.commons.lang3.ObjectUtils;
 
 /**
  * Decrypts the payload of boost license.
@@ -58,12 +60,12 @@ public static LicenseDecryptorUtil getInstance() {
 
     private LicenseDecryptorUtil() {
     }
-    
+
     public LicenseInfo createLicenseInfo(LicenseResponse licenseResponse) throws JsonProcessingException, InvalidLicenseException {
-        if (licenseResponse == null || licenseResponse.getBoostLicense() == null) {
-            throw new InvalidLicenseException("License or boost license are null");
+        if (licenseResponse == null) {
+            throw new InvalidLicenseException("License is null");
         }
-        
+
         DecryptedLicenseResponse decrypted = parseLicenseJSON(licenseResponse.getBoostLicense());
         return new LicenseInfo(licenseResponse, decrypted);
     }
@@ -78,6 +80,9 @@ public LicenseInfo createLicenseInfo(LicenseResponse licenseResponse) throws Jso
      * com.basistech.df.cybertriage.autopsy.ctapi.util.LicenseDecryptorUtil.InvalidLicenseException
      */
     public DecryptedLicenseResponse parseLicenseJSON(BoostLicenseResponse licenseResponse) throws JsonProcessingException, InvalidLicenseException {
+        if (licenseResponse == null) {
+            throw new InvalidLicenseException("Boost license is null");
+        }
 
         String decryptedJsonResponse;
         try {
@@ -101,6 +106,12 @@ public DecryptedLicenseResponse parseLicenseJSON(BoostLicenseResponse licenseRes
     }
 
     private String decryptLicenseString(String encryptedJson, String ivBase64, String encryptedKey, String version) throws IOException, GeneralSecurityException, InvalidLicenseException {
+        if (ObjectUtils.anyNull(encryptedJson, ivBase64, encryptedKey, version)) {
+            throw new InvalidLicenseException(MessageFormat.format(
+                    "encryptedJson: {0}, iv: {1}, encryptedKey: {2}, version: {3} must all be non-null",
+                    encryptedJson, ivBase64, encryptedKey, version));
+        }
+
         if (!"1.0".equals(version)) {
             throw new InvalidLicenseException("Unexpected file version: " + version);
         }
diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties-MERGED b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties-MERGED
index 0e888c7fac..8dd673e2d7 100644
--- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties-MERGED
+++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties-MERGED
@@ -31,6 +31,8 @@ CTMalwareScannerOptionsPanel_licenseAddDialogEnteredErr_title=License Number Alr
 CTMalwareScannerOptionsPanel_licenseAddDialogPatternErr_desc=Please verify that license number is of format 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
 CTMalwareScannerOptionsPanel_licenseAddDialogPatternErr_title=Invalid License Number
 CTMalwareScannerOptionsPanel_LicenseFetcher_apiErr_title=Server Error
+# {0} - licenseCode
+CTMalwareScannerOptionsPanel_LicenseFetcher_defaultErrMsg_desc=Error activating boost license {0}
 CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_desc=A general error occurred while fetching license information.  Please try again later.
 CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_title=General Error
 # {0} - expiresDate
diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java
index 375943188c..735be72afc 100644
--- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java
+++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java
@@ -608,6 +608,8 @@ private void acceptEula(LicenseResponse licenseResponse) {
     @NbBundle.Messages({
         "CTMalwareScannerOptionsPanel_LicenseFetcher_apiErr_title=Server Error",
         "CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_title=General Error",
+        "# {0} - licenseCode",
+        "CTMalwareScannerOptionsPanel_LicenseFetcher_defaultErrMsg_desc=Error activating boost license {0}",
         "CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_desc=A general error occurred while fetching license information.  Please try again later.",})
     private class LicenseFetcher extends SwingWorker<LicenseResponse, Void> {
 
@@ -629,10 +631,9 @@ protected LicenseResponse doInBackground() throws Exception {
         protected void done() {
             try {
                 LicenseResponse licenseResponse = get();
-                if (licenseResponse != null && licenseResponse.isSuccess()) {
-                    SwingUtilities.invokeLater(() -> acceptEula(licenseResponse));
-                } else {
-                    logger.log(Level.WARNING, "An API error occurred while fetching license information.  License fetch was not successful");
+                // if no result, show unauthorized
+                if (licenseResponse == null) {
+                    logger.log(Level.WARNING, "An API error occurred while fetching license information.  License fetch returned no result.");
                     JOptionPane.showMessageDialog(
                             CTMalwareScannerOptionsPanel.this,
                             CTCloudException.ErrorCode.UN_AUTHORIZED.getDescription(),
@@ -640,7 +641,30 @@ protected void done() {
                             JOptionPane.ERROR_MESSAGE);
                     setLicenseDisplay(licenseInfo, null);
                     loadMalwareScansInfo(licenseInfo);
+                    return;
+                }
+                
+                // if not successful response 
+                if (!Boolean.TRUE.equals(licenseResponse.isSuccess())) {
+                    logger.log(Level.WARNING, "An API error occurred while fetching license information.  License fetch was not successful");
+                    // use default message unless error message specified
+                    String message = Bundle.CTMalwareScannerOptionsPanel_LicenseFetcher_defaultErrMsg_desc(licenseText);
+                    if (!StringUtils.isBlank(licenseResponse.getErrorMsg())) {
+                        message = licenseResponse.getErrorMsg();
+                    }
+                    JOptionPane.showMessageDialog(
+                            CTMalwareScannerOptionsPanel.this,
+                            message,
+                            Bundle.CTMalwareScannerOptionsPanel_LicenseFetcher_apiErr_title(),
+                            JOptionPane.ERROR_MESSAGE);
+                    setLicenseDisplay(licenseInfo, null);
+                    loadMalwareScansInfo(licenseInfo);
+                    return;
                 }
+                
+                // otherwise, load 
+                SwingUtilities.invokeLater(() -> acceptEula(licenseResponse));
+                
             } catch (InterruptedException | CancellationException ex) {
                 // ignore cancellation; just load current license
                 setLicenseDisplay(licenseInfo, null);
-- 
GitLab