update to applet version 3.0
This commit is contained in:
parent
096ace0535
commit
9d74eb1fe0
|
@ -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' }
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue