Skip to content

Commit

Permalink
Hide send(apdu: APDU) -> Response from the public parts of the framew…
Browse files Browse the repository at this point in the history
…ork. This function should only be used by the sendRecursive() function in Connection+Extensions.swift. Users of the SDK should use send(apdu: APDU) -> Data instead.
  • Loading branch information
jensutbult committed Nov 22, 2023
1 parent 21d23c7 commit 34610c6
Show file tree
Hide file tree
Showing 8 changed files with 30 additions and 22 deletions.
10 changes: 9 additions & 1 deletion YubiKit/YubiKit/Connection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,20 @@ public protocol Connection: AnyObject {
/// Send a APDU to the Connection. Commands that need multiple reads from the YubiKey will
/// be handled automatically and returning a Response only when all data has been read from the
/// YubiKey.
func send(apdu: APDU) async throws -> Response // TODO: this should probably only return the Data since the code will always be 0x9100
func send(apdu: APDU) async throws -> Data
}

internal protocol InternalConnection {

func session() async -> Session?
func setSession(_ session: Session?) async

// The internal version of the send() function returns a Response instead of Data. The reason for this is
// to handle reads of large chunks of data that will be split into multiple reads. If the result is
// to large for a single read that is signaled by sw1 being 0x61. The Response struct will return both
// the data and sw1 and sw2. sendRecursive() in Connection+Extensions will look at the sw1 code and if
// it indicates there's more data to read, it will call itself recursivly to retrieve the next chunk of data.
func send(apdu: APDU) async throws -> Response
}

extension InternalConnection {
Expand Down
2 changes: 1 addition & 1 deletion YubiKit/YubiKit/LightningConnection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public final actor LightningConnection: Connection, InternalConnection {
}
}

