11

Im digging into Apple's Touch ID, more precisely the Local Authenticator. The documentation as of now is pretty sparse. Its mainly just this:

LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
NSString *myLocalizedReasonString = <#String explaining why app needs authentication#>;

if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics    error:&authError]) {
[myContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
              localizedReason:myLocalizedReasonString
                        reply:^(BOOL success, NSError *error) {
        if (success) {
            // User authenticated successfully, take appropriate action
        } else {
            // User did not authenticate successfully, look at error and take appropriate action
        }
    }];
} else {
// Could not evaluate policy; look at authError and present an appropriate message to user
}

as taken from https://developer.apple.com/documentation/localauthentication

The idea of using your fingerprint for authentication is nice. But I can add fingerprints in the device if I know the passcode. And its very easy to get the passcode, like you sit in the train next to ur victim and watch him/her enter the passcode.

I want to use the fingerprint as a way of secure authentication but want to be able to detect if new fingerprints were added since the last time I requested the fingerprint.

Apple is doing this for the AppStore. If you want to authenticate a transaction in the AppStore and have added a new Fingerprint since your last transaction, the AppStore requests your AppleId-Password. This is sane behaviour, because the phone might have been taken by someone else who knows the passcode and added his own fingerprint to buy something expensive.

My Question: Can I detect if a new fingerprint was added since the last time that I used Local Authenticator?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Stas Stelle
  • 195
  • 2
  • 8

6 Answers6

20

This is now possible in iOS9. The property evaluatedPolicyDomainState has been added to LAContext.

If the fingerprint database is modified (fingers were added or removed), the data returned by evaluatedPolicyDomainState will change. The nature of the changes cannot be determined but by comparing data of evaluatedPolicyDomainState after different evaluatePolicy calls you can detect that the set of fingerprints has been modified.

Note that this property is set only when evaluatePolicy is called and a succesful Touch ID authentication was performed, or when canEvaluatePolicy succeeds for a biometric policy.

Will
  • 151
  • 1
  • 4
Keith Coughtrey
  • 1,386
  • 13
  • 17
  • I integrated touch id in my app.I want to use the touch id as a second secure authentication. I want to detect if new fingerprints were added or removed. Could you help me with programmatically usage? – Begum Feb 25 '16 at 07:30
  • 1
    You would probably be better off using TouchID to unlock a token in the keychain rather than using local authentication. If you use the policy kSecAccessControlTouchIDCurrentSet, the token becomes inaccessible if the set of fingerprints is changed. See http://stackoverflow.com/questions/28108232/secitemcopymatching-for-touch-id-without-passcode-fallback/29910710#29910710 – Keith Coughtrey Feb 25 '16 at 20:42
11

As Keith stated, in iOS 9 it's possible. You should do it like this.

    let context = LAContext()
    context.canEvaluatePolicy(.DeviceOwnerAuthenticationWithBiometrics, error: nil)

    if let domainState = context.evaluatedPolicyDomainState
        where domainState == oldDomainState  {
        // Enrollment state the same

    } else {
        // Enrollment state changed

    }

Every time you add or delete a fingerprint, the domain state changes. You need to call canEvaluatePolicy for evaluatedPolicyDomainStateto be updated.

Cristian Pena
  • 2,139
  • 1
  • 19
  • 31
5

In short; no.

In a bit more detail; the LocalAuthentication framework is a tightly-guarded black box. The information you get back from it is very limited. Your interaction with it goes something like this:

  • Ask it if it's able to authenticate for some type of policy (there is only 1 available at time of writing - Biometrics (Touch ID))
  • If it can, ask it to actually do it
  • The system takes over for the actual authentication
  • It lets you know if the authentication was successful or not (if not, it tells you why)

You have no concept of the actual authentication process (which finger was used, for example). This, of course, is by design. Apple does not want, nor need, to give you access to such information.

