Implement bank transfer app flows
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
import CommonCrypto
|
||||
import Foundation
|
||||
|
||||
enum PinHash {
|
||||
private static let iterations: UInt32 = 100_000
|
||||
private static let keyLength = 32 // 256 bits
|
||||
|
||||
static func generateSalt() -> Data {
|
||||
var salt = Data(count: 16)
|
||||
_ = salt.withUnsafeMutableBytes { SecRandomCopyBytes(kSecRandomDefault, 16, $0.baseAddress!) }
|
||||
return salt
|
||||
}
|
||||
|
||||
static func hash(_ input: String, salt: Data) -> String {
|
||||
var derivedKey = Data(count: keyLength)
|
||||
let inputData = Data(input.utf8)
|
||||
|
||||
_ = derivedKey.withUnsafeMutableBytes { derivedPtr in
|
||||
inputData.withUnsafeBytes { inputPtr in
|
||||
salt.withUnsafeBytes { saltPtr in
|
||||
CCKeyDerivationPBKDF(
|
||||
CCPBKDFAlgorithm(kCCPBKDF2),
|
||||
inputPtr.baseAddress, inputData.count,
|
||||
saltPtr.baseAddress, salt.count,
|
||||
CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA256),
|
||||
iterations,
|
||||
derivedPtr.baseAddress, keyLength
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return derivedKey.base64EncodedString()
|
||||
}
|
||||
|
||||
static func verify(_ input: String, against storedHash: String, salt: Data) -> Bool {
|
||||
hash(input, salt: salt) == storedHash
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import CryptoKit
|
||||
import Foundation
|
||||
|
||||
enum Totp {
|
||||
// timeStep offsets the counter by N periods — use -1/+1 to handle clock skew at window boundaries.
|
||||
static func generate(_ base32Secret: String, digits: Int = 6, period: Int = 30, timeStep: Int = 0) -> String {
|
||||
let counter = Int64(Date().timeIntervalSince1970) / Int64(period) + Int64(timeStep)
|
||||
guard let keyBytes = base32Decode(base32Secret.uppercased()) else { return String(repeating: "0", count: digits) }
|
||||
let otp = hotp(key: keyBytes, counter: counter, digits: digits)
|
||||
return String(format: "%0\(digits)d", otp)
|
||||
}
|
||||
|
||||
private static func hotp(key: [UInt8], counter: Int64, digits: Int) -> Int {
|
||||
var counterBigEndian = counter.bigEndian
|
||||
let counterData = withUnsafeBytes(of: &counterBigEndian) { Data($0) }
|
||||
|
||||
let hmac = HMAC<Insecure.SHA1>.authenticationCode(
|
||||
for: counterData,
|
||||
using: SymmetricKey(data: Data(key))
|
||||
)
|
||||
let hash = Array(hmac)
|
||||
|
||||
let offset = Int(hash[hash.count - 1] & 0x0F)
|
||||
let truncated = (Int(hash[offset]) & 0x7F) << 24
|
||||
| Int(hash[offset + 1]) << 16
|
||||
| Int(hash[offset + 2]) << 8
|
||||
| Int(hash[offset + 3])
|
||||
|
||||
let modulus = Int(pow(10.0, Double(digits)))
|
||||
return truncated % modulus
|
||||
}
|
||||
|
||||
private static func base32Decode(_ input: String) -> [UInt8]? {
|
||||
let alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
|
||||
var bits = 0
|
||||
var bitCount = 0
|
||||
var result: [UInt8] = []
|
||||
|
||||
for char in input where char != "=" {
|
||||
guard let idx = alphabet.firstIndex(of: char) else { return nil }
|
||||
let value = alphabet.distance(from: alphabet.startIndex, to: idx)
|
||||
bits = (bits << 5) | value
|
||||
bitCount += 5
|
||||
if bitCount >= 8 {
|
||||
bitCount -= 8
|
||||
result.append(UInt8((bits >> bitCount) & 0xFF))
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user