add TinyBERTLV and ApplicationInfo

This commit is contained in:
Michele Balistreri 2019-07-08 14:34:51 +03:00
parent d9b0d4aac6
commit 11ddedc9ce
4 changed files with 188 additions and 0 deletions

View File

@ -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 } }
}

View File

@ -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
}
}

View File

@ -1,3 +1,9 @@
class SecureChannel {
func generateSecret(pubKey: [UInt8]) {
}
func reset() {
}
}

View File

@ -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)
}
}
}