Touch ID

The iPhone 5s and newer, iPad Air 2 and newer, and iPad mini 3 and newer comes equipped with Touch ID, a fingerprint scanner. Currently, there is no official developer API for it, because it is intended for unlocking the device and purchasing items on iTunes Store only. According to this article the sensor is bound to each device uniquely. This means that Touch ID sensors seem to be tied to specific devices somehow similar to HDMI protected media path.

However there is a private API for it; its dylib file is in Xcode 5 in the path Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.0.sdk/System/Library/PrivateFrameworks/BiometricKit.framework/BiometricKit

As of iOS 8, the dylib has been removed from the iOS SDK, and has been replaced by a stub (containing symbols, but no code). The dylib can still be obtained easily from the dyld_shared_cache on the device. Code is ARM64, but can be disassembled by newer versions of IDA (6.4) or NewOSXBook.com's jtool.

Fingerprint Registration Process
Apple has applied for belows process to be patented for TouchID in Apple Patent Application 20130308838.


 * The fingerprint sensor detects an object to scan (activated via the 'metal ring' around the home button).
 * The fingerprint sensor starts the scan - basically it takes a picture of the finger (UIImage).
 * The picture is transferred to the Secure Enclave Processor (SEP) over an an encrypted dataline (similar to HDMI protected media path).
 * The SEP stores this picture as a so-called template. Then it constructs a lower resolution version: a histogram of the most common ridge angles storing it together with the higer resolution template in the Secure Enclave.
 * The SEP sends the lower resolution version to the main CPU.
 * The main CPU stores the lower resultion version in a database (for a later authentication).

Fingerprint Authentication Process

 * The fingerprint sensor detects an object to scan (activated via the 'metal ring' around the home button).
 * The fingerprint sensor starts the scan - basically it takes a picture of the finger (UIImage).
 * The picture is transferred to the Secure Enclave Processor (SEP) over an an encrypted dataline (similar to HDMI protected media path).
 * The SEP constructs a lower resolution version: a histogram of the most common ridge angles.
 * The SEP sends the lower resolution version to the main CPU.
 * The main compares the the lower resolution version for possible matches in its database.
 * The main sends possible matches back to SEP or the authentication is rejected if no matches are found.
 * The SEP takes the matches received by the main CPU and compares the initial image to high resolution versions of the received matches from main CPU.
 * Access is granted in case of positive comparison or rejected in case of negative comparison.

Inferred Information
Based on a string dump, here is what is implied.


 * It's codename is "mesa"
 * It communicates over XPC to a binary that handles access to it
 * There are kernel extensions to interface with it
 * The kernel extension communicates to the secure keystore to set and verify fingerprints
 * The fingerprint scanner calibrates itself and has upgradable firmware
 * The fingerprint scanner uses normal image formats (i.e. UIImage) before setting and verifying fingerprints
 * There's biometric lockout as well as passcode lockout
 * The A7 chip contains a secure element marketed as the Secure Enclave. The string dump refers to SEP, the Secure Element Protocol. This chip is most likely one sourced from NXP. It contains physical security to ensure that the only operations of the chip involve setting new fingerprints and verifying fingerprints against the ones stored in it (i.e. challenge-response). This way, the fingerprint data cannot be extracted from it.

