DOT iOS NFC library
v2.2.1
Introduction
DOT iOS NFC provides a component for NFC reading which is easy to integrate into an iOS application. Supported documents are those which implement Machine Readable Travel Document (MRTD) standards as specified by International Civil Aviation Organization (ICAO).
Requirements
Xcode 11.4+
Framework supports iOS 13+. However, it can be added to projects with minimum deployment target iOS 11.0+. This allows you to put DOT iOS NFC code in
@available
block.Swift or Objective-C support
CocoaPods
Distribution
Cocoapods
DOT iOS NFC is distributed as a XCFramework - DotNfc.xcframework using Cocoapods
with its dependencies stored in our public github repository. It can be easily integrated into XCode with the custom definition of podspecs
. The first step is to insert the following line of code on top of your Podfile
.
source 'https://github.com/innovatrics/innovatrics-podspecs'
Then DOT iOS NFC dependency must be specified in Podfile
. Dependencies of DOT iOS NFC will be downloaded alongside it.
source 'https://github.com/innovatrics/innovatrics-podspecs'
use_frameworks!
target 'YOUR_TARGET' do
pod 'dot-nfc'
end
In the case of CocoaPods problem with
|
Supported Architectures
DOT iOS NFC provides all supported architectures in the distributed XCFramework package.
Device binary contains: arm64
.
Simulator binary contains: x86_64
, arm64
.
Permissions
Add capability to your Xcode target:
Near Field Communication Tag Reading
DOT iOS NFC requires following keys in
Info.plist
:
<key>NFCReaderUsageDescription</key>
<string>NFC tag to read NDEF messages into the application</string>
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
<string>A0000002471001</string>
</array>
Basic Setup
Logging
DOT iOS NFC supports logging using a global Logger
class. You can set the log level as follows:
import DotNfc
Logger.logLevel = .debug
Log levels:
info
debug
warning
error
none
Each log message contains dot-nfc
tag. Keep in mind that logging should be used just for debugging purposes.
Components
Overview
DOT iOS NFC provides a non-UI component for NFC reading. You may customize native UI, which is presented during the NFC reading on the iOS platform.
List of Non-UI Components
- NFC TRAVEL DOCUMENT READER
The component for reading NFC enabled travel documents.
- FLAT TAG STRUCTURE PARSER
The component for parsing flat tag structure in a data group as defined in Doc 9303.
Non-UI Components
NFC Travel Document Reader
The NfcTravelDocumentReader
class provides NFC reading functionality.
Create NfcTravelDocumentReader
:
let authorityCertificatesUrl = Bundle.main.url(forResource: "masterList", withExtension: "pem")
let configuration = NfcTravelDocumentReaderConfiguration(authorityCertificatesUrl: authorityCertificatesUrl)
let nfcTravelDocumentReader = NfcTravelDocumentReader(configuration: configuration)
nfcTravelDocumentReader.setDelegate(self)
NfcTravelDocumentReaderConfiguration
(Optional)
[-]
authorityCertificatesUrl: URL?
– URL to certificates of trusted authorities. Certificates are required to successfully execute Passive Authentication.
To read NFC data, call the following method:
nfcTravelDocumentReader.read(nfcKey: nfcKey)
NfcKey
is the access key to NFC data.
You can handle the NfcTravelDocumentReader
events using its delegate NfcTravelDocumentReaderDelegate
.
@objc(DOTNfcTravelDocumentReaderDelegate) public protocol NfcTravelDocumentReaderDelegate {
/// Tells the delegate that NFC reading has finished successfully with travel document as a result.
@objc func nfcTravelDocumentReader(_ nfcTravelDocumentReader: NfcTravelDocumentReader, succeeded travelDocument: TravelDocument)
/// Tells the delegate that NFC reading has failed with an error.
@objc func nfcTravelDocumentReader(_ nfcTravelDocumentReader: NfcTravelDocumentReader, failed error: NfcTravelDocumentReaderError)
}
NfcKey
The NfcKey
object is created from the travel document number, date of birth and date of expiry.
let nfcKey = try NfcKey(documentNumber: documentNumber, dateOfExpiry: dateOfExpiry, dateOfBirth: dateOfBirth)
Reading Process
Travel document reading process consists of three steps: Access Establishment, Passive Authentication, Active Authentication.
Access Establishment
There are two Access Establishment protocols Basic Access Control - BAC and Password Authenticated Connection Establishment - PACE. DOT iOS NFC supports only BAC.
BAC: In order to access document using BAC, NFC Key is required. This NFC Key is created from the document number, date of birth and date of expiry.
PACE: The newer and more secure version of Access Establishment protocols. It uses NFC Key (weak password with low entropy) and generates cryptographically strong session keys.
Passive Authentication
The purpose of Passive Authentication is to validate the integrity of data stored on NFC chip. In other words, it verifies that data stored on NFC chip has not been altered. Passive Authentication has the following steps:
extract and validate Document Signing Certificate with CSCA Certificates from master list
verify that Security Data (EF.SOD) has been correctly signed by Document Signing Certificate
verify that hashes stored in EF.SOD are valid, i.e. hashes stored in EF.SOD are equal to hashes computed from data groups present on the document
Active Authentication
The purpose of Active Authentication is to verify that document is genuine, i.e. it is not a copy. Active Authentication has the following steps:
generate random challenge
request a signature for this challenge from the NFC chip
verify the signature using the public key stored in Data Group 15 (DG15)
The public key stored in DG15 can be RSA or ECDSA. DOT iOS NFC supports both RSA and ECDSA.
Flat Tag Structure Parser
The FlatTagStructureParser
class parses input data into a dictionary of data elements.
Create a FlatTagStructureParser
:
let flatTagStructureParser = FlatTagStructureParser()
To parse a Data Group value, call the following method:
let elements = try flatTagStructureParser.parse(value: bytes)
Following data structure is expected as an input:
ELEMENT_1_TAG, ELEMENT_1_LENGTH, ELEMENT_1_VALUE, ..., ELEMENT_N_TAG, ELEMENT_N_LENGTH, ELEMENT_N_VALUE
Such a value is parsed into this map:
Tag | Value |
|
|
… | … |
|
|
This is an example of a resulting map:
Tag | Length | Value |
|
|
|
|
|
|
Some country authorities may use this structure in the Optional Details. This data is present in TravelDocument.optionalDetails
as a result of the NFC reading.
Customization of native UI
Localization
String resources can be overridden in your application and alternative strings for supported languages can be provided following these two steps:
Add your own
Localizable.strings
file to your project using standard iOS localization mechanism. To change a specific text override corresponding key in thisLocalizable.strings
file.Set the localization bundle to the bundle of your application (preferably during the application launch in your
AppDelegate
).
Use this setup if you want to use standard iOS localization mechanism, which means your iOS application uses system defined locale.
import DotNfc
Localization.bundle = .main
Custom Localization
You can override standard iOS localization mechanism by providing your own translation dictionary and setting the Localization.useLocalizationDictionary
flag to true
. Use this setup if you do not want to use standard iOS localization mechanism, which means your iOS application ignores system defined locale and uses its own custom locale.
import DotNfc
guard let localizableUrl = Bundle.main.url(forResource: "Localizable", withExtension: "strings", subdirectory: nil, localization: "de"),
let dictionary = NSDictionary(contentsOf: localizableUrl) as? [String: String]
else { return }
Localization.useLocalizationDictionary = true
Localization.localizationDictionary = dictionary
"dot.nfc_travel_document_reader.instruction.beginReading" = "Hold your iPhone near an NFC enabled document.";
"dot.nfc_travel_document_reader.instruction.establishAccessControl" = "Establishing access control with the document...";
"dot.nfc_travel_document_reader.instruction.readingSuccessful" = "Document reading was successful.";
"dot.nfc_travel_document_reader.instruction.accessControlError" = "There was a problem establishing access control with the document.";
"dot.nfc_travel_document_reader.instruction.readingError" = "There was an error reading the document.";
"dot.nfc_travel_document_reader.instruction.readingCom" = "Reading COM...";
"dot.nfc_travel_document_reader.instruction.readingSod" = "Reading SOD...";
"dot.nfc_travel_document_reader.instruction.readingDG1" = "Reading DG1...";
"dot.nfc_travel_document_reader.instruction.readingDG2" = "Reading DG2...";
"dot.nfc_travel_document_reader.instruction.readingDG3" = "Reading DG3...";
"dot.nfc_travel_document_reader.instruction.readingDG4" = "Reading DG4...";
"dot.nfc_travel_document_reader.instruction.readingDG5" = "Reading DG5...";
"dot.nfc_travel_document_reader.instruction.readingDG6" = "Reading DG6...";
"dot.nfc_travel_document_reader.instruction.readingDG7" = "Reading DG7...";
"dot.nfc_travel_document_reader.instruction.readingDG8" = "Reading DG8...";
"dot.nfc_travel_document_reader.instruction.readingDG9" = "Reading DG9...";
"dot.nfc_travel_document_reader.instruction.readingDG10" = "Reading DG10...";
"dot.nfc_travel_document_reader.instruction.readingDG11" = "Reading DG11...";
"dot.nfc_travel_document_reader.instruction.readingDG12" = "Reading DG12...";
"dot.nfc_travel_document_reader.instruction.readingDG13" = "Reading DG13...";
"dot.nfc_travel_document_reader.instruction.readingDG14" = "Reading DG14...";
"dot.nfc_travel_document_reader.instruction.readingDG15" = "Reading DG15...";
"dot.nfc_travel_document_reader.instruction.readingDG16" = "Reading DG16...";
"dot.nfc_travel_document_reader.instruction.readingUnknownDG" = "Reading unknown data group...";
Appendix
Changelog
2.2.0 - 2022-01-21
Added
FlatTagStructureParser
2.1.0 - 2022-01-19
Added
OptionalDetails
TravelDocument.optionalDetails
Changed
AdditionalPersonalDetails.nameOfHolder
type fromString?
toNameOfHolder?
Fixed
resolving of JPEG image format, using
ImageFormat.jpeg
2.0.2 - 2021-12-13
Fixed
parsing of
AdditionalDocumentDetails.dateOfIssue
2.0.1 - 2021-12-13
Fixed
wrong Passive Authentication flag returned
Image
Objective-C class name changed fromDOTImage
toDOTNImage
2.0.0 - 2021-12-02
Added
NfcKey
NfcTravelDocumentReaderConfiguration
NfcTravelDocumentReaderDelegate
NfcTravelDocumentReaderError
PassiveAuthenticationStatus
ActiveAuthenticationStatus
Image
MachineReadableZoneInformation
EncodedIdentificationFeaturesFace
DisplayedSignatureOrUsualMark
AdditionalPersonalDetails
AdditionalDocumentDetails
NameOfHolder
AccessControlProtocol
Logger
Changed
minimal required iOS version to iOS 11.0
added support for simulator build
added support for bitcode, ENABLE_BITCODE = YES
updated OpenSSL to version 1.1.1l
added support for Active Authentication with RSA public keys
removed default master list
renamed module to
DotNfc
renamed
DotNfcLocalization
toLocalization
updated localization keys
renamed
NFCPassportReader
toNfcTravelDocumentReader
NfcTravelDocumentReader
is now using delegate patternchanged
NFCPassportReader.readPassport(mrzKey: String, result: @escaping ReadPassportResult) → Void
toNfcTravelDocumentReader.read(nfcKey: NfcKey)
renamed
Passport
toTravelDocument
Removed
NFCFormatter
1.2.1 - 2021-10-01
Changed
release library as a XCFramework package
1.2.0 - 2021-03-04
Added
DotNfcLocalization
class to enable localization of alert messages
1.1.1 - 2020-10-23
Fixed
removed force unwraps and added better error handling
1.1.0 - 2020-07-30
Added
Released as a new component
dot-nfc
1.0.3 - 2020-03-31
Changed
Renamed class
NFCReader
toNFCPassportReader
1.0.2 - 2020-03-31
Added
BUILD_LIBRARY_FOR_DISTRIBUTION = YES
1.0.1 - 2020-03-12
Added
OpenSSL
dependency as an externalpod
dependencyinnovatrics-openssl
1.0.0 - 2020-03-02
Added
faceImage
andsignatureImage
toPassport
struct