update to applet version 3.0

This commit is contained in:
Michele Balistreri 2019-10-16 17:14:00 +03:00
parent 096ace0535
commit 9d74eb1fe0
6 changed files with 77 additions and 108 deletions

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |spec|
spec.name = 'UB'
spec.version = '0.0.0'
spec.name = 'Keycard'
spec.version = '3.0.0'
spec.authors = {'Bitgamma' => 'opensource@bitgamma.com'}
spec.homepage = 'https://github.com/status-im/Keycard.swift'
spec.license = { :type => 'Apache' }

View File

@ -1,81 +0,0 @@
class CardDuplicator {
let secret: [UInt8]
let cmdSet: KeycardCommandSet
let delegate: DuplicatorDelegate?
var startedDuplication: Set<[UInt8]>
var addedEntropy: Set<[UInt8]>
var finishedDuplication: Set<[UInt8]>
init(cmdSet: KeycardCommandSet, delegate: DuplicatorDelegate?) {
self.cmdSet = cmdSet
self.delegate = delegate
self.secret = Crypto.shared.random(count: 32)
self.startedDuplication = Set()
self.addedEntropy = Set()
self.finishedDuplication = Set()
}
convenience init(channel: CardChannel) {
self.init(cmdSet: KeycardCommandSet(cardChannel: channel), delegate: nil)
}
func selectAndCheck(processed: inout Set<[UInt8]>) throws -> ApplicationInfo {
let appInfo = try ApplicationInfo(cmdSet.select().checkOK().data)
let (inserted, _) = processed.insert(appInfo.instanceUID)
if !inserted {
throw CardError.invalidState
}
return appInfo
}
func preamble(processed: inout Set<[UInt8]>) throws {
let appInfo = try selectAndCheck(processed: &processed)
let pairing = delegate!.getPairing(forApplication: appInfo)
if pairing == nil {
throw CardError.notPaired
}
cmdSet.pairing = pairing
try cmdSet.autoOpenSecureChannel()
let appStatus = try ApplicationStatus(cmdSet.getStatus(info: GetStatusP1.application.rawValue).checkOK().data)
var remainingAttempts = appStatus.pinRetryCount
while remainingAttempts > 0 {
do {
_ = try cmdSet.verifyPIN(pin: delegate!.getPIN(forApplication: appInfo, withRemainingAttempts: remainingAttempts))
} catch CardError.wrongPIN(let retryCount) {
remainingAttempts = retryCount
}
}
if remainingAttempts <= 0 {
throw CardError.pinBlocked
}
}
func startDuplication(clientCount: UInt8) throws {
try preamble(processed: &startedDuplication)
_ = try cmdSet.duplicateKeyStart(entropyCount: clientCount, firstEntropy: self.secret).checkOK()
}
func exportKey() throws -> [UInt8] {
try preamble(processed: &finishedDuplication)
return try cmdSet.duplicateKeyExport().checkOK().data
}
func importKey(key: [UInt8]) throws -> [UInt8] {
try preamble(processed: &finishedDuplication)
return try cmdSet.duplicateKeyImport(key: key).checkOK().data
}
func addEntropy(clientCount: UInt8) throws {
_ = try selectAndCheck(processed: &addedEntropy)
_ = try cmdSet.duplicateKeyAddEntropy(entropy: self.secret).checkOK()
}
}

View File

@ -0,0 +1,33 @@
enum AppInfoTag: UInt8 {
case template = 0xA4
case pubKey = 0x80
case pubData = 0x82
}
public struct CashApplicationInfo {
public let secureChannelPubKey: [UInt8]
public let appVersion: UInt16
public let pubData: [UInt8]
public var appVersionString: String {
return "\(appVersion >> 8).\(appVersion & 0xff)"
}
public init(_ data: [UInt8]) throws {
let tlv = TinyBERTLV(data)
_ = try tlv.enterConstructed(tag: AppInfoTag.template.rawValue)
secureChannelPubKey = try tlv.readPrimitive(tag: AppInfoTag.pubKey.rawValue)
appVersion = try UInt16(tlv.readInt())
keyUID = try tlv.readPrimitive(tag: AppInfoTag.pubData.rawValue)
}
public init(secureChannelPubKey: [UInt8],
appVersion: UInt16,
pubData: [UInt8]) {
self.secureChannelPubKey = secureChannelPubKey
self.appVersion = appVersion
self.pubData = initializedCard
}
}

View File

