From 11ddedc9cef9c470c344cf61167a48d39e7e8c90 Mon Sep 17 00:00:00 2001 From: Michele Balistreri Date: Mon, 8 Jul 2019 14:34:51 +0300 Subject: [PATCH] add TinyBERTLV and ApplicationInfo --- Sources/Keycard/ApplicationInfo.swift | 71 ++++++++++++++++++++ Sources/Keycard/KeycardCommandSet.swift | 23 +++++++ Sources/Keycard/SecureChannel.swift | 6 ++ Sources/Keycard/TinyBERTLV.swift | 88 +++++++++++++++++++++++++ 4 files changed, 188 insertions(+) create mode 100644 Sources/Keycard/ApplicationInfo.swift create mode 100644 Sources/Keycard/TinyBERTLV.swift diff --git a/Sources/Keycard/ApplicationInfo.swift b/Sources/Keycard/ApplicationInfo.swift new file mode 100644 index 0000000..99115e6 --- /dev/null +++ b/Sources/Keycard/ApplicationInfo.swift @@ -0,0 +1,71 @@ +enum AppInfoTag: UInt8 { + case template = 0x04 + case pubKey = 0x80 + case uid = 0x8f + case keyUID = 0x8e + case capabilities = 0x8d +} + +enum AppCapability: UInt8 { + case secureChannel = 0x01 + case keyManagement = 0x02 + case credentialsManagement = 0x04 + case ndef = 0x08 + case all = 0x0f +} + +struct ApplicationInfo { + let instanceUID: [UInt8] + let freePairingSlots : Int + let appVersion : UInt16 + let keyUID: [UInt8] + let secureChannelPubKey: [UInt8] + let initializedCard: Bool + let capabilities: UInt8 + + init(_ data: [UInt8]) throws { + let tlv = TinyBERTLV(data) + let topTag = try tlv.readTag() + tlv.unreadLastTag() + + if (topTag == AppInfoTag.pubKey.rawValue) { + secureChannelPubKey = try tlv.readPrimitive(tag: AppInfoTag.pubKey.rawValue) + initializedCard = false + + if (secureChannelPubKey.count > 0) { + capabilities = AppCapability.credentialsManagement.rawValue | AppCapability.secureChannel.rawValue + } else { + capabilities = AppCapability.credentialsManagement.rawValue + } + + instanceUID = [] + freePairingSlots = 0 + appVersion = 0 + keyUID = [] + + return + } + + _ = try tlv.enterConstructed(tag: AppInfoTag.template.rawValue) + instanceUID = try tlv.readPrimitive(tag: AppInfoTag.uid.rawValue) + secureChannelPubKey = try tlv.readPrimitive(tag: AppInfoTag.pubKey.rawValue) + appVersion = try UInt16(tlv.readInt()) + freePairingSlots = try tlv.readInt() + keyUID = try tlv.readPrimitive(tag: AppInfoTag.keyUID.rawValue) + + do { + capabilities = try tlv.readPrimitive(tag: AppInfoTag.capabilities.rawValue)[0] + } catch TLVError.endOfTLV { + capabilities = AppCapability.all.rawValue + } + + initializedCard = true + } + + var appVersionString: String { get { "\(appVersion >> 8).\(appVersion & 0xff)" } } + var hasMasterKey: Bool { get { keyUID.count > 0 } } + var hasSecureChannelCapability: Bool { get { (capabilities & AppCapability.secureChannel.rawValue) != 0 } } + var hasKeyManagementCapability: Bool { get { (capabilities & AppCapability.keyManagement.rawValue) != 0 } } + var hasCredentialsManagementCapability: Bool { get { (capabilities & AppCapability.credentialsManagement.rawValue) != 0 } } + var hasNDEFCapability: Bool { get { (capabilities & AppCapability.ndef.rawValue) != 0 } } +} diff --git a/Sources/Keycard/KeycardCommandSet.swift b/Sources/Keycard/KeycardCommandSet.swift index 9af9042..e70fcd7 100644 --- a/Sources/Keycard/KeycardCommandSet.swift +++ b/Sources/Keycard/KeycardCommandSet.swift @@ -1,3 +1,26 @@ class KeycardCommandSet { + let cardChannel: CardChannel + let secureChannel: SecureChannel + var info: ApplicationInfo? + init(cardChannel: CardChannel) { + self.cardChannel = cardChannel + self.secureChannel = SecureChannel() + } + + func select(instanceIdx: UInt8 = 1) throws -> APDUResponse { + let selectApplet: APDUCommand = APDUCommand(cla: 0x00, ins: 0xA4, p1: 0x04, p2: 0x00, data: Identifier.getKeycardInstanceAID(instanceId: instanceIdx)) + let resp: APDUResponse = try cardChannel.send(selectApplet) + + if resp.sw == StatusWord.ok.rawValue { + info = try ApplicationInfo(resp.data) + + if (info!.hasSecureChannelCapability) { + secureChannel.generateSecret(pubKey: info!.secureChannelPubKey) + secureChannel.reset() + } + } + + return resp + } } diff --git a/Sources/Keycard/SecureChannel.swift b/Sources/Keycard/SecureChannel.swift index 50ce058..7ab19bd 100644 --- a/Sources/Keycard/SecureChannel.swift +++ b/Sources/Keycard/SecureChannel.swift @@ -1,3 +1,9 @@ class SecureChannel { + func generateSecret(pubKey: [UInt8]) { + + } + func reset() { + + } } diff --git a/Sources/Keycard/TinyBERTLV.swift b/Sources/Keycard/TinyBERTLV.swift new file mode 100644 index 0000000..9d9cd8f --- /dev/null +++ b/Sources/Keycard/TinyBERTLV.swift @@ -0,0 +1,88 @@ +enum TLVTag: UInt8 { + case int = 0x01 + case bool = 0x02 +} + +enum TLVError: Error { + case unexpectedTag(expected: UInt8, actual: UInt8) + case unexpectedLength(length: Int) + case endOfTLV +} + +class TinyBERTLV { + let buf: [UInt8] + var pos: Int + + init(_ buf: [UInt8]) { + self.buf = buf + self.pos = 0 + } + + func enterConstructed(tag: UInt8) throws -> Int { + try checkTag(expected: tag, actual: readTag()) + return readLength() + } + + func readPrimitive(tag: UInt8) throws -> [UInt8] { + try checkTag(expected: tag, actual: readTag()) + let len = readLength() + pos += len + return Array(buf[Int((pos - len))...pos]) + } + + func readBoolean() throws -> Bool { + let val = try readPrimitive(tag: TLVTag.bool.rawValue) + return val[0] == 0xff + } + + func readInt() throws -> Int { + let val = try readPrimitive(tag: TLVTag.int.rawValue) + switch val.count { + case 1: + return Int(val[0]) + case 2: + return (Int(val[0]) << 8) | Int(val[1]) + case 3: + return (Int(val[0]) << 16) | (Int(val[1] << 8)) | Int(val[2]) + case 4: + return (Int(val[0]) << 24) | (Int(val[1] << 16)) | (Int(val[2] << 8)) | Int(val[3]) + default: + throw TLVError.unexpectedLength(length: val.count) + } + } + + func readLength() -> Int { + var len = Int(buf[pos]) + pos += 1 + + if (len == 0x81) { + len = Int(buf[pos]) + pos += 1 + } + + return len + } + + func readTag() throws -> UInt8 { + if (pos < buf.count) { + let ret = buf[pos] + pos += 1 + return ret + } else { + throw TLVError.endOfTLV + } + } + + func unreadLastTag() { + if (pos < buf.count) { + pos -= 1; + } + } + + func checkTag(expected: UInt8, actual: UInt8) throws { + if (expected != actual) { + unreadLastTag() + throw TLVError.unexpectedTag(expected: expected, actual: actual) + } + } +}