Files
GLEGram-iOS/GLEGram/DoubleBottom/Sources/DoubleBottomPasscodeStore.swift
Leeksov 4647310322 GLEGram 12.5 — Initial public release
Based on Swiftgram 12.5 (Telegram iOS 12.5).
All GLEGram features ported and organized in GLEGram/ folder.

Features: Ghost Mode, Saved Deleted Messages, Content Protection Bypass,
Font Replacement, Fake Profile, Chat Export, Plugin System, and more.

See CHANGELOG_12.5.md for full details.
2026-04-06 09:48:12 +03:00

117 lines
4.4 KiB
Swift

// MARK: Swiftgram - Keychain storage for hidden-account passcodes (Double Bottom)
import Foundation
import Security
private let serviceName = "SwiftgramDoubleBottom"
/// Key for the single "secret" passcode (second password). When user unlocks with this, only one account is shown.
private let secretPasscodeAccountKey = "secret"
public enum DoubleBottomPasscodeStore {
// MARK: - Secret passcode (second password -> show only 1 account)
public static func setSecretPasscode(_ passcode: String) {
let data = passcode.data(using: .utf8)!
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: serviceName,
kSecAttrAccount as String: secretPasscodeAccountKey
]
var addQuery = query
addQuery[kSecValueData as String] = data
var status = SecItemAdd(addQuery as CFDictionary, nil)
if status == errSecDuplicateItem {
SecItemDelete(query as CFDictionary)
status = SecItemAdd(addQuery as CFDictionary, nil)
}
}
public static func secretPasscodeMatches(_ passcode: String) -> Bool {
guard let stored = secretPasscode() else { return false }
return stored == passcode
}
public static func hasSecretPasscode() -> Bool {
return secretPasscode() != nil
}
public static func removeSecretPasscode() {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: serviceName,
kSecAttrAccount as String: secretPasscodeAccountKey
]
SecItemDelete(query as CFDictionary)
}
private static func secretPasscode() -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: serviceName,
kSecAttrAccount as String: secretPasscodeAccountKey,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
guard status == errSecSuccess, let data = result as? Data, let string = String(data: data, encoding: .utf8) else {
return nil
}
return string
}
// MARK: - Per-account passcodes (hidden accounts)
public static func setPasscode(_ passcode: String, forAccountId accountId: Int64) {
let account = "\(accountId)"
let data = passcode.data(using: .utf8)!
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: serviceName,
kSecAttrAccount as String: account
]
var addQuery = query
addQuery[kSecValueData as String] = data
var status = SecItemAdd(addQuery as CFDictionary, nil)
if status == errSecDuplicateItem {
SecItemDelete(query as CFDictionary)
status = SecItemAdd(addQuery as CFDictionary, nil)
}
}
public static func passcode(forAccountId accountId: Int64) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: serviceName,
kSecAttrAccount as String: "\(accountId)",
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
guard status == errSecSuccess, let data = result as? Data, let string = String(data: data, encoding: .utf8) else {
return nil
}
return string
}
public static func removePasscode(forAccountId accountId: Int64) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: serviceName,
kSecAttrAccount as String: "\(accountId)"
]
SecItemDelete(query as CFDictionary)
}
/// Returns the account id whose passcode matches the given value, or nil.
public static func accountId(matchingPasscode passcode: String, candidateIds: [Int64]) -> Int64? {
for id in candidateIds {
if Self.passcode(forAccountId: id) == passcode {
return id
}
}
return nil
}
}