Skip to content

NFC chip reading

The chip on Turkish ID cards and passports is read with PassportReader. Accessing the chip needs the document’s serial number, date of birth and expiry date; take them from the document capture result (capturedData) and let the user confirm them.

iOS shows its own NFC sheet while reading; the SDK sets its texts. Set up the NFC capability first.

import passport_reader
import ProveIDApiClient
final class NFCViewController: UIViewController {
var transactionId = ""
var documentNumber = "" // capturedData.sno
var birthDate = "" // capturedData.bdat, dd.MM.yyyy
var expireDate = "" // capturedData.edat, dd.MM.yyyy
private var passportReader: PassportReader?
override func viewDidLoad() {
super.viewDidLoad()
PassportReader.PACE_ENABLED = true
LocalNFCCommandTransmitter.NFC_START_MESSAGE =
"Place the top of your phone on the middle of your ID card and hold still."
passportReader = PassportReader(commandTransmitter: LocalNFCCommandTransmitter(),
progressMessage: { input in
ProgressMessageOutput(alertMessage: "Reading…")
},
errorMessage: { input in
ErrorMessageOutput(alertMessage: "Reading failed.", suppressError: false)
})
startReading()
}
}

progressMessage and errorMessage set the text on the iOS NFC sheet. input.nfcOperationType and input.progress tell you how far reading has got; input.error the error (see Errors).

Get a transaction-specific nonce from Prove ID for active authentication, then start reading:

func startReading() {
IDServiceAPI.generateNonce(body: NonceGeneratorModel(transactionId: transactionId)) { result in
switch result {
case .success(let response):
DispatchQueue.main.async { self.read(nonce: response.nonce) }
case .failure(let error):
showError(error)
}
}
}
func read(nonce: String?) {
let formatter = DateFormatter()
formatter.dateFormat = "dd.MM.yyyy"
formatter.timeZone = TimeZone(secondsFromGMT: 0)
guard let birth = formatter.date(from: birthDate),
let expire = formatter.date(from: expireDate) else {
// Ask the user to correct the dates
return
}
passportReader?.read(documentNumber: documentNumber,
birthDate: birth,
expireDate: expire,
nonce: nonce,
callback: self)
}
extension NFCViewController: PassportReaderCallback {
func result(passportModel: PassportModel) {
// Send the chip data for validation (below)
}
func progress(progressType: NFCOperationType, progress: Double) {
// START, AUTHENTICATION, DG1, DG2, DG11, DG12, DG14, DG15, SOD, ACTIVE_AUTHENTICATON, CHIP_AUTHENTICATION, SUCCESS
}
func error(error: Error) {
// See Errors
}
}
let validation = IDValidationModel(dg1: passportModel.getDg1()!,
dg2: passportModel.getDg2()!,
sod: passportModel.getSod()!,
dg11: passportModel.getDg11(),
dg12: passportModel.getDg12(),
dg14: passportModel.getDg14(),
dg15: passportModel.getDg15(),
nonce: passportModel.getNonce(),
signedData: passportModel.getSignedData(),
mseResp: passportModel.getCaMseResponse(),
gaResp: passportModel.getCaGaResponse(),
probeResp: passportModel.getCaProbeResponse(),
accessProtocol: passportModel.getAccessProtocol()?.value)
let body = ValidationInputModel(validationInformation: validation,
idType: "IDCARD",
transactionId: transactionId)
IDServiceAPI.validateAndSaveIDCard(body: body) { result in
switch result {
case .success(let response):
let controls = response.controlResults
showNfcResult(controls)
case .failure(let error):
showError(error)
}
}

With chip authentication the chip proves it is genuine and not cloned. The keys for it are kept on Prove ID, never in the app: the SDK asks your ChipAuthenticationBackend to prepare the commands, and you forward the request to Prove ID.

final class ChipAuthenticationBackendImpl: ChipAuthenticationBackend {
var transactionId = ""
func prepareCA(dg14Bytes: String,
completed: @escaping (ChipAuthenticationPlan?, Error?) -> Void) {
let body = CAPrepareInputModel(transactionId: transactionId, dg14: dg14Bytes)
IDServiceAPI.prepareCA(body: body) { result in
switch result {
case .success(let output):
guard let mse = output.mseApdu, let ga = output.gaApdu, let probe = output.probeApdu else {
completed(nil, NSError(domain: "ChipAuthentication", code: -1,
userInfo: [NSLocalizedDescriptionKey: output.message ?? ""]))
return
}
completed(ChipAuthenticationPlan(mseApdu: mse, gaApdu: ga, smProbeApdu: probe), nil)
case .failure(let error):
completed(nil, error)
}
}
}
}
let backend = ChipAuthenticationBackendImpl() // keep a strong reference
backend.transactionId = transactionId
PassportReader.DO_CHIP_AUTHENTICATION = true
PassportReader.CHIP_AUTHENTICATION_BACKEND = backend

The chip authentication responses are sent with the chip data in IDValidationModel (mseResp, gaResp, probeResp).

Setting Description
PassportReader.PACE_ENABLED Uses PACE to access the chip when the document supports it
PassportReader.DO_CHIP_AUTHENTICATION Runs chip authentication
PassportReader.CHIP_AUTHENTICATION_BACKEND Your ChipAuthenticationBackend
PassportReader.SUPPRESS_ACTIVE_AUTH Continues reading if active authentication fails
PassportReader.SUPPRESS_CHIP_AUTHENTICATION Continues reading if chip authentication fails
LocalNFCCommandTransmitter.NFC_START_MESSAGE Text on the iOS NFC sheet when reading starts
PassportReaderEvent.logLevel Log level of the SDK (.none in production)
PassportReaderEvent.listener An EventListener that receives the SDK’s log events