Revert "now reading and sending proof"

This reverts commit 1d76de2807.
This commit is contained in:
0xturboblitz
2023-12-09 17:06:20 -05:00
parent ce99bd688a
commit a45a0f3eb7
8 changed files with 501 additions and 0 deletions

View File

@@ -0,0 +1,193 @@
//
// BACHandler.swift
// NFCTest
//
// Created by Andy Qua on 07/06/2019.
// Copyright © 2019 Andy Qua. All rights reserved.
//
import Foundation
#if !os(macOS)
import CoreNFC
@available(iOS 15, *)
public class BACHandler {
let KENC : [UInt8] = [0,0,0,1]
let KMAC : [UInt8] = [0,0,0,2]
public var ksenc : [UInt8] = []
public var ksmac : [UInt8] = []
var rnd_icc : [UInt8] = []
var rnd_ifd : [UInt8] = []
public var kifd : [UInt8] = []
var tagReader : TagReader?
public init() {
// For testing only
}
public init(tagReader: TagReader) {
self.tagReader = tagReader
}
public func performBACAndGetSessionKeys( mrzKey : String ) async throws {
guard let tagReader = self.tagReader else {
throw NFCPassportReaderError.NoConnectedTag
}
Log.debug( "BACHandler - deriving Document Basic Access Keys" )
_ = try self.deriveDocumentBasicAccessKeys(mrz: mrzKey)
// Make sure we clear secure messaging (could happen if we read an invalid DG or we hit a secure error
tagReader.secureMessaging = nil
// get Challenge
Log.debug( "BACHandler - Getting initial challenge" )
let response = try await tagReader.getChallenge()
Log.verbose( "DATA - \(response.data)" )
Log.debug( "BACHandler - Doing mutual authentication" )
let cmd_data = self.authentication(rnd_icc: [UInt8](response.data))
let maResponse = try await tagReader.doMutualAuthentication(cmdData: Data(cmd_data))
Log.debug( "DATA - \(maResponse.data)" )
guard maResponse.data.count > 0 else {
throw NFCPassportReaderError.InvalidMRZKey
}
let (KSenc, KSmac, ssc) = try self.sessionKeys(data: [UInt8](maResponse.data))
tagReader.secureMessaging = SecureMessaging(ksenc: KSenc, ksmac: KSmac, ssc: ssc)
Log.debug( "BACHandler - complete" )
}
func deriveDocumentBasicAccessKeys(mrz: String) throws -> ([UInt8], [UInt8]) {
let kseed = generateInitialKseed(kmrz:mrz)
Log.verbose("Calculate the Basic Access Keys (Kenc and Kmac) using TR-SAC 1.01, 4.2")
let smskg = SecureMessagingSessionKeyGenerator()
self.ksenc = try smskg.deriveKey(keySeed: kseed, mode: .ENC_MODE)
self.ksmac = try smskg.deriveKey(keySeed: kseed, mode: .MAC_MODE)
return (ksenc, ksmac)
}
///
/// Calculate the kseed from the kmrz:
/// - Calculate a SHA-1 hash of the kmrz
/// - Take the most significant 16 bytes to form the Kseed.
/// @param kmrz: The MRZ information
/// @type kmrz: a string
/// @return: a 16 bytes string
///
/// - Parameter kmrz: mrz key
/// - Returns: first 16 bytes of the mrz SHA1 hash
///
func generateInitialKseed(kmrz : String ) -> [UInt8] {
Log.verbose("Calculate the SHA-1 hash of MRZ_information")
Log.verbose("\tMRZ KEY - \(kmrz)")
let hash = calcSHA1Hash( [UInt8](kmrz.data(using:.utf8)!) )
Log.verbose("\tsha1(MRZ_information): \(binToHexRep(hash))")
let subHash = Array(hash[0..<16])
Log.verbose("Take the most significant 16 bytes to form the Kseed")
Log.verbose("\tKseed: \(binToHexRep(subHash))" )
return Array(subHash)
}
/// Construct the command data for the mutual authentication.
/// - Request an 8 byte random number from the MRTD's chip (rnd.icc)
/// - Generate an 8 byte random (rnd.ifd) and a 16 byte random (kifd)
/// - Concatenate rnd.ifd, rnd.icc and kifd (s = rnd.ifd + rnd.icc + kifd)
/// - Encrypt it with TDES and the Kenc key (eifd = TDES(s, Kenc))
/// - Compute the MAC over eifd with TDES and the Kmax key (mifd = mac(pad(eifd))
/// - Construct the APDU data for the mutualAuthenticate command (cmd_data = eifd + mifd)
///
/// @param rnd_icc: The challenge received from the ICC.
/// @type rnd_icc: A 8 bytes binary string
/// @return: The APDU binary data for the mutual authenticate command
func authentication( rnd_icc : [UInt8]) -> [UInt8] {
self.rnd_icc = rnd_icc
Log.verbose("Request an 8 byte random number from the MRTD's chip")
Log.verbose("\tRND.ICC: " + binToHexRep(self.rnd_icc))
self.rnd_icc = rnd_icc
let rnd_ifd = generateRandomUInt8Array(8)
let kifd = generateRandomUInt8Array(16)
Log.verbose("Generate an 8 byte random and a 16 byte random")
Log.verbose("\tRND.IFD: \(binToHexRep(rnd_ifd))" )
Log.verbose("\tRND.Kifd: \(binToHexRep(kifd))")
let s = rnd_ifd + rnd_icc + kifd
Log.verbose("Concatenate RND.IFD, RND.ICC and Kifd")
Log.verbose("\tS: \(binToHexRep(s))")
let iv : [UInt8] = [0, 0, 0, 0, 0, 0, 0, 0]
let eifd = tripleDESEncrypt(key: ksenc,message: s, iv: iv)
Log.verbose("Encrypt S with TDES key Kenc as calculated in Appendix 5.2")
Log.verbose("\tEifd: \(binToHexRep(eifd))")
let mifd = mac(algoName: .DES, key: ksmac, msg: pad(eifd, blockSize:8))
Log.verbose("Compute MAC over eifd with TDES key Kmac as calculated in-Appendix 5.2")
Log.verbose("\tMifd: \(binToHexRep(mifd))")
// Construct APDU
let cmd_data = eifd + mifd
Log.verbose("Construct command data for MUTUAL AUTHENTICATE")
Log.verbose("\tcmd_data: \(binToHexRep(cmd_data))")
self.rnd_ifd = rnd_ifd
self.kifd = kifd
return cmd_data
}
/// Calculate the session keys (KSenc, KSmac) and the SSC from the data
/// received by the mutual authenticate command.
/// @param data: the data received from the mutual authenticate command send to the chip.
/// @type data: a binary string
/// @return: A set of two 16 bytes keys (KSenc, KSmac) and the SSC
public func sessionKeys(data : [UInt8] ) throws -> ([UInt8], [UInt8], [UInt8]) {
Log.verbose("Decrypt and verify received data and compare received RND.IFD with generated RND.IFD \(binToHexRep(self.ksmac))" )
let response = tripleDESDecrypt(key: self.ksenc, message: [UInt8](data[0..<32]), iv: [0,0,0,0,0,0,0,0] )
let response_kicc = [UInt8](response[16..<32])
let Kseed = xor(self.kifd, response_kicc)
Log.verbose("Calculate XOR of Kifd and Kicc")
Log.verbose("\tKseed: \(binToHexRep(Kseed))" )
let smskg = SecureMessagingSessionKeyGenerator()
let KSenc = try smskg.deriveKey(keySeed: Kseed, mode: .ENC_MODE)
let KSmac = try smskg.deriveKey(keySeed: Kseed, mode: .MAC_MODE)
// let KSenc = self.keyDerivation(kseed: Kseed,c: KENC)
// let KSmac = self.keyDerivation(kseed: Kseed,c: KMAC)
Log.verbose("Calculate Session Keys (KSenc and KSmac) using Appendix 5.1")
Log.verbose("\tKSenc: \(binToHexRep(KSenc))" )
Log.verbose("\tKSmac: \(binToHexRep(KSmac))" )
let ssc = [UInt8](self.rnd_icc.suffix(4) + self.rnd_ifd.suffix(4))
Log.verbose("Calculate Send Sequence Counter")
Log.verbose("\tSSC: \(binToHexRep(ssc))" )
return (KSenc, KSmac, ssc)
}
}
#endif

