> 属于 seed/熵审计任务 ArcBlock/arc-wallet-ios#4647 的分项。 ## 问题 `ArcBlockSDK/ABSDKCoreKit/BIP44Utils.swift:60-64`: ```swift private static func getEntropy(secretCode: String, recoveryCode: String) -> Data? { var entropy = (secretCode.sha3(.keccak256).uppercased() + recoveryCode).sha3(.keccak256).uppercased() entropy = String(entropy.prefix(32)) return entropy.data(using: .ascii) } ``` CryptoSwift 的 `String.sha3(_:)` 返回的是**十六进制字符串**,不是 bytes。所以: 1. 内层 `.sha3()` → 64 个 hex 字符 2. 拼上 `recoveryCode`,外层 `.sha3()` → 又是 64 个 hex 字符 3. `.prefix(32)` → 32 个 hex 字符 4. `.data(using: .ascii)` → **32 字节,但每字节取值只在 `[0-9A-F]` 这 16 个值里** 即:一个 32 字节的熵缓冲区,实际只承载 **32 × 4 = 128 bit**。 然后 `generateSeed(secretCode:recoveryCode:)` 把它喂给 `BIP39.seedFromEntropy(entropy)` —— 32 字节熵在 BIP39 里对应 **24 词助记词**。 ## 影响 **不可利用。** `recoveryCode` 来自 `generateRecoveryCode()` → `MCrypto.generateRandomBytes(bytesCount: 16)` → `SecRandomCopyBytes`,是 128 bit 真随机;128 bit 是安全的。 但这是**标称强度 ≠ 实际强度**:用户拿到一条 24 词助记词,按 BIP39 的常识那代表 256 bit,实际只有 128 bit。这正是 Coldcard 那篇复盘在讲的缺陷类别 —— 名义熵和有效熵对不上,只是我们这边差 2×、他们差 2^88。 任何在这条路上创建过钱包的用户,手里的 24 词其实值 128 bit。 ## 当前可达性 `BIP44Utils.generateSeed(secretCode:recoveryCode:)` 在 arc-wallet-ios 里只有一个调用方: - `WalletInitManager.codeRecovery(secretCode:recoveryCode:password:)`(`WalletInitManager.swift:208`) - 被 `ImportBackupPasswordViewController.swift:232` 调用 —— **恢复/导入流程,不是创建流程** 也就是说这条路今天只用于恢复历史钱包,新钱包走的是 `generateMnemonics()` → `BIP39.generateMnemonics(bitsOfEntropy: 128)`(`SecRandomCopyBytes`,正确)。 ## 建议 1. `getEntropy` 保留原样 —— 它是历史钱包的恢复算法,**不能改**,改了老用户恢复不出来。 2. 但要加注释说明"输出是 32 字节 ASCII-hex,实际熵 128 bit",避免后来人误以为这是 256 bit 派生,或把它复用到新路径上。 3. 确认没有任何创建(而非恢复)路径还在调用 `generateSeed(secretCode:recoveryCode:)`。 4. Android 侧有逐字对应的实现(`Bip44Utils.getEntropy`,用 `BaseEncoding.base16()` 做同样的事),已在 ArcBlock/did-wallet-sdk-android 另开 issue。
Activity