forked from johnthethird/keymaster
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkeymaster.swift
122 lines (107 loc) · 3.43 KB
/
keymaster.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// Keymaster, access Keychain secrets guarded by TouchID
//
import Foundation
import LocalAuthentication
let policy = LAPolicy.deviceOwnerAuthenticationWithBiometrics
func setPassword(key: String, password: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: key,
kSecValueData as String: password
]
let status = SecItemAdd(query as CFDictionary, nil)
return status == errSecSuccess
}
func deletePassword(key: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: key,
kSecMatchLimit as String: kSecMatchLimitOne
]
let status = SecItemDelete(query as CFDictionary)
return status == errSecSuccess
}
func getPassword(key: String) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: key,
kSecMatchLimit as String: kSecMatchLimitOne,
kSecReturnData as String: true
]
var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
guard status == errSecSuccess,
let passwordData = item as? Data,
let password = String(data: passwordData, encoding: String.Encoding.utf8)
else { return nil }
return password
}
func usage() {
print("keymaster [get|set|delete] [key] [secret]")
}
func main() {
let inputArgs: [String] = Array(CommandLine.arguments.dropFirst())
if (inputArgs.count < 2 || inputArgs.count > 3) {
usage()
exit(EXIT_FAILURE)
}
let action = inputArgs[0]
let key = inputArgs[1]
var secret = ""
if (action == "set" && inputArgs.count == 3) {
secret = inputArgs[2]
}
let context = LAContext()
context.touchIDAuthenticationAllowableReuseDuration = 0
var error: NSError?
guard context.canEvaluatePolicy(policy, error: &error) else {
print("This Mac doesn't support deviceOwnerAuthenticationWithBiometrics")
exit(EXIT_FAILURE)
}
if (action == "set") {
context.evaluatePolicy(policy, localizedReason: "set to your password") { success, error in
guard setPassword(key: key, password: secret) else {
print("Error setting password")
exit(EXIT_FAILURE)
}
print("Key \(key) has been sucessfully set in the keychain")
exit(EXIT_SUCCESS)
}
dispatchMain()
}
if (action == "get") {
context.evaluatePolicy(policy, localizedReason: "access to your password") { success, error in
if success && error == nil {
guard let password = getPassword(key: key) else {
print("Error getting password")
exit(EXIT_FAILURE)
}
print(password)
exit(EXIT_SUCCESS)
} else {
let errorDescription = error?.localizedDescription ?? "Unknown error"
print("Error \(errorDescription)")
exit(EXIT_FAILURE)
}
}
dispatchMain()
}
if (action == "delete") {
context.evaluatePolicy(policy, localizedReason: "delete your password") { success, error in
if success && error == nil {
guard deletePassword(key: key) else {
print("Error deleting password")
exit(EXIT_FAILURE)
}
print("Key \(key) has been sucessfully deleted from the keychain")
exit(EXIT_SUCCESS)
} else {
let errorDescription = error?.localizedDescription ?? "Unknown error"
print("Error \(errorDescription)")
exit(EXIT_FAILURE)
}
}
dispatchMain()
}
}
main()