montana/Монтана-iOS/Sources/MontanaApp/Identity/Wordlist.swift

45 lines
1.8 KiB
Swift
Raw Normal View History

import Foundation
import CryptoKit
/// spec, раздел "Мнемоника и seed Каноническая wordlist".
/// 2048 lowercase ASCII слов, sorted lexicographically.
/// Binding fingerprint: SHA-256(canonical_bytes) =
/// 2f5eed53a4727b4bf8880d8f3f199efc90e58503646d9ff8eff3a2ed3b24dbda
enum Wordlist {
static let canonical: [String] = {
guard let url = Bundle.main.url(forResource: "wordlist", withExtension: "txt"),
let raw = try? String(contentsOf: url, encoding: .ascii)
else {
fatalError("Resources/wordlist.txt отсутствует или повреждён")
}
let words = raw.split(separator: "\n").map { String($0) }
precondition(words.count == 2048, "wordlist должен содержать ровно 2048 слов, получено \(words.count)")
return words
}()
/// Binary search index слова в canonical wordlist.
static func index(of word: String) -> UInt16? {
var lo = 0
var hi = canonical.count
while lo < hi {
let mid = (lo + hi) / 2
if canonical[mid] < word {
lo = mid + 1
} else if canonical[mid] > word {
hi = mid
} else {
return UInt16(mid)
}
}
return nil
}
/// Verify binding fingerprint при старте приложения (spec обязательное правило).
static func verifyBindingFingerprint() -> Bool {
let canonicalBytes = canonical.map { $0 + "\n" }.joined().data(using: .ascii)!
let hash = SHA256.hash(data: canonicalBytes)
let hex = hash.map { String(format: "%02x", $0) }.joined()
return hex == "2f5eed53a4727b4bf8880d8f3f199efc90e58503646d9ff8eff3a2ed3b24dbda"
}
}