Initial commit.
This commit is contained in:
129
src/LicenseManager.swift
Normal file
129
src/LicenseManager.swift
Normal file
@@ -0,0 +1,129 @@
|
||||
//
|
||||
// LicenseManager.swift
|
||||
// CmdBar
|
||||
//
|
||||
// Created by Igor Kolokolnikov on 8/26/23.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import CryptoKit
|
||||
|
||||
fileprivate struct LicenseModel: Codable {
|
||||
let valid: Bool
|
||||
let error: String
|
||||
let email: String
|
||||
let key: String
|
||||
}
|
||||
|
||||
// Converts string to a hash.
|
||||
fileprivate func hashString(_ string: String) -> String {
|
||||
return SHA256.hash(data: Data(string.utf8)).compactMap { String(format: "%02x", $0) }.joined()
|
||||
}
|
||||
|
||||
// Converts passphrase to a SymmetricKey.
|
||||
fileprivate func passToKey(from passphrase: String) -> SymmetricKey {
|
||||
let passphraseData = Data(passphrase.utf8)
|
||||
let keyData = SHA256.hash(data: passphraseData).withUnsafeBytes { buffer in
|
||||
return Data(buffer)
|
||||
}
|
||||
return SymmetricKey(data: keyData)
|
||||
}
|
||||
|
||||
final class LicenseManager {
|
||||
static let standard = LicenseManager()
|
||||
|
||||
private let passkey = passToKey(from: "B2B67FB0E22B814CE5A6E7DA729A068BF21C9490919EA0B42FCEB322F9C9BA82")
|
||||
private let defaults = UserDefaults.standard
|
||||
private var timer: DispatchSourceTimer?
|
||||
private var quitAfterMinutes = 20.0 * 60.0 // quit after 20 minutes
|
||||
|
||||
func isRegistered() -> Bool {
|
||||
guard let email = defaults.object(forKey: "Email") as? String,
|
||||
let key = defaults.object(forKey: "Key") as? String,
|
||||
let licenseData = defaults.object(forKey: "License") as? Data,
|
||||
let unsealedBox = try? AES.GCM.SealedBox(combined: licenseData),
|
||||
let licenseHashData = try? AES.GCM.open(unsealedBox, using: passkey),
|
||||
!email.isEmpty,
|
||||
!key.isEmpty
|
||||
else { return false }
|
||||
|
||||
let currentHashData = Data(SHA256.hash(data: Data(hashString("\(email)\(key)\(NSUserName())").utf8))
|
||||
.compactMap { String(format: "%02x", $0) }.joined().utf8)
|
||||
|
||||
return licenseHashData == currentHashData
|
||||
}
|
||||
|
||||
func activateLicense(email: String, key: String, completionHandler: @escaping (_ data: Resulting<Bool, String>) -> Void) {
|
||||
guard let url = URL(string: EndpointConstants.validateURL)?.appending(component: key)
|
||||
else { completionHandler(.failure); return }
|
||||
|
||||
URLSession.shared.dataTask(with: url) { [weak self] (data, response, error) in
|
||||
guard
|
||||
let data = data,
|
||||
error == nil,
|
||||
let response = response as? HTTPURLResponse,
|
||||
(200...299).contains(response.statusCode)
|
||||
else { completionHandler(.failure); return }
|
||||
|
||||
guard let jsonResponse = try? JSONDecoder().decode(LicenseModel.self, from: data)
|
||||
else { completionHandler(.failure); return }
|
||||
|
||||
if jsonResponse.email == email && jsonResponse.key == key {
|
||||
self?.saveLicense(with: email, and: key)
|
||||
self?.stopTimer()
|
||||
completionHandler(.success)
|
||||
} else {
|
||||
completionHandler(.failure)
|
||||
}
|
||||
}.resume()
|
||||
}
|
||||
|
||||
func deactivateLicense() {
|
||||
clearLicense()
|
||||
startTimer()
|
||||
}
|
||||
|
||||
func getLicense() -> (email: String, key: String) {
|
||||
guard
|
||||
let email = defaults.object(forKey: "Email") as? String,
|
||||
let key = defaults.object(forKey: "Key") as? String
|
||||
else { return ("", "") }
|
||||
return (email, key)
|
||||
}
|
||||
|
||||
private func saveLicense(with email: String, and key: String) {
|
||||
let defaults = UserDefaults.standard
|
||||
|
||||
let licenseHashData = Data(SHA256.hash(data: Data(hashString("\(email)\(key)\(NSUserName())").utf8))
|
||||
.compactMap { String(format: "%02x", $0) }.joined().utf8)
|
||||
|
||||
guard let sealedBoxData = try? AES.GCM.seal(licenseHashData, using: passkey).combined else { return }
|
||||
defaults.set(email, forKey: "Email")
|
||||
defaults.set(key, forKey: "Key")
|
||||
defaults.set(sealedBoxData, forKey: "License")
|
||||
}
|
||||
|
||||
func clearLicense() {
|
||||
defaults.set(nil, forKey: "Email")
|
||||
defaults.set(nil, forKey: "Key")
|
||||
defaults.set(nil, forKey: "License")
|
||||
}
|
||||
|
||||
// MARK: - Timer
|
||||
func startTimer() {
|
||||
timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global())
|
||||
timer?.schedule(
|
||||
deadline: .now() + quitAfterMinutes,
|
||||
repeating: DispatchTimeInterval.never
|
||||
)
|
||||
timer?.setEventHandler {
|
||||
DispatchQueue.main.async { NSApplication.shared.terminate(self) }
|
||||
}
|
||||
timer?.resume()
|
||||
}
|
||||
|
||||
func stopTimer() {
|
||||
timer?.cancel()
|
||||
timer = nil
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user