JoeFryer
  • 2,751
  • 1
  • 18
  • 23
  • 1
    hope they open this interface a little bit more in near future. Its not much of help, if I dont know wheather someone screwed with touchID settings or not. – Stas Stelle Oct 23 '14 at 09:20
  • I see what you mean, but the idea is that you don't have to worry about the details of the authentication; you let the system handle it for you. – JoeFryer Oct 23 '14 at 09:33
  • 1
    its not well thought through, to add a new finger print, I need the same code that I enter every 10 minutes on the bus, on the train, on the sidewalk where people can see it over my shoulder. If someone intents to use touch id for more than testing, its easy to steal that device, enter a new fingerprint and misuse the newly granted authentication. This way its less secure than static short passwords. – Stas Stelle Oct 23 '14 at 13:22
  • But the idea is that you wouldn't enter your passcode most of the time to unlock your device, you'd use Touch ID instead, so people wouldn't be likely to see you entering it. – JoeFryer Oct 23 '14 at 13:25
  • it will depend on how people use it – Stas Stelle Oct 23 '14 at 13:37
3

This is the solution to verify if a fingerprint was added or removed, and the difference between the Swift and ObjC solution is that canEvaluatePolicy just verify if something changes, while evaluatePolicy opens the modal verification.

Swift 5.2

    let context = LAContext()
    context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)

    let defaults = UserDefaults.standard
    let oldDomainState = defaults.object(forKey: "domainTouchID") as? Data

    if let domainState = context.evaluatedPolicyDomainState, domainState == oldDomainState  {
        // Enrollment state the same
        print("nothing change")

    } else {
        // Enrollment state changed
        print("domain state was changed")
    }

    // save the domain state for the next time
    defaults.set(context.evaluatedPolicyDomainState, forKey: "domainTouchID")

Objective-C

    - (void)evaluatedPolicyDomainState {
    LAContext *context = [[LAContext alloc] init];
    __block  NSString *message;

    // show the authentication UI with reason string
    [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:@"Unlock access to locked feature" reply:^(BOOL success, NSError *authenticationError) {

        if (success) {
        
            // load the last domain state from touch id
            NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        
            NSData *oldDomainState = [defaults objectForKey:@"domainTouchID"];
        
            NSData *domainState = [context evaluatedPolicyDomainState];

            // check for domain state changes
            if ([oldDomainState isEqual:domainState]) {             
                message = @"nothing change";
            } else {
                message = @"domain state was changed";
            }
        
            // save the domain state that will be loaded next time
            oldDomainState = [context evaluatedPolicyDomainState];
            [defaults setObject:oldDomainState forKey:@"domainTouchID"];
            [defaults synchronize];
        
        } else {
            message = [NSString stringWithFormat:@"evaluatePolicy: %@", authenticationError.localizedDescription];
        }
    
        [self printMessage:message inTextView:self.textView];
    }];
}
Carlos Irano
  • 682
  • 7
  • 8
3

I would recommend to store the evaluatedPolicyDomainState value into keychain instead of storing it in NSUserDefault.

You can convert the data value of evaluatedPolicyDomainState into string, which is a 44 character string. Below is the code to convert the evaluatedPolicyDomainState data value into string -

if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) {
    if let domainState = context.evaluatedPolicyDomainState {
        let bData = domainState.base64EncodedData()
        if let decodedString = String(data: bData, encoding: .utf8) {
            print("Decoded Value: \(decodedString)")
        }
    }
}

Now if the device owner made any change in Touch ID like adding a new finger Id; then this data value will be changed and you can take necessary steps to handle the change based on your project needs.

A K M Saleh Sultan
  • 2,403
  • 2
  • 22
  • 28
0

I would like to add something,

-(BOOL)hasFingerPrintChanged
{
   BOOL changed = NO;

   LAContext *context = [[LAContext alloc] init];
   [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:nil];

   NSData *domainState = [context evaluatedPolicyDomainState];

   // load the last domain state from touch id
   NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
   NSData *oldDomainState = [defaults objectForKey:@"domainTouchID"];

        if (oldDomainState)
        {
            // check for domain state changes

            if ([oldDomainState isEqual:domainState])
            {
                NSLog(@"nothing changed.");
            }
            else
            {
                changed = YES;
                NSLog(@"domain state was changed!");

                NSString *message = @"Your Touch ID is invalidated, because you have added or removed finger(s).";

            }

        }

        // save the domain state that will be loaded next time
    [defaults setObject:domainState forKey:@"domainTouchID"];
    [defaults synchronize];


        return changed;
    }

It's better to store user password etc in keychain. I'm using https://github.com/reidmain/FDKeychain

Muhammad Waqas
  • 904
  • 2
  • 10
  • 21