SEC_Warehouse/utils/ble/modules/auth.js

181 lines
4.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { buildA7Packets } from '../core/packet'
import { parseDeviceInfo } from '../parsers'
import { BLE_STAGE } from '../core/state'
function waitForStateCode(ble, timeout = 6000) {
return new Promise((resolve, reject) => {
let timer = null
let off = null
const finish = (callback) => {
if (timer) {
clearTimeout(timer)
}
if (off) {
off()
}
callback()
}
timer = setTimeout(() => {
finish(() => reject(new Error('等待 UUID_STATE 超时')))
}, timeout)
off = ble.on('protocol:state', (payload) => {
finish(() => resolve(payload))
})
})
}
function generateRandomKey(length = 16) {
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789'
let result = ''
for (let index = 0; index < length; index += 1) {
const random = Math.floor(Math.random() * chars.length)
result += chars[random]
}
return result
}
async function writePacketsToKeyCharacteristic(ble, keyText) {
const { key } = ble.getState().characteristics
if (!key || !key.uuid) {
throw new Error('未发现 UUID_KEY 特征值')
}
const profile = ble.getProfile()
const keyValue = String(keyText || '').trim()
if (keyValue.length !== profile.auth.keyLength) {
throw new Error(`密钥长度必须为 ${profile.auth.keyLength}`)
}
const packets = buildA7Packets(keyValue, profile.packet)
for (const packet of packets) {
await ble.writeCharacteristic(key.uuid, packet)
}
return keyValue
}
async function writeKeyAndExpectStates(ble, keyText, expectedStateCodes, options = {}) {
const keyValue = await writePacketsToKeyCharacteristic(ble, keyText)
const stateEvent = await waitForStateCode(ble, options.timeout || 6000)
const stateCode = stateEvent.stateCode
if (!expectedStateCodes.includes(stateCode)) {
throw new Error(`密钥流程状态异常,期望=${expectedStateCodes.join('/')} 实际=${stateCode}`)
}
ble.setStage(BLE_STAGE.AUTHORIZED)
ble.setAuthState({
keyMatched: true,
lastStateCode: stateCode
})
return {
success: true,
stateCode,
key: keyValue
}
}
export function createAuthModule(ble) {
async function readDeviceInfo() {
const { msg } = ble.getState().characteristics
if (!msg || !msg.uuid) {
throw new Error('未发现 UUID_MSG 特征值')
}
const valuePromise = new Promise((resolve) => {
const off = ble.on('protocol:msg', (payload) => {
off()
resolve(payload)
})
})
await ble.readCharacteristic(msg.uuid)
const payload = await valuePromise
return parseDeviceInfo(payload.value)
}
/**
* 严格遵循协议1.2绑定流程:
* 1) 先读取 UUID_MSG 获取 bindStatus
* 2) bindStatus=0 -> 写随机密钥并等待 UUID_STATE=5(绑定成功)
* 3) bindStatus=1 -> 写已知密钥并等待 UUID_STATE=7(密钥匹配成功)。
*
* 兼容说明:当某些固件 MSG 未明确返回 0/1 时,回退为“已知密钥匹配优先”,
* 允许状态 7(匹配成功) 或 5(设备首次绑定成功)。
*/
async function ensureAuthorized(keyText, options = {}) {
ble.setStage(BLE_STAGE.AUTHORIZING)
const info = await readDeviceInfo()
const bindStatus = Number(info.bindStatus)
ble.setAuthState({
bound: Number.isNaN(bindStatus) ? null : bindStatus,
keyMatched: false
})
// 未绑定:按协议执行绑定。
if (bindStatus === 0) {
const keyToBind = String(keyText || '').trim() || generateRandomKey(ble.getProfile().auth.keyLength)
const authResult = await writeKeyAndExpectStates(ble, keyToBind, [5], options)
return {
mode: 'BIND',
deviceInfo: info,
authResult,
key: keyToBind
}
}
// 已绑定:按协议执行密钥匹配。
if (bindStatus === 1) {
const keyToMatch = String(keyText || '').trim()
if (!keyToMatch) {
throw new Error('设备已绑定,必须提供已绑定密钥用于匹配')
}
const authResult = await writeKeyAndExpectStates(ble, keyToMatch, [7], options)
return {
mode: 'MATCH',
deviceInfo: info,
authResult,
key: keyToMatch
}
}
// 绑定状态不明确时,执行兼容分支(优先匹配,允许绑定成功)。
const fallbackKey = String(keyText || '').trim()
if (!fallbackKey) {
throw new Error(`无法识别的绑定状态: ${info.bindStatus},且未提供密钥`)
}
const fallbackResult = await writeKeyAndExpectStates(ble, fallbackKey, [7, 5], options)
return {
mode: 'UNKNOWN_BIND_STATE_FALLBACK',
deviceInfo: info,
authResult: fallbackResult,
key: fallbackKey
}
}
return {
readDeviceInfo,
ensureAuthorized,
generateRandomKey
}
}