Skip to content

Commit

Permalink
Merge pull request #3 from andrea-deluca/pre-release
Browse files Browse the repository at this point in the history
release(v0.1.1): exposed data and bug fix patch
  • Loading branch information
andrea-deluca authored Dec 1, 2023
2 parents 6207ed9 + 44cea23 commit be3a9f7
Show file tree
Hide file tree
Showing 208 changed files with 1,234 additions and 324 deletions.
12 changes: 11 additions & 1 deletion Sources/NFCPassportReader/Models/DocumentDetails.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,17 @@ public struct DocumentDetails {
private var details: [ASN1Tag: String] = [:]

public var iussingAuthority: String? { details[0x5F19] }
public var dateOfIssue: String? { details[0x5F26] }

public var dateOfIssue: String? {
if let date = details[0x5F26] {
let strategy = Date.ParseStrategy(
format: "\(year: .padded(4))\(month: .twoDigits)\(day: .twoDigits)",
timeZone: TimeZone(identifier: "UTC")!
)
return try? Date(date, strategy: strategy).formatted(date: .abbreviated, time: .omitted)
} else { return details[0x5F26] }
}

public var otherPersonDetails: String? { details[0xA0] }
public var endorsementsOrObservations: String? { details[0x5F1B] }
public var taxOrExitRequirements: String? { details[0x5F1C] }
Expand Down
75 changes: 57 additions & 18 deletions Sources/NFCPassportReader/Models/MRZ.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,89 +28,117 @@ public struct MRZ {
private var bytes: [UInt8]
private var type: TDType

var code: String? { String(bytes: bytes, encoding: .utf8) }
public var code: String? { String(bytes: bytes, encoding: .utf8) }

var documentCode: String? {
public var documentCode: String? {
String(bytes: bytes[0..<2], encoding:.utf8)
}

var issuingState: String? {
public var issuingState: String? {
String(bytes: bytes[2..<5], encoding:.utf8)
}

var holderName: String? {
switch self.type {
public var holderName: String? {
let data = switch self.type {
case .TD1: String(bytes: bytes[60...], encoding:.utf8)
case .TD2: String(bytes: bytes[5..<36], encoding:.utf8)
case .TD3: String(bytes: bytes[5..<44], encoding:.utf8)
}

if let components = data?.components(separatedBy: "<<") {
let surname = components[0].components(separatedBy: "<").joined(separator: " ")
let name = components[1].components(separatedBy: "<").joined(separator: " ")
return [surname, name].joined(separator: " ")
} else { return data }
}

internal var surname: String? {
if let components = holderName?.components(separatedBy: "<<") {
let surname = components[0].components(separatedBy: "<").joined(separator: " ")
return surname
} else { return nil }
}

internal var name: String? {
if let components = holderName?.components(separatedBy: "<<") {
let name = components[1].components(separatedBy: "<").joined(separator: " ")
return name
} else { return nil }
}

var documentNumber: String? {
public var documentNumber: String? {
switch self.type {
case .TD1: String(bytes: bytes[5..<14], encoding:.utf8)
case .TD2: String(bytes: bytes[36..<45], encoding:.utf8)
case .TD3: String(bytes: bytes[44..<53], encoding:.utf8)
}
}

var documentNumberCheckDigit: String? {
public var documentNumberCheckDigit: String? {
switch type {
case .TD1: String(bytes: bytes[14..<15], encoding:.utf8)
case .TD2: String(bytes: bytes[45..<46], encoding:.utf8)
case .TD3: String(bytes: bytes[53..<54], encoding:.utf8)
}
}

var nationality: String? {
public var nationality: String? {
switch self.type {
case .TD1: String(bytes: bytes[45..<48], encoding:.utf8)
case .TD2: String(bytes: bytes[46..<49], encoding:.utf8)
case .TD3: String(bytes: bytes[54..<57], encoding:.utf8)
}
}

var dateOfBirth: String? {
switch self.type {
public var dateOfBirth: String? {
let date = switch self.type {
case .TD1: String(bytes: bytes[30..<36], encoding:.utf8)
case .TD2: String(bytes: bytes[49..<55], encoding:.utf8)
case .TD3: String(bytes: bytes[57..<63], encoding:.utf8)
}

if let date = date {
return self.parseDate(date: date)
} else { return date }
}

var dateOfBirthCheckDigit: String? {
public var dateOfBirthCheckDigit: String? {
switch self.type {
case .TD1: String(bytes: [bytes[36]], encoding:.utf8)
case .TD2: String(bytes: [bytes[55]], encoding:.utf8)
case .TD3: String(bytes: [bytes[63]], encoding:.utf8)
}
}

var sex: String? {
public var sex: String? {
switch self.type {
case .TD1: String(bytes: [bytes[37]], encoding:.utf8)
case .TD2: String(bytes: [bytes[56]], encoding:.utf8)
case .TD3: String(bytes: [bytes[64]], encoding:.utf8)
}
}

var dateOfExpiry: String? {
switch self.type {
public var dateOfExpiry: String? {
let date = switch self.type {
case .TD1: String(bytes: bytes[38..<44], encoding:.utf8)
case .TD2: String(bytes: bytes[57..<63], encoding:.utf8)
case .TD3: String(bytes: bytes[65..<71], encoding:.utf8)
}

if let date = date {
return self.parseDate(date: date)
} else { return date }
}

var dateOfExpiryCheckDigit: String? {
public var dateOfExpiryCheckDigit: String? {
switch self.type {
case .TD1: String(bytes: [bytes[44]], encoding:.utf8)
case .TD2: String(bytes: [bytes[63]], encoding:.utf8)
case .TD3: String(bytes: [bytes[71]], encoding:.utf8)
}
}

var optionalData: String? {
public var optionalData: String? {
switch self.type {
case .TD1:
(String(bytes: bytes[15..<30], encoding:.utf8) ?? "") +
Expand All @@ -120,13 +148,13 @@ public struct MRZ {
}
}

var checkDigit: String? {
public var checkDigit: String? {
if self.type == .TD3 {
String(bytes: [bytes[86]], encoding:.utf8)
} else { nil }
}

var compositeCheckDigit: String? {
public var compositeCheckDigit: String? {
switch self.type {
case .TD1: nil
case .TD2: String(bytes: bytes[71..<72], encoding:.utf8)
Expand All @@ -142,3 +170,14 @@ public struct MRZ {
self.type = type
}
}

internal extension MRZ {
func parseDate(date: String) -> String? {
let strategy = Date.ParseStrategy(
format: "\(year: .twoDigits)\(month: .twoDigits)\(day: .twoDigits)",
timeZone: TimeZone(identifier: "UTC")!
)

return try? Date(date, strategy: strategy).formatted(date: .abbreviated, time: .omitted)
}
}
55 changes: 51 additions & 4 deletions Sources/NFCPassportReader/Models/PersonalDetails.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,58 @@ public struct PersonalDetails {

private var details: [ASN1Tag: String] = [:]

public var fullName: String? { details[0x5F0E] }
public var fullName: String? {
if let components = details[0x5F0E]?.components(separatedBy: "<<") {
let surname = components[0].components(separatedBy: "<").joined(separator: " ")
let name = components[1].components(separatedBy: "<").joined(separator: " ")
return [surname, name].joined(separator: " ")
} else { return details[0x5F0E] }
}

internal var surname: String? {
if let components = details[0x5F0E]?.components(separatedBy: "<<") {
let surname = components[0].components(separatedBy: "<").joined(separator: " ")
return surname
} else { return details[0x5F0E] }
}

internal var name: String? {
if let components = details[0x5F0E]?.components(separatedBy: "<<") {
let name = components[1].components(separatedBy: "<").joined(separator: " ")
return name
} else { return details[0x5F0E] }
}

public var personalNumber: String? { details[0x5F10] }
public var dateOfBirth: String? { details[0x5F2B] }
public var placeOfBirth: String? { details[0x5F11] }
public var address: String? { details[0x5F42] }

public var dateOfBirth: String? {
if let date = details[0x5F2B] {
let strategy = Date.ParseStrategy(
format: "\(year: .padded(4))\(month: .twoDigits)\(day: .twoDigits)",
timeZone: TimeZone(identifier: "UTC")!
)
return try? Date(date, strategy: strategy).formatted(date: .abbreviated, time: .omitted)
} else { return details[0x5F2B] }
}

public var placeOfBirth: String? {
if let components = details[0x5F11]?.components(separatedBy: "<") {
let city = components[0]
let province = "\(components[1])"
return "\(city) (\(province))"
} else { return details[0x5F11] }

}

public var address: String? {
if let components = details[0x5F42]?.components(separatedBy: "<") {
let address = components[0]
let city = components[1]
let province = components[2]
return "\(address) \(city) (\(province))"
} else { return details[0x5F42] }
}

public var telephone: String? { details[0x5F12] }
public var profession: String? { details[0x5F13] }
public var title: String? { details[0x5F14] }
Expand Down
68 changes: 62 additions & 6 deletions Sources/NFCPassportReader/NFCPassportModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,73 @@ public struct NFCPassportModel {
(self.dataGroupsRead[.DG12] as? DataGroup12)?.documentDetails
}

public var firstName: String {
personalDetails?.fullName?.components(separatedBy: "<<")[0] ??
travelDocument?.mrz.holderName?.components(separatedBy: "<<")[0] ?? "Unkown"
public var certificateDetails: X509CertificateDetails? {
(self.dataGroupsRead[.SOD] as? SOD)?.certs.first?.details
}

public var lastName: String {
personalDetails?.fullName?.components(separatedBy: "<<")[1] ??
travelDocument?.mrz.holderName?.components(separatedBy: "<<")[1] ?? "Unkown"
public var lastName: String? {
personalDetails?.surname ??
travelDocument?.mrz.surname
}

public var firstName: String? {
personalDetails?.name ??
travelDocument?.mrz.name
}

public var holderPicture: UIImage? {
self.faceBiometricDataEncoding?.image
}

public var availableDataGroups: [String]? {
(self.dataGroupsRead[.COM] as? COM)?.availableDataGroups.map { $0.name }
}

public var dataGroupsReadNames: [String] {
self.dataGroupsRead.map { $0.key.name }
}

public var paceSecurityProtocol: String? {
if let paceInfo = (self.dataGroupsRead[.DG14] as? DataGroup14)?
.securityInfos
.first(where: { $0 is PACEInfo }) as? PACEInfo {
return "\(paceInfo.securityProtocol)"
} else { return nil }
}

public var chipAuthenticationSecurityProtocol: String? {
if let caInfo = (self.dataGroupsRead[.DG14] as? DataGroup14)?
.securityInfos
.first(where: { $0 is ChipAuthenticationInfo }) as? ChipAuthenticationInfo {
return "\(caInfo.securityProtocol)"
} else if let caPubKeyInfo = (self.dataGroupsRead[.DG14] as? DataGroup14)?
.securityInfos
.first(where: { $0 is ChipAuthenticationPublicKeyInfo }) as? ChipAuthenticationPublicKeyInfo {
return "\(caPubKeyInfo.securityProtocol.defaultChipAuthenticationSecurityProtocol)"
} else { return nil }
}

public var chipAuthenticationPublicKeySecurityProtocol: String? {
if let caPubKeyInfo = (self.dataGroupsRead[.DG14] as? DataGroup14)?
.securityInfos
.first(where: { $0 is ChipAuthenticationPublicKeyInfo }) as? ChipAuthenticationPublicKeyInfo {
return "\(caPubKeyInfo.securityProtocol)"
} else { return nil }
}

public var sodHashes: [String: String] {
if let sod = (self.dataGroupsRead[.SOD] as? SOD) {
var hashes: [String: String] = [:]
sod.signedData.encapContentInfo.forEach {
hashes.updateValue(
BytesRepresentationConverter.convertToHexRepresentation(from: $1),
forKey: $0.name
)}
return hashes
} else { return [:] }
}

public var sodPemCertificate: String? {
(self.dataGroupsRead[.SOD] as? SOD)?.certs.first?.pem
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ internal final class PassiveAuthenticationHandler {
throw NFCPassportReaderError.PassiveAuthenticationFailed("Data group hash not found in SOD")
}

let computedHash = try HashAlgorithm.hash([UInt8](dataGroup.data.encodedBytes), with: sod.signedData.digestAlgorithm)
let computedHash = try HashAlgorithm.hash([UInt8](dataGroup.data.encodedBytes), with: sod.signedData.digestAlgorithm ?? .SHA1 )

if computedHash != currentSodHash {
throw NFCPassportReaderError.PassiveAuthenticationFailed("\(String(describing: dataGroup.identifier)) hash not match")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ internal final class SignedData {
private var data: ASN1NodeCollection

/// The hash algorithm used for signing.
private(set) var digestAlgorithm: HashAlgorithm!
private(set) var digestAlgorithm: HashAlgorithm?

/// A dictionary representing encapsulated content information,
/// where the key is the ``DGTag`` and the value is the data group hash.
Expand Down
Loading

0 comments on commit be3a9f7

Please sign in to comment.