]> git.basschouten.com Git - openhab-addons.git/commitdiff
[gree] Add support for ASC/GCM encryption (#16950)
authorZhivka Dimova <zhivka.dimova@myforest.net>
Thu, 1 Aug 2024 09:36:52 +0000 (11:36 +0200)
committerGitHub <noreply@github.com>
Thu, 1 Aug 2024 09:36:52 +0000 (11:36 +0200)
* [gree] support for ASC/GCM encryption

Signed-off-by: Zhivka Dimova <zhivka.dimova@myforest.net>
20 files changed:
bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/GreeCryptoUtil.java
bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/discovery/GreeDeviceFinder.java
bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeBaseDTO.java [new file with mode: 0644]
bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeBindRequestPackDTO.java
bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeBindResponseDTO.java
bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeBindResponsePackDTO.java
bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeExecResponseDTO.java
bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeExecResponsePackDTO.java
bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeExecuteCommandPackDTO.java
bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeReqStatusDTO.java
bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeReqStatusPackDTO.java
bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeRequestDTO.java
bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeResponseBaseDTO.java [new file with mode: 0644]
bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeScanReponsePackDTO.java [deleted file]
bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeScanRequestDTO.java
bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeScanResponseDTO.java
bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeScanResponsePackDTO.java [new file with mode: 0644]
bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeStatusResponseDTO.java
bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeStatusResponsePackDTO.java
bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/handler/GreeAirDevice.java

index 7e58f002055062f8b8fa79330fbb003516aea38d..f51678788bcb057521bbcfba2721c7db372d0eaf 100644 (file)
 package org.openhab.binding.gree.internal;
 
 import java.nio.charset.StandardCharsets;
+import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.Key;
 import java.security.NoSuchAlgorithmException;
 import java.util.Base64;
+import java.util.HexFormat;
 
 import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
 import javax.crypto.IllegalBlockSizeException;
 import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.GCMParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.gree.internal.gson.GreeBaseDTO;
 
 /**
  * The CryptoUtil class provides functionality for encrypting and decrypting
@@ -36,11 +40,68 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 @NonNullByDefault
 public class GreeCryptoUtil {
     private static final String AES_KEY = "a3K8Bx%2r8Y7#xDh";
+    private static final String GCM_KEY = "{yxAHAY_Lm6pbC/<";
+    private static final String GCM_IV = "5440784449675a516c5e6313";
+    private static final String GCM_ADD = "qualcomm-test";
+    private static final int TAG_LENGTH = 16;
+
+    public enum EncryptionTypes {
+        ECB,
+        GCM
+    };
 
     public static byte[] getAESGeneralKeyByteArray() {
         return AES_KEY.getBytes(StandardCharsets.UTF_8);
     }
 
+    public static byte[] getGCMGeneralKeyByteArray() {
+        return GCM_KEY.getBytes(StandardCharsets.UTF_8);
+    }
+
+    public static byte[] getGeneralKeyByteArray(EncryptionTypes encType) {
+        if (encType == EncryptionTypes.GCM) {
+            return getGCMGeneralKeyByteArray();
+        }
+        return getAESGeneralKeyByteArray();
+    }
+
+    public static byte[] getGCMIVByteArray() {
+        return HexFormat.of().parseHex(GCM_IV);
+    }
+
+    public static byte[] getGCMADDByteArray() {
+        return GCM_ADD.getBytes(StandardCharsets.UTF_8);
+    }
+
+    public static <T extends GreeBaseDTO> EncryptionTypes getEncryptionType(T response) {
+        return response.tag != null ? EncryptionTypes.GCM : EncryptionTypes.ECB;
+    }
+
+    public static <T extends GreeBaseDTO> String decrypt(T response) throws GreeException {
+        return decrypt(response, getEncryptionType(response));
+    }
+
+    public static <T extends GreeBaseDTO> String decrypt(byte[] keyarray, T response) throws GreeException {
+        return decrypt(keyarray, response, getEncryptionType(response));
+    }
+
+    public static <T extends GreeBaseDTO> String decrypt(T response, EncryptionTypes encType) throws GreeException {
+        if (encType == EncryptionTypes.GCM) {
+            return decrypt(getGCMGeneralKeyByteArray(), response, encType);
+        } else {
+            return decrypt(getAESGeneralKeyByteArray(), response, encType);
+        }
+    }
+
+    public static <T extends GreeBaseDTO> String decrypt(byte[] keyarray, T response, EncryptionTypes encType)
+            throws GreeException {
+        if (encType == EncryptionTypes.GCM) {
+            return decryptGCMPack(keyarray, response.pack, response.tag);
+        } else {
+            return decryptPack(keyarray, response.pack);
+        }
+    }
+
     public static String decryptPack(byte[] keyarray, String message) throws GreeException {
         try {
             Key key = new SecretKeySpec(keyarray, "AES");
@@ -58,6 +119,41 @@ public class GreeCryptoUtil {
         }
     }
 
+    public static String decryptGCMPack(byte[] keyBytes, String pack, String tag) throws GreeException {
+        try {
+            Key key = new SecretKeySpec(keyBytes, "AES");
+            Base64.Decoder decoder = Base64.getDecoder();
+
+            byte[] packBytes = decoder.decode(pack);
+            byte[] tagBytes = decoder.decode(tag);
+
+            byte[] messageBytes = new byte[packBytes.length + tagBytes.length];
+            System.arraycopy(packBytes, 0, messageBytes, 0, packBytes.length);
+            System.arraycopy(tagBytes, 0, messageBytes, packBytes.length, tagBytes.length);
+
+            Cipher gcmCipher = Cipher.getInstance("AES/GCM/NoPadding");
+            GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, getGCMIVByteArray());
+            gcmCipher.init(Cipher.DECRYPT_MODE, key, gcmParameterSpec);
+            gcmCipher.updateAAD(getGCMADDByteArray());
+
+            byte[] bytePlainText = gcmCipher.doFinal(messageBytes);
+            return new String(bytePlainText, StandardCharsets.UTF_8);
+        } catch (NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException | InvalidKeyException
+                | IllegalBlockSizeException | InvalidAlgorithmParameterException ex) {
+            throw new GreeException("GCM decryption of recieved data failed", ex);
+        }
+    }
+
+    public static String[] encrypt(byte[] keyarray, String message, EncryptionTypes encType) throws GreeException {
+        if (encType == EncryptionTypes.GCM) {
+            return encryptGCMPack(keyarray, message);
+        } else {
+            String[] res = new String[1];
+            res[0] = encryptPack(keyarray, message);
+            return res;
+        }
+    }
+
     public static String encryptPack(byte[] keyarray, String message) throws GreeException {
         try {
             Key key = new SecretKeySpec(keyarray, "AES");
@@ -72,4 +168,32 @@ public class GreeCryptoUtil {
             throw new GreeException("Unable to encrypt outbound data", ex);
         }
     }
+
+    public static String[] encryptGCMPack(byte[] keyarray, String message) throws GreeException {
+        try {
+            Key key = new SecretKeySpec(keyarray, "AES");
+
+            Cipher gcmCipher = Cipher.getInstance("AES/GCM/NoPadding");
+            GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, getGCMIVByteArray());
+            gcmCipher.init(Cipher.ENCRYPT_MODE, key, gcmParameterSpec);
+            gcmCipher.updateAAD(getGCMADDByteArray());
+
+            byte[] encrypted = gcmCipher.doFinal(message.getBytes(StandardCharsets.UTF_8));
+
+            int packLength = encrypted.length - TAG_LENGTH;
+            byte[] pack = new byte[packLength];
+            byte[] tag = new byte[TAG_LENGTH];
+            System.arraycopy(encrypted, 0, pack, 0, packLength);
+            System.arraycopy(encrypted, packLength, tag, 0, TAG_LENGTH);
+
+            Base64.Encoder encoder = Base64.getEncoder();
+            String[] encryptedData = new String[2];
+            encryptedData[0] = new String(encoder.encode(pack), StandardCharsets.UTF_8);
+            encryptedData[1] = new String(encoder.encode(tag), StandardCharsets.UTF_8);
+            return encryptedData;
+        } catch (NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException | InvalidKeyException
+                | IllegalBlockSizeException | InvalidAlgorithmParameterException ex) {
+            throw new GreeException("Unable to encrypt (gcm) outbound data", ex);
+        }
+    }
 }
index 61b0cb8e50e8bc21862ce1d8b1675a7bebdbb03d..bc277d7034e61748e8d89a69744af93aa4f96290 100644 (file)
@@ -28,9 +28,9 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.gree.internal.GreeCryptoUtil;
 import org.openhab.binding.gree.internal.GreeException;
-import org.openhab.binding.gree.internal.gson.GreeScanReponsePackDTO;
 import org.openhab.binding.gree.internal.gson.GreeScanRequestDTO;
 import org.openhab.binding.gree.internal.gson.GreeScanResponseDTO;
+import org.openhab.binding.gree.internal.gson.GreeScanResponsePackDTO;
 import org.openhab.binding.gree.internal.handler.GreeAirDevice;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
@@ -103,12 +103,12 @@ public class GreeDeviceFinder {
                     }
 
                     // Decrypt message - a GreeException is thrown when something went wrong
-                    String decryptedMsg = scanResponseGson.decryptedPack = GreeCryptoUtil
-                            .decryptPack(GreeCryptoUtil.getAESGeneralKeyByteArray(), scanResponseGson.pack);
+                    String decryptedMsg = scanResponseGson.decryptedPack = GreeCryptoUtil.decrypt(scanResponseGson);
+
                     logger.debug("Response received from address {}: {}", remoteAddress.getHostAddress(), decryptedMsg);
 
                     // Create the JSON to hold the response values
-                    scanResponseGson.packJson = GSON.fromJson(decryptedMsg, GreeScanReponsePackDTO.class);
+                    scanResponseGson.packJson = GSON.fromJson(decryptedMsg, GreeScanResponsePackDTO.class);
 
                     // Now make sure the device is reported as a Gree device
                     if ("gree".equalsIgnoreCase(scanResponseGson.packJson.brand)) {
diff --git a/bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeBaseDTO.java b/bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeBaseDTO.java
new file mode 100644 (file)
index 0000000..a758de6
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.gree.internal.gson;
+
+/**
+ *
+ * The GreeBaseDTO class is used as a base class for request and response classes
+ *
+ * @author Zhivka Dimvoa - Initial contribution
+ */
+public class GreeBaseDTO {
+    public String t = null;
+    public int i = 0;
+    public int uid = 0;
+    public String cid = null;
+    public String tcid = null;
+    public String tag = null;
+    public String pack = null;
+}
index 78bf49d6bd7e500fe8b3485268814c1a160d2167..3791a6722ea5cab2120098ba635b408933e780f4 100644 (file)
@@ -14,7 +14,7 @@ package org.openhab.binding.gree.internal.gson;
 
 /**
  *
- * The GreeBindRequestPack4Gson class is used by Gson to hold values to be send to
+ * The GreeBindRequestPackDTO class is used by Gson to hold values to be send to
  * the Air Conditioner during Binding
  *
  * @author John Cunha - Initial contribution
index 4a3ca6b11a8b7dfd1acf3f79e5aa2e5c2566f1a0..9ed9ddc6048cc27cf3063f9458db19480138e08c 100644 (file)
@@ -14,20 +14,11 @@ package org.openhab.binding.gree.internal.gson;
 
 /**
  *
- * The GreeBindResponse4Gson class is used by Gson to hold values returned from
+ * The GreeBindResponseDTO class is used by Gson to hold values returned from
  * the Air Conditioner during Binding
  *
  * @author John Cunha - Initial contribution
  */
-public class GreeBindResponseDTO {
-
-    public String t = null;
-    public int i = 0;
-    public int uid = 0;
-    public String cid = null;
-    public String tcid = null;
-    public String pack = null;
-
-    public transient String decryptedPack = null;
+public class GreeBindResponseDTO extends GreeResponseBaseDTO {
     public transient GreeBindResponsePackDTO packJson = null;
 }
index 1ec5335eae8a2039000fc406e054fd1313a13782..8c05b3c9b6ae3d58b029a2fcf6a0a165b1f48ddc 100644 (file)
@@ -14,7 +14,7 @@ package org.openhab.binding.gree.internal.gson;
 
 /**
  *
- * The GreeBindResponsePack4Gson class is used by Gson to hold values returned from
+ * The GreeBindResponsePackDTO class is used by Gson to hold values returned from
  * the Air Conditioner during Binding
  *
  * @author John Cunha - Initial contribution
index e37cfc2d2762c0501177d0a37bedccc004168b2d..34c3bec9cb19792ef2e3b79abfdc42ceb0c21b66 100644 (file)
@@ -14,21 +14,12 @@ package org.openhab.binding.gree.internal.gson;
 
 /**
  *
- * The GreeExecResponse4Gson class is used by Gson to hold values returned from
+ * The GreeExecResponseDTO class is used by Gson to hold values returned from
  * the Air Conditioner during requests for Execution of Commands to the
  * Air Conditioner.
  *
  * @author John Cunha - Initial contribution
  */
-public class GreeExecResponseDTO {
-
-    public String t = null;
-    public int i = 0;
-    public int uid = 0;
-    public String cid = null;
-    public String tcid = null;
-    public String pack = null;
-
-    public transient String decryptedPack = null;
+public class GreeExecResponseDTO extends GreeResponseBaseDTO {
     public transient GreeExecResponsePackDTO packJson = null;
 }
index b6d595f2606a4ce1f50fead7de9ac154ce785c1b..e97e81c15e701c9f04e27661a7016f1b1ff13e0b 100644 (file)
@@ -14,7 +14,7 @@ package org.openhab.binding.gree.internal.gson;
 
 /**
  *
- * The GreeExecResponsePack4Gson class is used by Gson to hold values returned from
+ * The GreeExecResponsePackDTO class is used by Gson to hold values returned from
  * the Air Conditioner during requests for Execution of Commands to the
  * Air Conditioner.
  *
index 7e17ef545f4f097334ac632dce6ac3ee65bab7c6..dd34dedf37809b8b41fcc58deccb94659271d8f4 100644 (file)
@@ -14,7 +14,7 @@ package org.openhab.binding.gree.internal.gson;
 
 /**
  *
- * The GreeExecuteCommandPack4Gson class is used by Gson to hold values to be send to
+ * The GreeExecuteCommandPackDTO class is used by Gson to hold values to be send to
  * the Air Conditioner during requests for Execution of Commands to the
  * Air Conditioner.
  *
index 69c49ae2ad85359348fc221eee405c67f511db3e..d77571babac0914ad661bd5a94986cdda1835bd9 100644 (file)
@@ -14,17 +14,11 @@ package org.openhab.binding.gree.internal.gson;
 
 /**
  *
- * The GreeReqStatus4Gson class is used by Gson to hold values to be send to
+ * The GreeReqStatusDTO class is used by Gson to hold values to be send to
  * the Air Conditioner during requests for Status Updates to the
  * Air Conditioner.
  *
  * @author John Cunha - Initial contribution
  */
-public class GreeReqStatusDTO {
-    public String cid = null;
-    public int i = 0;
-    public String t = null;
-    public int uid = 0;
-    public String pack = null;
-    public String tcid = null;
+public class GreeReqStatusDTO extends GreeRequestDTO {
 }
index b2655595cfa14c08cf46d5763aaf9f44729f3274..9c06ca535139395c23848c528ae83cc17b3b0c4d 100644 (file)
@@ -14,14 +14,13 @@ package org.openhab.binding.gree.internal.gson;
 
 /**
  *
- * The GreeReqStatusPack4Gson class is used by Gson to hold values to be send to
+ * The GreeReqStatusPackDTO class is used by Gson to hold values to be send to
  * the Air Conditioner during requests for Status Updates to the
  * Air Conditioner.
  *
  * @author John Cunha - Initial contribution
  */
 public class GreeReqStatusPackDTO {
-
     public String t = null;
     public String[] cols = null;
     public String mac = null;
index 4207e0c4c5665ded3179b557a697a16a6134d844..cd3cc8fbb1a90d9909d5915e6c17d301e8c645d4 100644 (file)
@@ -14,17 +14,10 @@ package org.openhab.binding.gree.internal.gson;
 
 /**
  *
- * The GreeBindRequest4Gson class is used by Gson to hold values to be send to
- * the Air Conditioner during Binding
+ * The GreeRequestDTO class is used by Gson to hold values to be send to
+ * the Air Conditioner during Binding and as a base class for other request classes
  *
  * @author John Cunha - Initial contribution
  */
-public class GreeRequestDTO {
-
-    public int uid = 0;
-    public String t = null;
-    public int i = 0;
-    public String pack = null;
-    public String cid = null;
-    public String tcid = null;
+public class GreeRequestDTO extends GreeBaseDTO {
 }
diff --git a/bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeResponseBaseDTO.java b/bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeResponseBaseDTO.java
new file mode 100644 (file)
index 0000000..17042a6
--- /dev/null
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.gree.internal.gson;
+
+/**
+ *
+ * The GreeResponseBaseDTO class is used as a base class for response classes
+ *
+ * @author Zhivka Dimvoa - Initial contribution
+ */
+public class GreeResponseBaseDTO extends GreeBaseDTO {
+    public transient String decryptedPack = null;
+}
diff --git a/bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeScanReponsePackDTO.java b/bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeScanReponsePackDTO.java
deleted file mode 100644 (file)
index a7cf2e2..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * Copyright (c) 2010-2024 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.gree.internal.gson;
-
-/**
- *
- * The GreeScanReponsePack4Gson class is used by Gson to hold values returned by
- * the Air Conditioner during Scan Requests to the Air Conditioner.
- *
- * @author John Cunha - Initial contribution
- */
-public class GreeScanReponsePackDTO {
-
-    public String t = null;
-    public String cid = null;
-    public String bc = null;
-    public String brand = null;
-    public String catalog = null;
-    public String mac = null;
-    public String mid = null;
-    public String model = null;
-    public String name = null;
-    public String series = null;
-    public String vender = null;
-    public String ver = null;
-    public int lock = 0;
-}
index a823ffe35b8bc6a1d3bd30a399adeb8f240c499d..f9e9dfb6083bbb32347fd1d987ee26dd56d4c96f 100644 (file)
@@ -14,7 +14,7 @@ package org.openhab.binding.gree.internal.gson;
 
 /**
  *
- * The GreeScanRequest4Gson class is used by Gson to hold values sent to
+ * The GreeScanRequestDTO class is used by Gson to hold values sent to
  * the Air Conditioner during Scan Requests to the Air Conditioner.
  *
  * @author John Cunha - Initial contribution
index 2a6310528302f4097ba99e6eb39991e83d37d222..601139bf181cf98d82588fd8cb21db91ecc5b27b 100644 (file)
@@ -14,18 +14,11 @@ package org.openhab.binding.gree.internal.gson;
 
 /**
  *
- * The GreeScanResponse4Gson class is used by Gson to hold values returned by
+ * The GreeScanResponseDTO class is used by Gson to hold values returned by
  * the Air Conditioner during Scan Requests to the Air Conditioner.
  *
  * @author John Cunha - Initial contribution
  */
-public class GreeScanResponseDTO {
-    public String t = null;
-    public int i = 0;
-    public int uid = 0;
-    public String cid = null;
-    public String tcid = null;
-    public String pack = null;
-    public transient String decryptedPack = null;
-    public transient GreeScanReponsePackDTO packJson = null;
+public class GreeScanResponseDTO extends GreeResponseBaseDTO {
+    public transient GreeScanResponsePackDTO packJson = null;
 }
diff --git a/bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeScanResponsePackDTO.java b/bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/gson/GreeScanResponsePackDTO.java
new file mode 100644 (file)
index 0000000..89746a0
--- /dev/null
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.gree.internal.gson;
+
+/**
+ *
+ * The GreeScanReponsePackDTO class is used by Gson to hold values returned by
+ * the Air Conditioner during Scan Requests to the Air Conditioner.
+ *
+ * @author John Cunha - Initial contribution
+ */
+public class GreeScanResponsePackDTO {
+    public String t = null;
+    public String cid = null;
+    public String bc = null;
+    public String brand = null;
+    public String catalog = null;
+    public String mac = null;
+    public String mid = null;
+    public String model = null;
+    public String name = null;
+    public String series = null;
+    public String vender = null;
+    public String ver = null;
+    public int lock = 0;
+}
index 14f358ff1f2a673beb5edbda83a79becdb4045fc..9e7f6c101766c1086d07977bb7de7cad93df0301 100644 (file)
@@ -14,21 +14,12 @@ package org.openhab.binding.gree.internal.gson;
 
 /**
  *
- * The GreeStatusResponse4Gson class is used by Gson to hold values returned from
+ * The GreeStatusResponseDTO class is used by Gson to hold values returned from
  * the Air Conditioner during requests for Status Updates to the
  * Air Conditioner.
  *
  * @author John Cunha - Initial contribution
  */
-public class GreeStatusResponseDTO {
-
-    public String t = null;
-    public int i = 0;
-    public int uid = 0;
-    public String cid = null;
-    public String tcid = null;
-    public String pack = null;
-
-    public transient String decryptedPack = null;
+public class GreeStatusResponseDTO extends GreeResponseBaseDTO {
     public transient GreeStatusResponsePackDTO packJson = null;
 }
index 6daac5a87247f984ddfe09838365e15627f770a0..5ec5bb5a2b2737150a6ee74139cc6a86458b8166 100644 (file)
@@ -14,7 +14,7 @@ package org.openhab.binding.gree.internal.gson;
 
 /**
  *
- * The GreeStatusResponsePack4Gson class is used by Gson to hold values returned from
+ * The GreeStatusResponsePackDTO class is used by Gson to hold values returned from
  * the Air Conditioner during requests for Status Updates to the
  * Air Conditioner.
  *
index 8ef42783bf45fe5cf69cb7dc2ecdcdadf0ecd9cd..2a7db2a5c59193f79da32eee70493b0648ea3eff 100644 (file)
@@ -64,6 +64,7 @@ public class GreeAirDevice {
     private final InetAddress ipAddress;
     private int port = 0;
     private String encKey = "";
+    private GreeCryptoUtil.EncryptionTypes encType = GreeCryptoUtil.EncryptionTypes.ECB;
     private Optional<GreeScanResponseDTO> scanResponseGson = Optional.empty();
     private Optional<GreeStatusResponseDTO> statusResponseGson = Optional.empty();
     private Optional<GreeStatusResponsePackDTO> prevStatusResponsePackGson = Optional.empty();
@@ -76,6 +77,7 @@ public class GreeAirDevice {
         this.ipAddress = ipAddress;
         this.port = port;
         this.scanResponseGson = Optional.of(scanResponse);
+        this.encType = GreeCryptoUtil.getEncryptionType(scanResponse);
     }
 
     public void getDeviceStatus(DatagramSocket clientSocket) throws GreeException {
@@ -117,9 +119,8 @@ public class GreeAirDevice {
             String reqStatusPackStr = GSON.toJson(reqStatusPackGson);
 
             // Encrypt and send the Status Request pack
-            String encryptedStatusReqPacket = GreeCryptoUtil.encryptPack(getKey(), reqStatusPackStr);
-            DatagramPacket sendPacket = createPackRequest(0,
-                    new String(encryptedStatusReqPacket.getBytes(), StandardCharsets.UTF_8));
+            String[] encryptedStatusReqData = GreeCryptoUtil.encrypt(getKey(), reqStatusPackStr, encType);
+            DatagramPacket sendPacket = createPackRequest(0, encryptedStatusReqData);
             clientSocket.send(sendPacket);
 
             // Keep a copy of the old response to be used to check if values have changed
@@ -131,7 +132,7 @@ public class GreeAirDevice {
 
             // Read the response, create the JSON to hold the response values
             GreeStatusResponseDTO resp = receiveResponse(clientSocket, GreeStatusResponseDTO.class);
-            resp.decryptedPack = GreeCryptoUtil.decryptPack(getKey(), resp.pack);
+            resp.decryptedPack = GreeCryptoUtil.decrypt(getKey(), resp, encType);
             logger.debug("Response from device: {}", resp.decryptedPack);
             resp.packJson = GSON.fromJson(resp.decryptedPack, GreeStatusResponsePackDTO.class);
 
@@ -157,14 +158,14 @@ public class GreeAirDevice {
             String bindReqPackStr = GSON.toJson(bindReqPackGson);
 
             // Encrypt and send the Binding Request pack
-            String encryptedBindReqPacket = GreeCryptoUtil.encryptPack(GreeCryptoUtil.getAESGeneralKeyByteArray(),
-                    bindReqPackStr);
-            DatagramPacket sendPacket = createPackRequest(1, encryptedBindReqPacket);
+            String[] encryptedBindReqData = GreeCryptoUtil.encrypt(GreeCryptoUtil.getGeneralKeyByteArray(encType),
+                    bindReqPackStr, encType);
+            DatagramPacket sendPacket = createPackRequest(1, encryptedBindReqData);
             clientSocket.send(sendPacket);
 
             // Recieve a response, create the JSON to hold the response values
             GreeBindResponseDTO resp = receiveResponse(clientSocket, GreeBindResponseDTO.class);
-            resp.decryptedPack = GreeCryptoUtil.decryptPack(GreeCryptoUtil.getAESGeneralKeyByteArray(), resp.pack);
+            resp.decryptedPack = GreeCryptoUtil.decrypt(resp, encType);
             resp.packJson = GSON.fromJson(resp.decryptedPack, GreeBindResponsePackDTO.class);
 
             // Now set the key and flag to indicate the bind was successful
@@ -424,13 +425,13 @@ public class GreeAirDevice {
             String execCmdPackStr = GSON.toJson(execCmdPackGson);
 
             // Now encrypt and send the Command Request pack
-            String encryptedCommandReqPacket = GreeCryptoUtil.encryptPack(getKey(), execCmdPackStr);
-            DatagramPacket sendPacket = createPackRequest(0, encryptedCommandReqPacket);
+            String[] encryptedCommandReqData = GreeCryptoUtil.encrypt(getKey(), execCmdPackStr, encType);
+            DatagramPacket sendPacket = createPackRequest(0, encryptedCommandReqData);
             clientSocket.send(sendPacket);
 
             // Receive and decode result
             GreeExecResponseDTO execResponseGson = receiveResponse(clientSocket, GreeExecResponseDTO.class);
-            execResponseGson.decryptedPack = GreeCryptoUtil.decryptPack(getKey(), execResponseGson.pack);
+            execResponseGson.decryptedPack = GreeCryptoUtil.decrypt(getKey(), execResponseGson, encType);
 
             // Create the JSON to hold the response values
             execResponseGson.packJson = GSON.fromJson(execResponseGson.decryptedPack, GreeExecResponsePackDTO.class);
@@ -451,14 +452,21 @@ public class GreeAirDevice {
         executeCommand(clientSocket, Map.of(command, value));
     }
 
-    private DatagramPacket createPackRequest(int i, String pack) {
+    private DatagramPacket createPackRequest(int i, String[] data) {
         GreeRequestDTO request = new GreeRequestDTO();
         request.cid = GREE_CID;
         request.i = i;
         request.t = GREE_CMDT_PACK;
         request.uid = 0;
         request.tcid = getId();
-        request.pack = pack;
+        request.pack = data[0];
+        if (encType == GreeCryptoUtil.EncryptionTypes.GCM) {
+            if (data.length > 1) {
+                request.tag = data[1];
+            } else {
+                logger.warn("Missing string for tag property for GCM encryption data");
+            }
+        }
         byte[] sendData = GSON.toJson(request).getBytes(StandardCharsets.UTF_8);
         return new DatagramPacket(sendData, sendData.length, ipAddress, port);
     }