add TinyBERTLV and ApplicationInfo
This commit is contained in:
parent
d9b0d4aac6
commit
11ddedc9ce
|
@ -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 } }
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
class SecureChannel {
|
||||
func generateSecret(pubKey: [UInt8]) {
|
||||
|
||||
}
|
||||
|
||||
func reset() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue