From e491cdb48129752324c4e3764f99bd9203c56dec Mon Sep 17 00:00:00 2001
From: lgq <1015864684@qq.com>
Date: 星期二, 31 三月 2026 09:48:44 +0800
Subject: [PATCH] 1.新增VF205门禁机代码

---
 vf205_access/dxmodules/dxQrRule.js |  215 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 215 insertions(+), 0 deletions(-)

diff --git a/vf205_access/dxmodules/dxQrRule.js b/vf205_access/dxmodules/dxQrRule.js
new file mode 100644
index 0000000..36bc9a8
--- /dev/null
+++ b/vf205_access/dxmodules/dxQrRule.js
@@ -0,0 +1,215 @@
+//build: 20240301
+//寰厜浜岀淮鐮佺爜鍒惰鍒欙紝鍖呮嫭101锛�103 绛�
+//渚濊禆缁勪欢: dxDriver,dxCommon,dxLogger
+import common from './dxCommon.js'
+import base64 from './dxBase64.js'
+import logger from './dxLogger.js'
+let sqliteObj
+
+// 姣旇緝涓や釜瀛楃涓茬殑鍓峃涓瓧绗︽槸鍚︾浉绛�
+function comparePrefix(str1, str2, N) {
+    let substring1 = str1.substring(0, N);
+    let substring2 = str2.substring(0, N);
+    return substring1 === substring2;
+}
+
+
+
+// 101鐮佸�艰В鐮�
+function decode101(str) {
+    if (str.length < 5) {
+        logger.info("鏃犳晥浜岀淮鐮�")
+        throw new Error("code invalid,length too short")
+    }
+    let decodeBuf = base64.decode(str.slice(0, -4))
+    decodeBuf=decodeBuf.substring(4)
+
+    return decodeBuf
+}
+function hexStringToArrayBuffer (hexString) {
+    var byteString = hexString.match(/.{1,2}/g);
+    var byteArray = byteString.map(function (byte) {
+        return parseInt(byte, 16);
+    });
+    var buffer = new Uint8Array(byteArray).buffer;
+    return buffer;
+}
+
+/**
+ * 103鐮佸�艰В鐮�
+ * 1銆乥ase64瑙g爜
+ * 2銆佽В鏋愮粍缁囩紪鍙�
+ * 3銆丷SA瑙e瘑
+ * 4銆佽В鏋愯韩浠界被鍨�
+ * 5銆佽В鏋愭潈闄愭爣璇�
+ * 6銆佽В鏋愮敓鐮佹椂闂�
+ * 7銆佽В鏋愮爜杩囨湡鏃堕棿
+ * 8銆佹牎楠岄�氳鐮佹槸鍚﹁繃鏈�
+ * @param {*} str 
+ * @returns 
+ */
+function decode103(str) {
+    // FIXME 杩欎釜pubKey鍚庢湡闇�瑕佷粠閰嶇疆涓煡璇�
+    let TLV_T_SIZE = 2, TLV_L_SIZE = 2, offset = 0, code, decryptedData, generationCodeTime, expirationTime
+
+    // 1銆乥ase64瑙g爜
+    let decodeBuf = base64.toHexString(str)
+    decodeBuf= hexStringToArrayBuffer(decodeBuf)
+    let view = new Uint8Array(decodeBuf)
+    let organizationNumber;
+    // 2銆佽В鏋愮粍缁囩紪鍙�
+    if (view[offset] == 0x01) {
+        offset += TLV_T_SIZE
+        let orgNumLen = view[offset]
+        offset += TLV_L_SIZE
+        let orgNumBuf = new Uint8Array(decodeBuf, offset, orgNumLen);
+        organizationNumber = String.fromCharCode.apply(null, new Uint8Array(orgNumBuf));
+        logger.info("缁勭粐缂栧彿: " + organizationNumber)
+        offset += orgNumLen
+    } else {
+        throw new Error("code invalid,organization number error")
+    }
+
+    // 3銆丷SA瑙e瘑
+    if (view[offset] == 0x02) {
+        // 缁勭粐缂栧彿鏁版嵁闀垮害
+        offset += TLV_T_SIZE
+        let cipherTextLen = view[offset]
+        offset += TLV_L_SIZE
+        // 瀵瑰瘑鏂囪繘琛孯SA瑙e瘑
+        let encryptedData = decodeBuf.slice(offset, offset + cipherTextLen)
+
+
+        // TODO 绉橀挜鍐欐锛屽悗缁渶瑕佹毚闇插嚭鏉�
+        // RSA 鏌ヨ瀵嗛挜(涔熷彲浠ュ浐瀹氾紝涔熷彲浠ュ啓鍦ㄦ枃鏈腑)锛屾牴鎹瘑閽ュ啀娆¤В瀵�
+        // RSA瑙e瘑鍚庣殑鏁版嵁
+        let arr = sqliteObj.securityFindAllByCodeAndTypeAndTimeAndkey(undefined, undefined, undefined, Math.floor(Date.parse(new Date()) / 1000), organizationNumber)
+        if (arr && arr.length > 0) {
+            for (let data of arr) {
+                decryptedData = common.arrayBufferRsaDecrypt(encryptedData, data.value)
+                if (decryptedData != null) {
+                    break
+                }
+            }
+        }
+        if (!arr && arr.length <= 0 || decryptedData == null) {
+            return str
+        }
+    }
+    // 涓�涓澶囦竴涓瘑閽ワ紝鐩稿綋浜庤澶囧唴鐢ㄤ簬瑙e瘑鐨勫叕閽ユ槸鍥哄畾鐨勶紝鍙互鎶婂叕閽ユ斁鍒伴厤缃腑锛岃繖閲屽厛榛樿鍐欐
+    // "MTlBODExMDA2MTkwMzQ4Q0I5QUY3QTc4QzAzOTQzNUU5NzNFODAzMEU4QUU1QzBEMkZFOEYwRjEzRjU4M0M5MTU5QUU5MTdDMDIzRDU0RDgxRUY2NTI0NkUyQ0Y2MUMzMTQzNTNENjA2NDU5N0Y2OTY5RUE4QjA5MUY1RTYyODM=";
+    // let buf = common.arrayBufferRsaDecrypt(deData, deData.length)
+    // 0 3 0 3 0 31 30 33 4 0 a 0 31 30 35 34 33 32 33 33 32 33 5 0 4 0 af 8c fa 5a 6 0 1 0 35 7 0 0 0
+    // 0 
+    // 3 0 3 0 31 30 33 
+    // 4 0 a 0 31 30 35 34 33 32 33 33 32 33 
+    // 5 0 4 0 af 8c fa 5a 
+    // 6 0 1 0 35 
+    // 7 0 0 0
+
+    offset = 1;
+    view = new Uint8Array(decryptedData)
+    // 4銆佽В鏋愯韩浠界被鍨�(type:103)
+    if (view[offset] == 0x03) {
+        // 韬唤绫诲瀷鏁版嵁闀垮害
+        offset += TLV_T_SIZE
+        let identityTypeLength = view[offset]
+        // 韬唤绫诲瀷鏁版嵁
+        offset += TLV_L_SIZE
+        let identityTypeBuf = new Uint8Array(decryptedData, offset, identityTypeLength);
+        let identityType = String.fromCharCode.apply(null, identityTypeBuf);
+        offset += identityTypeLength
+        logger.info("韬唤绫诲瀷鏁版嵁: " + identityType)
+    }
+
+
+    // 5銆佽В鏋愭潈闄愭爣璇�(code)
+    if (view[offset] == 0x04) {
+        // 鏉冮檺鏍囪瘑鏁版嵁闀垮害
+        offset += TLV_T_SIZE
+        let identityCodeLength = view[offset]
+        // 鏉冮檺鏍囪瘑鏁版嵁
+        offset += TLV_L_SIZE
+        let identityCodeBuf = new Uint8Array(decryptedData, offset, identityCodeLength);
+        offset += identityCodeLength
+        code = String.fromCharCode.apply(null, identityCodeBuf);
+    }
+
+
+    // 6銆佽В鏋愮敓鐮佹椂闂�
+    if (view[offset] == 0x05) {
+        // 鐢熺爜鏃堕棿鏁版嵁闀垮害
+        offset += TLV_T_SIZE
+        let createTimeLength = view[offset]
+        // 鐢熺爜鏃堕棿鏁版嵁
+        offset += TLV_L_SIZE
+        let createTimeBuf = new Uint8Array(decryptedData, offset, createTimeLength);
+        offset += createTimeLength
+        generationCodeTime = parseInt(common.arrayBufferToHexString(createTimeBuf.reverse()), 16)
+    }
+
+
+    // 7銆佽В鏋愮爜杩囨湡鏃堕棿
+    if (view[offset] == 0x06) {
+        // 鐮佽繃鏈熸椂闂存暟鎹暱搴�
+        offset += TLV_T_SIZE
+        let expireTimeLength = view[offset]
+        // 鐮佽繃鏈熸椂闂存暟鎹�
+        offset += TLV_L_SIZE
+        let expireTimeBuf = new Uint8Array(decryptedData, offset, expireTimeLength);
+        offset += expireTimeLength
+        expirationTime = parseInt(String.fromCharCode.apply(null, expireTimeBuf))
+    }
+
+    // 8銆佹牎楠岄�氳鐮佹槸鍚﹁繃鏈�
+    let timestamp = Date.now();
+    expirationTime = generationCodeTime + expirationTime
+    if (expirationTime * 1000 > timestamp) {
+        return code
+    } else {
+        return null
+    }
+
+}
+
+const code = {
+    formatCode: function (msg, sqlObj) {
+        if (!msg) {
+            throw new Error("msg should not be null or empty")
+        }
+        if (!sqlObj) {
+            throw new Error("sqlObj should not be null or empty")
+        }
+        if (!sqliteObj) {
+            sqliteObj = sqlObj
+        }
+
+        let data = {}
+        // 鍒ゆ柇鐮佸��
+        if (comparePrefix(msg, "&llgy", "&llgy".length) || comparePrefix(msg, "&v101", "&v101".length)) {
+            // 101鐮佸��
+            data.type = 101
+            data.code = decode101(msg.substring(5))
+        }
+        else if (comparePrefix(msg, "vg://v103", "vg://v103".length)) {
+            // 103鐮佸��
+            data.type = 103
+            data.code = decode103(msg.substring(9)) ? decode103(msg.substring(9)) : msg.substring(9)
+        } else if (comparePrefix(msg, "___VBAR_CONFIG_V1.1.0___", "___VBAR_CONFIG_V1.1.0___".length) || comparePrefix(msg, "___VBAR_KZB_V1.1.0___", "___VBAR_KZB_V1.1.0___".length)) {
+            //TODO 鍏堣繖鏍峰啓锛岃璁哄ソ鍚庢洿鏀规祦杞�昏緫
+            data.type = 'config'
+            data.code = msg
+        } else {
+            data.type = 100
+            data.code = msg
+        }
+        if (data.code) {
+            return data
+        } else {
+            console.log("decode fail")
+            return
+        }
+    }
+}
+
+export default code;
\ No newline at end of file

--
Gitblit v1.9.3