View File

@@ -0,0 +1,223 @@
//
// ChipAuthenticationHandler.swift
// NFCPassportReader
//
// Created by Andy Qua on 25/02/2021.
//
import Foundation
import OpenSSL
#if !os(macOS)
import CoreNFC
import CryptoKit
@available(iOS 15, *)
class ChipAuthenticationHandler {
private static let NO_PACE_KEY_REFERENCE : UInt8 = 0x00
private static let ENC_MODE : UInt8 = 0x1
private static let MAC_MODE : UInt8 = 0x2
private static let PACE_MODE : UInt8 = 0x3
private static let COMMAND_CHAINING_CHUNK_SIZE = 224
var tagReader : TagReader?
var gaSegments = [[UInt8]]()
var chipAuthInfos = [Int:ChipAuthenticationInfo]()
var chipAuthPublicKeyInfos = [ChipAuthenticationPublicKeyInfo]()
var isChipAuthenticationSupported : Bool = false
public init(dg14 : DataGroup14, tagReader: TagReader) {
self.tagReader = tagReader
for secInfo in dg14.securityInfos {
if let cai = secInfo as? ChipAuthenticationInfo {
let keyId = cai.getKeyId()
chipAuthInfos[keyId] = cai
} else if let capki = secInfo as? ChipAuthenticationPublicKeyInfo {
chipAuthPublicKeyInfos.append(capki)
}
}
if chipAuthPublicKeyInfos.count > 0 {
isChipAuthenticationSupported = true
}
}
public func doChipAuthentication() async throws {
Log.info( "Performing Chip Authentication - number of public keys found - \(chipAuthPublicKeyInfos.count)" )
guard isChipAuthenticationSupported else {
throw NFCPassportReaderError.NotYetSupported( "ChipAuthentication not supported" )
}
var success = false
for pubKey in chipAuthPublicKeyInfos {
do {
success = try await self.doChipAuthentication( with: pubKey)
if success {
break
}
} catch {
// try next key
}
}
if !success {
throw NFCPassportReaderError.ChipAuthenticationFailed
}
}
private func doChipAuthentication( with chipAuthPublicKeyInfo : ChipAuthenticationPublicKeyInfo ) async throws -> Bool {
// So it turns out that some passports don't have ChipAuthInfo items.
// So if we do have a ChipAuthInfo the we take the keyId (if present) and OID from there,
// BUT if we don't then we will try to infer the OID from the public key
let keyId = chipAuthPublicKeyInfo.keyId
let chipAuthInfoOID : String
if let chipAuthInfo = chipAuthInfos[keyId ?? 0] {
chipAuthInfoOID = chipAuthInfo.oid
} else {
if let oid = inferOID( fromPublicKeyOID:chipAuthPublicKeyInfo.oid) {
chipAuthInfoOID = oid
} else {
return false
}
}
try await self.doCA( keyId: keyId, encryptionDetailsOID: chipAuthInfoOID, publicKey: chipAuthPublicKeyInfo.pubKey )
return true
}
/// Infer OID from public key type - Best guess seems to be to use 3DES_CBC_CBC for both ECDH and DH keys
/// Apparently works for French passports
private func inferOID(fromPublicKeyOID: String ) -> String? {
if fromPublicKeyOID == SecurityInfo.ID_PK_ECDH_OID {
Log.warning("No ChipAuthenticationInfo - guessing its id-CA-ECDH-3DES-CBC-CBC");
return SecurityInfo.ID_CA_ECDH_3DES_CBC_CBC_OID
} else if fromPublicKeyOID == SecurityInfo.ID_PK_DH_OID {
Log.warning("No ChipAuthenticationInfo - guessing its id-CA-DH-3DES-CBC-CBC");
return SecurityInfo.ID_CA_DH_3DES_CBC_CBC_OID
}
Log.warning("No ChipAuthenticationInfo and unsupported ChipAuthenticationPublicKeyInfo public key OID \(fromPublicKeyOID)")
return nil;
}
private func doCA( keyId: Int?, encryptionDetailsOID oid: String, publicKey: OpaquePointer) async throws {
// Generate Ephemeral Keypair from parameters from DG14 Public key
// This should work for both EC and DH keys
var ephemeralKeyPair : OpaquePointer? = nil
let pctx = EVP_PKEY_CTX_new(publicKey, nil)
EVP_PKEY_keygen_init(pctx)
EVP_PKEY_keygen(pctx, &ephemeralKeyPair)
EVP_PKEY_CTX_free(pctx)
// Send the public key to the passport
try await sendPublicKey(oid: oid, keyId: keyId, pcdPublicKey: ephemeralKeyPair!)
Log.debug( "Public Key successfully sent to passport!" )
// Use our ephemeral private key and the passports public key to generate a shared secret
// (the passport with do the same thing with their private key and our public key)
let sharedSecret = OpenSSLUtils.computeSharedSecret(privateKeyPair:ephemeralKeyPair!, publicKey:publicKey)
// Now try to restart Secure Messaging using the new shared secret and
try restartSecureMessaging( oid : oid, sharedSecret : sharedSecret, maxTranceiveLength : 1, shouldCheckMAC : true)
}
private func sendPublicKey(oid : String, keyId : Int?, pcdPublicKey : OpaquePointer) async throws {
let cipherAlg = try ChipAuthenticationInfo.toCipherAlgorithm(oid: oid)
guard let keyData = OpenSSLUtils.getPublicKeyData(from: pcdPublicKey) else {
throw NFCPassportReaderError.InvalidDataPassed("Unable to get public key data from public key" )
}
if cipherAlg.hasPrefix("DESede") {
var idData : [UInt8] = []
if let keyId = keyId {
idData = intToBytes( val:keyId, removePadding:true)
idData = wrapDO( b:0x84, arr:idData)
}
let wrappedKeyData = wrapDO( b:0x91, arr:keyData)
_ = try await self.tagReader?.sendMSEKAT(keyData: Data(wrappedKeyData), idData: Data(idData))
} else if cipherAlg.hasPrefix("AES") {
_ = try await self.tagReader?.sendMSESetATIntAuth(oid: oid, keyId: keyId)
let data = wrapDO(b: 0x80, arr:keyData)
gaSegments = self.chunk(data: data, segmentSize: ChipAuthenticationHandler.COMMAND_CHAINING_CHUNK_SIZE )
try await self.handleGeneralAuthentication()
} else {
throw NFCPassportReaderError.InvalidDataPassed("Cipher Algorithm \(cipherAlg) not supported")
}
}
private func handleGeneralAuthentication() async throws {
repeat {
// Pull next segment from list
let segment = gaSegments.removeFirst()
let isLast = gaSegments.isEmpty
// send it
_ = try await self.tagReader?.sendGeneralAuthenticate(data: segment, isLast: isLast)
} while ( !gaSegments.isEmpty )
}
private func restartSecureMessaging( oid : String, sharedSecret : [UInt8], maxTranceiveLength : Int, shouldCheckMAC : Bool) throws {
let cipherAlg = try ChipAuthenticationInfo.toCipherAlgorithm(oid: oid)
let keyLength = try ChipAuthenticationInfo.toKeyLength(oid: oid)
// Start secure messaging.
let smskg = SecureMessagingSessionKeyGenerator()
let ksEnc = try smskg.deriveKey(keySeed: sharedSecret, cipherAlgName: cipherAlg, keyLength: keyLength, mode: .ENC_MODE)
let ksMac = try smskg.deriveKey(keySeed: sharedSecret, cipherAlgName: cipherAlg, keyLength: keyLength, mode: .MAC_MODE)
let ssc = withUnsafeBytes(of: 0.bigEndian, Array.init)
if (cipherAlg.hasPrefix("DESede")) {
Log.info( "Restarting secure messaging using DESede encryption")
let sm = SecureMessaging(encryptionAlgorithm: .DES, ksenc: ksEnc, ksmac: ksMac, ssc: ssc)
tagReader?.secureMessaging = sm
} else if (cipherAlg.hasPrefix("AES")) {
Log.info( "Restarting secure messaging using AES encryption")
let sm = SecureMessaging(encryptionAlgorithm: .AES, ksenc: ksEnc, ksmac: ksMac, ssc: ssc)
tagReader?.secureMessaging = sm
} else {
Log.error( "Not restarting secure messaging as unsupported cipher algorithm requested - \(cipherAlg)")
throw NFCPassportReaderError.InvalidDataPassed("Unsupported cipher algorithm \(cipherAlg)" )
}
}
func inferDigestAlgorithmFromCipherAlgorithmForKeyDerivation( cipherAlg : String, keyLength : Int) throws -> String {
if cipherAlg == "DESede" || cipherAlg == "AES-128" {
return "SHA1"
}
if cipherAlg == "AES" && keyLength == 128 {
return "SHA1"
}
if cipherAlg == "AES-256" || cipherAlg == "AES-192" {
return "SHA256"
}
if cipherAlg == "AES" && (keyLength == 192 || keyLength == 256) {
return "SHA256"
}
throw NFCPassportReaderError.InvalidDataPassed("Unsupported cipher algorithm or key length")
}
/// Chunks up a byte array into a number of segments of the given size,
/// and a final segment if there is a remainder.
/// - Parameter segmentSize the number of bytes per segment
/// - Parameter data the data to be partitioned
/// - Parameter a list with the segments
func chunk( data : [UInt8], segmentSize: Int ) -> [[UInt8]] {
return stride(from: 0, to: data.count, by: segmentSize).map {
Array(data[$0 ..< Swift.min($0 + segmentSize, data.count)])
}
}
}
#endif

