-
Notifications
You must be signed in to change notification settings - Fork 0
/
TouchID.m
executable file
·125 lines (100 loc) · 6.37 KB
/
TouchID.m
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
123
124
125
#import "TouchID.h"
#import <LocalAuthentication/LocalAuthentication.h>
@implementation TouchID
- (void) verifyFingerprint:(CDVInvokedUrlCommand*)command {
NSString *message = [command.arguments objectAtIndex:0];
NSString *passcodeLabel = [command.arguments objectAtIndex:2];
BOOL fingerPrintOnly = [[command.arguments objectAtIndex:1] boolValue];
NSString *callbackId = command.callbackId;
NSBundle *bundle = [NSBundle mainBundle];
NSDictionary *info = [bundle infoDictionary];
if(fingerPrintOnly) {
LAContext *context = [[LAContext alloc] init];
context.localizedFallbackTitle = @"";
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:message reply:^(BOOL success, NSError *authenticationError){
if (success) {
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK] callbackId:command.callbackId];
}
else {
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:authenticationError.localizedDescription] callbackId:callbackId];
NSLog(@"Fingerprint validation failed: %@.", authenticationError.localizedDescription);
}
}];
} else {
// this replaces the default 'Enter password' button label
if ([passcodeLabel length] != 0) {
LAContext *context = [[LAContext alloc] init];
context.localizedFallbackTitle = passcodeLabel;
}
NSString * keychainItemIdentifier = [info objectForKey:@"CFBundleDisplayName"];
NSString * keychainItemServiceName = [info objectForKey:@"CFBundleIdentifier"];
// The content of the password is not important.
NSData * pwData = [@"pass" dataUsingEncoding:NSUTF8StringEncoding];
// Create the keychain entry attributes.
NSMutableDictionary * attributes = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
(__bridge id)(kSecClassGenericPassword), kSecClass,
keychainItemIdentifier, kSecAttrAccount,
keychainItemServiceName, kSecAttrService, nil];
CFErrorRef accessControlError = NULL;
SecAccessControlRef accessControlRef = SecAccessControlCreateWithFlags(
kCFAllocatorDefault,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
kSecAccessControlUserPresence,
&accessControlError);
if (accessControlRef == NULL || accessControlError != NULL)
{
NSLog(@"Cannot create SecAccessControlRef to store a password with identifier “%@” in the key chain: %@.", keychainItemIdentifier, accessControlError);
return;
}
attributes[(__bridge id)kSecAttrAccessControl] = (__bridge id)accessControlRef;
// In case this code is executed again and the keychain item already exists we want an error code instead of a fingerprint scan.
attributes[(__bridge id)kSecUseNoAuthenticationUI] = @YES;
attributes[(__bridge id)kSecValueData] = pwData;
CFTypeRef result;
OSStatus osStatus = SecItemAdd((__bridge CFDictionaryRef)attributes, &result);
if (osStatus != noErr)
{
//NSError * error = [[NSError alloc] initWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil];
//NSLog(@"Adding generic password with identifier “%@” to keychain failed with OSError %d: %@.", keychainItemIdentifier, (int)osStatus, error);
}
// Determine a string which the device will display in the fingerprint view explaining the reason for the fingerprint scan.
// The keychain operation shall be performed by the global queue. Otherwise it might just nothing happen.
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
// Create the keychain query attributes using the values from the first part of the code.
NSMutableDictionary * query = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
(__bridge id)(kSecClassGenericPassword), kSecClass, //kSecClassGenericPassword
keychainItemIdentifier, kSecAttrAccount,
keychainItemServiceName, kSecAttrService,
message, kSecUseOperationPrompt,
nil];
// Start the query and the fingerprint scan and/or device passcode validation
CFTypeRef result = nil;
OSStatus userPresenceStatus = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
if (noErr == userPresenceStatus)
{
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK] callbackId:command.callbackId];
}
else
{
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:userPresenceStatus] callbackId:callbackId];
}
});
}
}
- (void) isAvailable:(CDVInvokedUrlCommand*)command; {
if (NSClassFromString(@"LAContext") == NULL) {
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR] callbackId:command.callbackId];
return;
}
NSError *error = nil;
LAContext *laContext = [[LAContext alloc] init];
if ([laContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK]
callbackId:command.callbackId];
} else {
NSArray *errorKeys = @[@"code", @"localizedDescription"];
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[error dictionaryWithValuesForKeys:errorKeys]]
callbackId:command.callbackId];
}
}
@end