public func send(apdu: APDU) async throws -> Response {
internal func send(apdu: APDU) async throws -> Response {
print("⚡️ LightningConnection, send() \(apdu).")
guard let accessoryConnection, let outputStream = accessoryConnection.session.outputStream, let inputStream = accessoryConnection.session.inputStream else { throw "No current session" }
var data = Data([0x00]) // YLP iAP2 Signal
Expand Down
4 changes: 2 additions & 2 deletions YubiKit/YubiKit/Management/ManagementSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public final actor ManagementSession: Session, InternalSession {
public func getDeviceInfo() async throws -> DeviceInfo {
guard let connection else { throw SessionError.noConnection }
let apdu = APDU(cla: 0, ins: 0x1d, p1: 0, p2: 0)
let data: Data = try await connection.send(apdu: apdu)
let data = try await connection.send(apdu: apdu)
return try DeviceInfo(withData: data, fallbackVersion: version)
}

Expand Down Expand Up @@ -110,7 +110,7 @@ public final actor ManagementSession: Session, InternalSession {
command.append(tlv.data)

let apdu = APDU(cla: 0, ins: 0x1c, p1: 0, p2: 0, command: command)
let _: Data = try await connection.send(apdu: apdu)
let _ = try await connection.send(apdu: apdu)
}

public func disableApplication(_ application: ApplicationType, overTransport transport: DeviceTransport, reboot: Bool = false) async throws {
Expand Down
3 changes: 1 addition & 2 deletions YubiKit/YubiKit/NFCConnection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,7 @@ public final actor NFCConnection: Connection, InternalConnection {
}
}

// Send apdu over connection
public func send(apdu: APDU) async throws -> Response {
internal func send(apdu: APDU) async throws -> Response {
print("🛜 NFCConnection, send(apdu: \(apdu))")
guard let tag else { throw "No NFC tag" }
guard let apdu = apdu.nfcIso7816Apdu else { throw "Malformed APDU data" }
Expand Down
18 changes: 9 additions & 9 deletions YubiKit/YubiKit/OATH/OATHSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public final class OATHSession: Session, InternalSession {
guard let connection = _connection else { throw SessionError.noConnection }
print("Reset OATH application")
let apdu = APDU(cla: 0, ins: 0x04, p1: 0xde, p2: 0xad)
let _: Data = try await connection.send(apdu: apdu)
let _ = try await connection.send(apdu: apdu)
selectResponse = try await Self.selectApplication(withConnection: connection)
}

Expand Down Expand Up @@ -143,21 +143,21 @@ public final class OATHSession: Session, InternalSession {
}

let apdu = APDU(cla: 0x00, ins: 0x01, p1: 0x00, p2: 0x00, command: data)
let _: Data = try await connection.send(apdu: apdu)
let _ = try await connection.send(apdu: apdu)
return nil
}

public func deleteCredential(_ credential: Credential) async throws {
guard let connection = _connection else { throw SessionError.noConnection }
let deleteTlv = TKBERTLVRecord(tag: 0x71, value: credential.id)
let apdu = APDU(cla: 0, ins: 0x02, p1: 0, p2: 0, command: deleteTlv.data)
let _: Data = try await connection.send(apdu: apdu)
let _ = try await connection.send(apdu: apdu)
}

public func listCredentials() async throws -> [Credential] {
guard let connection = _connection else { throw SessionError.noConnection }
let apdu = APDU(cla: 0, ins: 0xa1, p1: 0, p2: 0)
let data: Data = try await connection.send(apdu: apdu)
let data = try await connection.send(apdu: apdu)
guard let result = TKBERTLVRecord.sequenceOfRecords(from: data) else { throw OATHSessionError.responseDataNotTLVFormatted }
return try result.map {
guard $0.tag == 0x72 else { throw OATHSessionError.unexpectedTag }
Expand Down Expand Up @@ -198,7 +198,7 @@ public final class OATHSession: Session, InternalSession {
let nameTLV = TKBERTLVRecord(tag: tagName, value: credential.id)
let apdu = APDU(cla: 0x00, ins: 0xa2, p1: 0, p2: 1, command: nameTLV.data + challengeTLV.data, type: .extended)

let data: Data = try await connection.send(apdu: apdu)
let data = try await connection.send(apdu: apdu)
guard let result = TKBERTLVRecord.init(from: data) else { throw OATHSessionError.responseDataNotTLVFormatted }

guard let digits = result.value.first else { throw OATHSessionError.unexpectedData }
Expand All @@ -215,7 +215,7 @@ public final class OATHSession: Session, InternalSession {
let challengeTLV = TKBERTLVRecord(tag: tagChallenge, value: bigChallenge.data)
let apdu = APDU(cla: 0x00, ins: 0xa4, p1: 0x00, p2: 0x01, command: challengeTLV.data)
guard let connection = _connection else { throw SessionError.noConnection }
let data: Data = try await connection.send(apdu: apdu)
let data = try await connection.send(apdu: apdu)
guard let result = TKBERTLVRecord.sequenceOfRecords(from: data)?.tuples() else { throw OATHSessionError.responseDataNotTLVFormatted }

return try await result.asyncMap { (name, response) in
Expand Down Expand Up @@ -273,7 +273,7 @@ public final class OATHSession: Session, InternalSession {
let response = challenge.hmacSha1(usingKey: accessKey)
let responseTlv = TKBERTLVRecord(tag: tagSetCodeResponse, value: response)
let apdu = APDU(cla: 0, ins: 0x03, p1: 0, p2: 0, command: keyTlv.data + challengeTlv.data + responseTlv.data)
let _: Data = try await connection.send(apdu: apdu)
let _ = try await connection.send(apdu: apdu)
}

public func unlockWithAccessKey(_ accessKey: Data) async throws {
Expand All @@ -284,8 +284,8 @@ public final class OATHSession: Session, InternalSession {
let apdu = APDU(cla: 0, ins: 0xa3, p1: 0, p2: 0, command: reponseTlv.data + challengeTlv.data)

do {
let result: Data = try await connection.send(apdu: apdu)
guard let resultTlv = TKBERTLVRecord(from: result), resultTlv.tag == tagSetCodeResponse else {
let data = try await connection.send(apdu: apdu)
guard let resultTlv = TKBERTLVRecord(from: data), resultTlv.tag == tagSetCodeResponse else {
throw OATHSessionError.unexpectedTag
}
let expectedResult = challenge.hmacSha1(usingKey: accessKey)
Expand Down
2 changes: 1 addition & 1 deletion YubiKit/YubiKit/Response.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import Foundation

public struct Response: CustomStringConvertible {
internal struct Response: CustomStringConvertible {

internal init(rawData: Data) {
if rawData.count > 2 {
Expand Down
3 changes: 1 addition & 2 deletions YubiKit/YubiKit/SmartCardConnection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,7 @@ public final actor SmartCardConnection: Connection, InternalConnection {
}
}

// Send apdu over connection
public func send(apdu: APDU) async throws -> Response {
internal func send(apdu: APDU) async throws -> Response {
print("🪪 SmartCardConnection, send(apdu: \(apdu))")
guard let smartCard else { throw "No SmartCard connection" }
let data = try await smartCard.transmit(apdu.data)
Expand Down
10 changes: 6 additions & 4 deletions YubiKit/YubiKit/Utilities/Connection+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ enum Application {
extension Connection {

func selectApplication(application: Application) async throws -> Data {
let response: Response = try await send(apdu: application.selectApplicationAPDU)
guard let internalConnection = self as? InternalConnection else { fatalError() }
let response: Response = try await internalConnection.send(apdu: application.selectApplicationAPDU)
switch response.statusCode {
case .ok:
return response.data
Expand All @@ -48,7 +49,7 @@ extension Connection {
}
}

func send(apdu: APDU) async throws -> Data {
public func send(apdu: APDU) async throws -> Data {
return try await sendRecursive(apdu: apdu)
}

Expand All @@ -64,11 +65,12 @@ extension Connection {
ins = 0xc0
}

guard let internalConnection = self as? InternalConnection else { fatalError() }
if readMoreData {
let apdu = APDU(cla: 0, ins: ins, p1: 0, p2: 0, command: nil, type: .short)
response = try await send(apdu: apdu)
response = try await internalConnection.send(apdu: apdu)
} else {
response = try await send(apdu: apdu)
response = try await internalConnection.send(apdu: apdu)
}

guard response.statusCode == .ok || response.statusCode == .moreData else {
Expand Down

0 comments on commit 34610c6

Please sign in to comment.