View File

@@ -1,3 +1,11 @@
//
// DataGroupHash.swift
// NFCPassportReader
//
// Created by Andy Qua on 09/02/2021.
// Copyright © 2021 Andy Qua. All rights reserved.
//
@available(iOS 13, macOS 10.15, *)
public struct DataGroupHash {
public var id: String

View File

@@ -1,3 +1,9 @@
//
// DataGroupParser.swift
//
// Created by Andy Qua on 14/06/2019.
//
import OpenSSL
@available(iOS 13, macOS 10.15, *)

View File

@@ -1,3 +1,11 @@
//
// Errors.swift
// NFCPassportReader
//
// Created by Andy Qua on 09/02/2021.
// Copyright © 2021 Andy Qua. All rights reserved.
//
import Foundation
// MARK: TagError

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="AwesomeProject" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="GJd-Yh-RWb">
<rect key="frame" x="0.0" y="202" width="375" height="43"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Powered by React Native" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="MN2-I3-ftu">
<rect key="frame" x="0.0" y="626" width="375" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstItem="Bcu-3y-fUS" firstAttribute="bottom" secondItem="MN2-I3-ftu" secondAttribute="bottom" constant="20" id="OZV-Vh-mqD"/>
<constraint firstItem="Bcu-3y-fUS" firstAttribute="centerX" secondItem="GJd-Yh-RWb" secondAttribute="centerX" id="Q3B-4B-g5h"/>
<constraint firstItem="MN2-I3-ftu" firstAttribute="centerX" secondItem="Bcu-3y-fUS" secondAttribute="centerX" id="akx-eg-2ui"/>
<constraint firstItem="MN2-I3-ftu" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" id="i1E-0Y-4RG"/>
<constraint firstItem="GJd-Yh-RWb" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="bottom" multiplier="1/3" constant="1" id="moa-c2-u7t"/>
<constraint firstItem="GJd-Yh-RWb" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" symbolic="YES" id="x7j-FC-K8j"/>
</constraints>
<viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="52.173913043478265" y="375"/>
</scene>
</scenes>
</document>

View File

@@ -1,3 +1,11 @@
//
// Logging.swift
// NFCTest
//
// Created by Andy Qua on 11/06/2019.
// Copyright © 2019 Andy Qua. All rights reserved.
//
import Foundation
// TODO: Quick log functions - will move this to something better

View File

@@ -1,3 +1,11 @@
//
// NFCPassportModel.swift
// NFCPassportReader
//
// Created by Andy Qua on 29/10/2019.
//
import Foundation
#if os(iOS)