跳到主要内容

iOS 设备认证 SDK

SDK Version: 1.0.0
Product: XTalkDeviceAuthSdk

认证产品不依赖 CoreBluetooth。客户通过 DeviceAuthTransport 把 BLE、UART 或其他串行 AT 通道接入认证器。

1. Transport 必须满足的契约​

  • label 是客户可读的传输名称,用于区分通道。
  • atEncoding 必须反映当前真实编码。
  • setAtEncoding 必须同时修改底层通道编码。
  • runAtCommand 必须串行化请求,并在返回错误前恢复干净的命令边界。
  • .collectFullWindow 与 .recoverableEncodingProbe 不得因普通 OK 或早到错误提前结束。
  • Task 取消、断链和超时必须能映射成 transport error;Auth 会转换成公开认证错误。

BLE adapter​

import XTalkBleSdk
import XTalkDeviceAuthSdk

@MainActor
final class XTalkBleAuthTransport: DeviceAuthTransport {
let label = "ble"
private(set) var atEncoding: DeviceAuthAtEncoding = .plain
private let ble: XTalkBleClient

init(ble: XTalkBleClient) {
self.ble = ble
}

func setAtEncoding(_ encoding: DeviceAuthAtEncoding) {
atEncoding = encoding
switch encoding {
case .plain: ble.setAtEncoding(.plain)
case .xor: ble.setAtEncoding(.xor)
}
}

func runAtCommand(
_ command: String,
timeoutMs: Int,
log: Bool,
responseMode: DeviceAuthResponseMode
) async throws -> [String] {
let bleMode: XTalkAtResponseMode
switch responseMode {
case .automatic: bleMode = .automatic
case .collectFullWindow, .recoverableEncodingProbe:
bleMode = .collectUntilTimeout
}
return try await ble.runAtCommand(
command,
timeoutMs: timeoutMs,
log: log,
responseMode: bleMode
)
}
}

BLE adapter 直接复用 XTalkBleClient 的事务隔离。UART 等自定义 transport 也必须保证迟到响应不会进入下一条事务,否则编码探测或 Challenge 重试可能读取到上一条命令的数据。

执行认证​

try await model.waitUntilAtReady()

let transport = XTalkBleAuthTransport(ble: model.ble)
let authenticator = XTalkDeviceAuthenticator(transport: transport)

do {
let result = try await authenticator.authenticate()
let did = result.identity.did
let selectedEncoding = result.encoding
let challenged = result.didRequireChallenge
onAuthenticated(did, selectedEncoding, challenged)
} catch let error as XTalkDeviceAuthError {
handleAuthenticationError(error)
}

transport 和 authenticator 应与 BLE client 一样被 session 强引用。不要每次点击认证都新建 transport 后立即释放。

默认配置为 probe 800 ms、strict probe 1200 ms、Challenge 4000 ms、DID 4000 ms、最多 3 次 Challenge。所有值必须大于 0。

同一认证器同时只允许一个会话;并发调用返回 .busy。页面离开、断链或切换设备时,取消等待认证的 Task,并调用 authenticator.cancel()。

认证结果中的 encoding 应继续用于后续 AT 操作。只有成功返回的 identity 才能进入客户业务状态。

3. 状态与结果​

认证状态依次可能为 .probing、.challenging(attempt:maxAttempts:)、.readingIdentity,最后进入 .authenticated(identity)、.failed(error) 或 .cancelled。客户可以通过 $state 驱动 UI。

authenticator.$state
.receive(on: RunLoop.main)
.sink { state in
// 更新进度、错误提示或认证成功界面。
}
.store(in: &cancellables)

成功结果:

  • identity.deviceID:规范化后的 32 位设备标识。
  • identity.did:推荐存储和展示的十六进制 DID 字符串。
  • identity.serial:规范化序列号文本。
  • identity.source:来自 EFUSE SN 或普通 SN 回退。
  • encoding:认证成功时选中的 .plain 或 .xor。
  • didRequireChallenge:本次是否执行 Challenge。

4. 取消、重试和安全边界​

private var authTask: Task<Void, Never>?

func beginAuthentication() {
authTask?.cancel()
authTask = Task { @MainActor in
do {
let result = try await authenticator.authenticate()
guard !Task.isCancelled else { return }
saveAuthenticatedIdentity(result.identity)
} catch is CancellationError {
// 正常页面退出。
} catch {
presentAuthenticationFailure(error)
}
}
}

func stopAuthentication() {
authTask?.cancel()
authenticator.cancel()
authTask = nil
}

认证失败默认 fail closed:不得沿用旧 DID 进入当前设备业务。重试前先确认同一设备仍连接且 atReady == true;通道状态不确定时先断开重连。

5. DID 工具​

XTalkDeviceIDCodec 用于客户已有持久化数据的规范化、解析和格式化:

let parsed: UInt32? = XTalkDeviceIDCodec.parse("0x12345678")
if let parsed {
let canonical = XTalkDeviceIDCodec.canonicalize(parsed)
let text = XTalkDeviceIDCodec.format(canonical)
print(text)
}

业务通常直接使用认证结果中的 identity.did,不需要自行从原始设备响应拼 DID。

查看 API 接口参考