String Dump
Below there is a full string dump of the framework, which can hint at its functionalities. initWithMachServiceName:options: connectWithReplyBlock: registerDelegate:withReplyBlock: suspendWork:withReplyBlock: enroll:withAuthToken:withReplyBlock: match:withReplyBlock: match:withOptions:withReplyBlock: matchIdentities:withReplyBlock: cancelWithReplyBlock: updateIdentity:withReplyBlock: removeIdentity:withReplyBlock: getIdentityFromUUID:withReplyBlock: identities:withReplyBlock: resetEngineWithReplyBlock: registerDSID:withAuthToken:withReplyBlock: registerStoreToken:withReplyBlock: getCountersignedStoreTokenWithReplyBlock: getMaxIdentityCount:withReplyBlock: enrollContinueWithReplyBlock: pullAlignmentDataWithReplyBlock: pullMatchTopologyDataWithReplyBlock: getNodeTopologyForIdentity:withReplyBlock: preventAutonomousMatchingMode:withReplyBlock: getProvisioningStateWithReplyBlock: getCalBlobVersionWithReplyBlock: getSensorCalibrationStatusWithReplyBlock: getCalibrationDataStateWithReplyBlock: setDebugImages:withReplyBlock: pullCaptureBufferWithReplyBlock: pullDebugImageData:withReplyBlock: provisionSensorWithReplyBlock: unpairSensorWithReplyBlock: lockSensorWithReplyBlock: getSerialisedTemplateForIdentity:withReplyBlock: interfaceWithProtocol: setRemoteObjectInterface: remoteObjectInterface setWithObject: setClasses:forSelector:argumentIndex:ofReply: setWithObjects: enrollResult: matchResult: statusMessage: homeButtonPressed setExportedInterface: setExportedObject: setInterruptionHandler: resume invalidate remoteObjectProxyWithErrorHandler: code respondsToSelector: connect registerDelegate: suspendWork: enroll:withAuthToken: match: match:withOptions: matchIdentities: cancel updateIdentity: removeIdentity: getIdentityFromUUID: identities: getMaxIdentityCount: resetEngine enrollContinue pullAlignmentData pullMatchTopologyData getNodeTopologyForIdentity: preventAutonomousMatchingMode: getProvisioningState registerDSID:withAuthToken: registerStoreToken: getCountersignedStoreToken: getCalBlobVersion getSensorCalibrationStatus getCalibrationDataState pullCaptureBuffer pullDebugImageData:imageWidth:imageHeight: provisionSensor unpairSensor lockSensor setDebugImages: getSerialisedTemplateForIdentity: delegate setDelegate: interruptionHandler _connection _delegate _interruptionHandler setTopology: setDetails: topology details _topology _details initEnrollmentValues message messageDetails objectForKeyedSubscript: currentPrimaryComponentID integerValue doubleValue statistics enroll: enrollResult:componentSet: enrollProgress: _fingerOn _enrolling _badImagePerFingerDown _enrollmentStarTime _touchesPerEnroll _badImagesPerEnroll _rejectedImagesPerEnroll _primaryClusterAdditions _primaryClusterFailedAdditions _otherClustersAdditions _joinEvents _area _primaryClusterArea numberWithBool: preferencesGetStringValue: preferencesGetBOOLValue: enableLogger:toPath: manager defaultCenter appDidEnterBackground: addObserver:selector:name:object: appWillEnterForeground: bundleForClass: imageNamed:inBundle: updateEnableLogger dataWithBytes:length: startEnrollLog data logRemoveIdentity: bytes imageFromRawImageData: imageFromBitmapData:inRect: pullDebugImageData:target: getRadarAtachmentsForLastEnrollment getRadarAtachmentsForLastMatch length stringWithString: getModulationRatio stringForProvisioningState: getSensorPatchVersion stringWithFormat: getLogsForProcess: sharedConnection isFingerprintUnlockAllowed setGracePeriod:passcode:completionBlock: finishEnrollLogWithStatus:withIdentity:withTemplate: matchResult:withDetails: createMatchInfo:withTopology:withMatchImage: logMatchResult:withTopology:withImage:withCaptureBuffer:withTemplate: getBytes:length: logEnrollMessage:withTopology:withImage:withCaptureBuffer: logStatus: enrollProgressMessage: logRejectedImage: size drawInRect: drawInRect:blendMode:alpha: CGImage scale imageOrientation imageWithCGImage:scale:orientation: imageWithImage:inRect: identityImage: imageWithImage:withNode:withRect:alpha: compositeTopologyImage: imageTopology:forGroup: imageFauxprint:withTheta:withLamda: greenColor imageWithImage:withTintColor: dataWithData: imageWithCGImage: preferencesSetBOOLValue:forKey: pullDebugImage: getLoggerAttachmentsForRadar: stringFromSensorConfiguration matchIdentity: topologyImage: imageWithImage:withMaskImage: inUse setInUse: enrollProgressConfigRenderMode setEnrollProgressConfigRenderMode: enrollProgressConfigRenderViewSize setEnrollProgressConfigRenderViewSize: renderMode setRenderMode: opacity setOpacity: _xpcClient _enrollingMode _matchingMode _statistics _scanbedImage _fauxprintImage _nodeRect _images _compSet _rejectTouchCount _rejectTouch _showDebugImages _enableLogger _enrollImageSet _isInternalInstall _inUse _enrollProgressConfigRenderMode _renderMode _opacity _enrollProgressConfigRenderViewSize setUuid: decodeBytesForKey:returnedLength: initWithUUIDBytes: decodeIntForKey: decodeObjectOfClass:forKey: copy getUUIDBytes: encodeBytes:length:forKey: encodeInt:forKey: encodeObject:forKey: biometricKitIdentity supportsSecureCoding encodeWithCoder: initWithCoder: uuid type setType: attribute setAttribute: entity setEntity: name setName: stringByReplacingOccurrencesOfString:withString: initWithCapacity: setLength: mutableBytes defaultManager createDirectoryAtPath:withIntermediateDirectories:attributes:error: removeItemAtPath:error: UUIDString setMessageDetails: setCaptureImage: setRenderedImage: progress setProgress: setCurrentPrimaryComponentID: captureImage renderedImage setMessage: _message _progress _currentPrimaryComponentID _captureImage _renderedImage _messageDetails setX: setY: angle setAngle: _angle setTransformationCoordinates: componentID setComponentID: transformationCoordinates _componentID _transformationCoordinates pathComponents com.apple.biometrickitd T@"",N,V_delegate interruptionHandler T@?,C,N,V_interruptionHandler topology T@"NSDictionary",&,N,V_details com.apple.fingerprint.enroll.attempts com.apple.fingerprint.enroll.passes com.apple.fingerprint.enroll.touchesPerEnroll com.apple.fingerprint.enroll.badImagesPerEnroll com.apple.fingerprint.enroll.rejectedImagesPerEnroll com.apple.fingerprint.enroll.primaryClusterAdditions com.apple.fingerprint.enroll.primaryClusterFailedAdditions com.apple.fingerprint.enroll.otherClustersAdditions com.apple.fingerprint.enroll.joinEvents com.apple.fingerprint.enroll.clusterCount com.apple.fingerprint.enroll.nodeCount com.apple.fingerprint.enroll.primaryClusterNodeCount com.apple.fingerprint.enroll.area com.apple.fingerprint.enroll.primaryClusterArea com.apple.fingerprint.enroll.passTime com.apple.fingerprint.enroll.fails com.apple.fingerprint.enroll.failTime com.apple.ManagedConfiguration.profileListChanged com.apple.biometrickitd.debugLogEnabled com.apple.biometrickitd.debugLogPath debugLogEnabled debugLogPath BKOptionSuppressHapticFeedback BKOptionFilterOutHomeButtonEvents BKOptionMatchForUnlock InternalBuild scanbed synthetic Uninitialized Not Provisioned Unprovisioned Provisioned Provisioned Locked Unpaired Unknown biosensor,mesa modulation-ratio AppleBiometricSensor patch-version Mesa configuration: Provisioning Status: %@ Calibrated: - Version: - Signed: Modulation ratio: Kernel: Thick kernel Thin kernel Sensor Patch Version: Steps to Reproduce: inUse TB,V_inUse enrollProgressConfigRenderMode Ti,N,V_enrollProgressConfigRenderMode enrollProgressConfigRenderViewSize T{CGSize=dd},N,V_enrollProgressConfigRenderViewSize renderMode Ti,N,V_renderMode opacity Tf,N,V_opacity Notification callback. BiometricKitErrorDomain BKIdentityUUID BKIdentityType BKIdentityAttribute BKIdentityEntityNumber BKIdentityName /var/mobile/BiometricKit/biometrickitd BKEPDReason BKEPDNewNodeID BKEPDNewComponentID BKEPDNewNodeCoordinates BKEPDRemovedNodeID BKEPDRemovedComponentID BKEPDExtendedComponentID BKEPDResultComponentID BKEPDMergedInComponents BKEPDRedundantNode BKTDLargestCompArea BKTDLargestCompNodes BKTDTotalArea BKTDTotalNodes BKTemplateUpdated Td,N,V_x Td,N,V_y angle Td,N,V_angle componentID Tq,N,V_componentID transformationCoordinates T@"BiometricKitEnrollProgressCoordinates",&,N,V_transformationCoordinates Remove identity: %@ Log package:%@ biometrickitd %@.tar.gz cd %@ && tar -cjf %@ %@ Time: % 8.3f PASS FAIL Closing log with result %@ Template identity: UUID : %@ Type : %i Attrib: %i Entity: %i Error: Unable to get identity Serialised template: %@ %@_%04d.bin Status message: %@ Progress %i Count of enrollments: %i BiometricKitErrorTaskCancelled BiometricKitErrorIdentityInvalid BiometricKitErrorIdentityNotAllowed BiometricKitErrorIdentityErrorInvalidData BiometricKitErrorIdentityErrorUnknown BiometricKitStatusFingerOn BiometricKitStatusFingerOff BiometricKitStatusEnrollmentComplete BiometricKitStatusEnrollmentCancelled BiometricKitStatusEnrollmentFailed BiometricKitStatusEnrollmentTimeout BiometricKitStatusUnknownError BiometricKitStatusImageRejected BiometricKitStatusNoCalibration BiometricKitImageForProcessing BiometricKitStatusTemplateListUpdated BiometricKitStatusRequestFingerOff BiometricKitStatusAutoMatchingStarted BiometricKitStatusAutoMatchingStopped BiometricKitStatusCaptureRestart BiometricKitStatusScanTooShort BiometricKitStatusAutoMatchingStartByHomeButton BiometricKitStatusMatchingCancelled BiometricKitStatusFingerOnBeforeFirstPasscodeUnlock BiometricKitStatusFingerOnInPasscodeLockout BiometricKitStatusFingerOnInBioLockout BiometricKitStatusFingerOnTokenExpired BiometricKitStatusESDRecovery BiometricKitStatusImageRejectedUnknown BiometricKitStatusImageRejectedBadBlocks BiometricKitStatusImageRejectedChFPN BiometricKitStatusImageRejectedCaFPN BiometricKitStatusSensorOperationModeIdle BiometricKitStatusSensorOperationModeCapture BiometricKitStatusSensorOperationModePause Other unknown status %i Image #%i was rejected Sensor patch version: %ld Sensor patch version: unknown Calibration Data: %@ Capture buffer: %@ Matcher: image refused Matcher: image %s node %u added as replaced Coordinates: %s[%i, %i, %i] inverse of Parent: %i Processed image: %@ Enroll debug log, version 7 static const node_placement_t table_%02i = %@{%i,%i,%i,%i,%i,%i,{ %@{%i,%i,%i,%i,%i} %@},%i,%i,%i,%i,%i,0x%04x}; bin8 bin16 binXX calibdata.bin calibration-blob Image #%i%s was sent to matcher Match result Match details: Matching score: %i Match S2S count: %i Matching node: %i Updated node: %i Not available No match (artificial) No match Image #%i Warning: the log is not complete, previous part of the log was deleted. Starting enrollment Starting matching No known identities with enroll log are available. Known identities with enroll log available: %@ : %@ Match debug log, version 7 messages.log OS version: %@ (%@) Mesa: %@ Process name:%@ PrimaryUsagePage PrimaryUsage com.apple.iokit.hid.displayStatus com.apple.mobile.keybagd.lock_status AppleMesaSEPDriver IOGeneralInterest ScanningState ScanningStateIdle ScanningStateShortScanning ScanningStateLongScanning Unknown scanning state %i Event: %@ HomeButtonPress ExtendedDeviceLockState MobileKeyBagDeviceIsUnlocked MobileKeyBagDeviceIsLocked MobileKeyBagDeviceIsLocking MobileKeyBagDisabled MobileKeyBagDeviceUnlockInProgress MobileKeyBagDeviceInGracePeriod MobileKeyBagDeviceInAssertDelay MobileKeyBagDeviceInBioUnlock Unknown lock state %i DisplayOn DisplayOff /SourceCache/Mesa/Mesa-152/AppleBiometricServices/BiometricKit/BiometricKitDebugLog.m /var/mobile/Library/Logs/CrashReporter/BiometricKit v24@0:8@"BiometricKitIdentity"16 @"NSXPCConnection" @"" @"BiometricKitXPCClient" @"BiometricKitStatistics" @"UIImage" [25{CGRect="origin"{CGPoint="x"d"y"d}"size"{CGSize="width"d"height"d}}] [25@"UIImage"] {?="count"i"capa"i"items"^^{?}"unusedImageCount"i"componentCount"i"componentCapa"i"bestComponentIndex"i"bestMapiComponentIndex"i"components"^^{?}"mapiNodeAddedIndex"s"mapiNodeRemovedIndex"s"updateCount"i"structureIsInconsistent"B} {?="nodes"[25{?="imageData"@"NSData""width"I"height"I}]} BiometricKitXpcProtocol BiometricKitDelegateXpcProtocol BiometricKitXPCClient BiometricKitTemplateInfo BiometricKitStatistics BiometricKit BiometricKitDelegate BiometricKitIdentity NSSecureCoding NSCoding BiometricKitMatchInfo BiometricKitEnrollProgressInfo BiometricKitEnrollProgressCoordinates BiometricKitEnrollProgressMergedComponent BiometricKitDebugLog