@ -0,0 +1,18 @@
import Foundation
public class CashCommandSet {
let cardChannel: CardChannel
public func select() throws -> APDUResponse {
let selectApplet: APDUCommand = APDUCommand(cla: CLA.iso7816.rawValue, ins: ISO7816INS.select.rawValue, p1: 0x04, p2: 0x00, data: Identifier.keycardCashInstanceAID)
return try cardChannel.send(selectApplet)
}
public func sign(data: [UInt8]) throws -> APDUResponse {
Logger.shared.log("sign data=\(Data(data).toHexString())")
let cmd = APDUCommand(cla: CLA.proprietary.rawValue, ins: KeycardINS.sign.rawValue, p1: 0, p2: 0, data: data)
return try cardChannel.send(cmd)
}
}

View File

@ -10,7 +10,6 @@ enum ISO7816INS: UInt8 {
enum KeycardINS: UInt8 {
case initialize = 0xfe
case getStatus = 0xf2
case setNDEF = 0xf3
case verifyPIN = 0x20
case changePIN = 0x21
case unblockPIN = 0x22
@ -19,10 +18,11 @@ enum KeycardINS: UInt8 {
case generateMnemonic = 0xd2
case removeKey = 0xd3
case generateKey = 0xd4
case duplicateKey = 0xd5
case sign = 0xc0
case setPinlessPath = 0xc1
case exportKey = 0xc2
case getData = 0xca
case storeData = 0xe2
}
public enum ChangePINP1: UInt8 {
@ -93,6 +93,12 @@ public enum PairP1: UInt8 {
case lastStep = 0x01
}
public enum StoreDataP1: UInt8 {
case publicData = 0x00
case ndef = 0x01
case cash = 0x02
}
public enum Identifier: String {
case packageAID = "A0000008040001"
case keycardAID = "A000000804000101"

View File

@ -82,7 +82,22 @@ public class KeycardCommandSet {
}
public func setNDEF(ndef: [UInt8]) throws -> APDUResponse {
let cmd = secureChannel.protectedCommand(cla: CLA.proprietary.rawValue, ins: KeycardINS.setNDEF.rawValue, p1: 0, p2: 0, data: ndef)
let len = (Int(ndef[0]) << 8) | Int(ndef[1])
if len != (ndef.count - 2) {
ndef = [UInt8(ndef.count >> 8), UInt8(ndef.count & 0xff)] + ndef
}
return try storeData(data: ndef, type: StoreDataP1.ndef.rawValue)
}
public func storeData(data: [UInt8], type: UInt8) throws -> APDUResponse {
let cmd = secureChannel.protectedCommand(cla: CLA.proprietary.rawValue, ins: KeycardINS.storeData.rawValue, p1: type, p2: 0, data: data)
return try secureChannel.transmit(channel: cardChannel, cmd: cmd)
}
public func getData(type: UInt8) throws -> APDUResponse {
let cmd = secureChannel.protectedCommand(cla: CLA.proprietary.rawValue, ins: KeycardINS.getData.rawValue, p1: type, p2: 0, data: data)
return try secureChannel.transmit(channel: cardChannel, cmd: cmd)
}
@ -154,28 +169,6 @@ public class KeycardCommandSet {
return try secureChannel.transmit(channel: cardChannel, cmd: cmd)
}
public func duplicateKeyStart(entropyCount: UInt8, firstEntropy: [UInt8]) throws -> APDUResponse {
try duplicateKey(p1: DuplicateKeyP1.start.rawValue, p2: entropyCount, data: firstEntropy)
}
public func duplicateKeyAddEntropy(entropy: [UInt8]) throws -> APDUResponse {
let cmd = APDUCommand(cla: CLA.proprietary.rawValue, ins: KeycardINS.duplicateKey.rawValue, p1: DuplicateKeyP1.addEntropy.rawValue, p2: 0, data: secureChannel.oneShotEncrypt(data: entropy))
return try secureChannel.transmit(channel: cardChannel, cmd: cmd)
}
public func duplicateKeyExport() throws -> APDUResponse {
try duplicateKey(p1: DuplicateKeyP1.exportKey.rawValue, p2: 0, data: [])
}
public func duplicateKeyImport(key: [UInt8]) throws -> APDUResponse {
try duplicateKey(p1: DuplicateKeyP1.importKey.rawValue, p2: 0, data: key)
}
public func duplicateKey(p1: UInt8, p2: UInt8, data: [UInt8]) throws -> APDUResponse {
let cmd = secureChannel.protectedCommand(cla: CLA.proprietary.rawValue, ins: KeycardINS.duplicateKey.rawValue, p1: p1, p2: p2, data: data)
return try secureChannel.transmit(channel: cardChannel, cmd: cmd)
}
public func sign(hash: [UInt8]) throws -> APDUResponse {
Logger.shared.log("sign hash=\(Data(hash).toHexString())")
return try sign(p1: SignP1.currentKey.rawValue, data: hash)