Skip to content

Commit

Permalink
Throw proper errors instead of Strings. Documentation fixes.
Browse files Browse the repository at this point in the history
  • Loading branch information
jensutbult committed Nov 30, 2023
1 parent c65e5a3 commit da2775d
Show file tree
Hide file tree
Showing 11 changed files with 67 additions and 23 deletions.
2 changes: 1 addition & 1 deletion FullStackTests/Tests/ConnectionFullStackTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class ConnectionFullStackTests: XCTestCase {
XCTAssertNotNil(secondConnection)
let closingError = await task.value
XCTAssertNil(closingError)
print("✅ connectionDidClose() returned: \(closingError ?? "nil")")
print("✅ connectionDidClose() returned: \(String(describing: closingError))")
} catch {
XCTFail("🚨 Failed with: \(error)")
}
Expand Down
5 changes: 4 additions & 1 deletion FullStackTests/Tests/ManagementFullStackTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,16 @@ class ManagementFullStackTests: XCTestCase {
}

func testGetDeviceInfo() throws {
runManagementTest { _, session, _ in
runManagementTest { connection, session, _ in
let info = try await session.getDeviceInfo()
print(info)
print("PIV enabled over usb: \(info.config.isApplicationEnabled(.piv, overTransport: .usb))")
print("PIV enabled over nfc: \(info.config.isApplicationEnabled(.piv, overTransport: .nfc))")
print("PIV supported over usb: \(info.isApplicationSupported(.piv, overTransport: .usb))")
print("PIV supported over nfc: \(info.isApplicationSupported(.piv, overTransport: .nfc))")
#if os(iOS)
await connection.nfcConnection?.close(message: "Test successful!")
#endif
}
}

Expand Down
14 changes: 14 additions & 0 deletions YubiKit/YubiKit/Connection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,17 @@ extension InternalConnection {
}
}

/// Connection Errors.
public enum ConnectionError: Error {
/// No current connection.
case noConnection
/// Unexpected result returned from YubiKey.
case unexpectedResult
/// YubiKey did not return any data.
case missingResult
/// Awaiting call to connect() was cancelled.
case cancelled
/// Connection was closed.
case closed
}

14 changes: 7 additions & 7 deletions YubiKit/YubiKit/LightningConnection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,15 @@ public final actor LightningConnection: Connection, InternalConnection {
}

internal func send(apdu: APDU) async throws -> Response {
guard let accessoryConnection, let outputStream = accessoryConnection.session.outputStream, let inputStream = accessoryConnection.session.inputStream else { throw "No current session" }
guard let accessoryConnection, let outputStream = accessoryConnection.session.outputStream, let inputStream = accessoryConnection.session.inputStream else { throw ConnectionError.noConnection }
var data = Data([0x00]) // YLP iAP2 Signal
data.append(apdu.data)

try outputStream.writeToYubiKey(data: data)
while true {
try await Task.sleep(for: .seconds(commandProcessingTime))
let result = try inputStream.readFromYubiKey()
if result.isEmpty { throw "Empty result" }
if result.isEmpty { throw ConnectionError.missingResult }
let status = Response.StatusCode(data: result.subdata(in: result.count-2..<result.count))

// BUG #62 - Workaround for WTX == 0x01 while status is 0x9000 (success).
Expand All @@ -96,7 +96,7 @@ public final actor LightningConnection: Connection, InternalConnection {
} else if result.bytes[0] == 0x01 { // Remove the YLP key protocol header and the WTX
return Response(rawData: result.subdata(in: 4..<result.count))
}
throw "Wrong response"
throw ConnectionError.unexpectedResult
}
}
}
Expand Down Expand Up @@ -221,9 +221,9 @@ fileprivate class EAAccessoryWrapper: NSObject, StreamDelegate {
queue.async {
// Signal closure and cancel previous connection handlers
// Swap out old handlers to the new ones
self.closingHandler?("Closed by new call to connection()")
self.closingHandler?(ConnectionError.closed)
self.closingHandler = nil
self.connectingHandler?(.failure("⚡️ Cancelled by new call to connection()"))
self.connectingHandler?(.failure(ConnectionError.cancelled))
self.connectingHandler = handler

if self.state == .ready {
Expand Down Expand Up @@ -306,10 +306,10 @@ fileprivate class EAAccessoryWrapper: NSObject, StreamDelegate {
break;
case .scanning:
// should we call the closingHandler as well?
self.connectingHandler?(.failure("Cancelled from stop()"))
self.connectingHandler?(.failure(ConnectionError.cancelled))
case .connected(let connection):
connection.close()
self.closingHandler?("Closed by user")
self.closingHandler?(ConnectionError.closed)
}
self.connectingHandler = nil
self.closingHandler = nil
Expand Down
19 changes: 14 additions & 5 deletions YubiKit/YubiKit/NFCConnection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ public final actor NFCConnection: Connection, InternalConnection {
}

internal func send(apdu: APDU) async throws -> Response {
guard let tag else { throw "No NFC tag" }
guard let apdu = apdu.nfcIso7816Apdu else { throw "Malformed APDU data" }
guard let tag else { throw ConnectionError.noConnection }
guard let apdu = apdu.nfcIso7816Apdu else { throw NFCConnectionError.malformedAPDU }
let result: (Data, UInt8, UInt8) = try await tag.sendCommand(apdu: apdu)
return Response(data: result.0, sw1: result.1, sw2: result.2)
}
Expand Down Expand Up @@ -207,9 +207,9 @@ fileprivate class NFCTagWrapper: NSObject, NFCTagReaderSessionDelegate {

internal func connection(alertMessage message: String?, completion handler: @escaping (Result<TagReaderSession, Error>) -> Void) {
queue.async {
self.closingHandler?("Closed by new call to connection()")
self.closingHandler?(ConnectionError.closed)
self.closingHandler = nil
self.connectingHandler?(.failure("Cancelled by new call to connection()"))
self.connectingHandler?(.failure(ConnectionError.cancelled))
self.connectingHandler = handler

switch self.state {
Expand Down Expand Up @@ -288,14 +288,23 @@ fileprivate class NFCTagWrapper: NSObject, NFCTagReaderSessionDelegate {
self.connectingHandler?(.success(session))
} else {
Logger.nfc.debug("Failed connecting to \(String(describing: tag)) since it's not a iso7816 tag.")
self.connectingHandler?(.failure("Not an iso7816 tag!"))
self.connectingHandler?(.failure(NFCConnectionError.noISO7816Tag))
}
self.connectingHandler = nil
}
}
}
}


/// NFCConnection specific errors.
public enum NFCConnectionError: Error {
/// The tag you are trying to connect to is not a ISO7816 tag.
case noISO7816Tag
/// The APDU you are trying to send to the YubiKey over NFC is malformed.
case malformedAPDU
}

extension Connection {
/// Returns a NFCConnection if the connection is a NFCConnection. This is useful since NFCConnections need to be
/// closed as soon as you are done sending commands to them while wired connections are usually kept alive.
Expand Down
22 changes: 15 additions & 7 deletions YubiKit/YubiKit/SmartCardConnection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public final actor SmartCardConnection: Connection, InternalConnection {
}

internal func send(apdu: APDU) async throws -> Response {
guard let smartCard else { throw "No SmartCard connection" }
guard let smartCard else { throw ConnectionError.noConnection }
let data = try await smartCard.transmit(apdu.data)
return Response(rawData: data)
}
Expand Down Expand Up @@ -178,9 +178,9 @@ fileprivate class TKSmartCardWrapper {
queue.async {
// Signal closure and cancel previous connection handlers
// Swap out old handlers to the new ones
self.closingHandler?("Closed by new call to connection()")
self.closingHandler?(ConnectionError.closed)
self.closingHandler = nil
self.connectingHandler?(.failure("🪪 Cancelled by new call to connection()"))
self.connectingHandler?(.failure(ConnectionError.cancelled))
self.connectingHandler = handler

// If we're currently not monitoring or initating a session
Expand Down Expand Up @@ -230,7 +230,7 @@ fileprivate class TKSmartCardWrapper {
manager.getSlot(withName: slotName) { slot in
guard let slot else {
self.state = .ready
self.connectingHandler?(.failure("Failed to get a TKSmartCardSlot from TKSmartCardSlotManager."))
self.connectingHandler?(.failure(SmartCardConnectionError.getSmartCardSlotFailed))
self.connectingHandler = nil
return
}
Expand Down Expand Up @@ -286,7 +286,7 @@ fileprivate class TKSmartCardWrapper {
self?.observeKeyRemoval()
} else {
self?.state = .ready
self?.connectingHandler?(.failure("🪪 Failed but got no error!"))
self?.connectingHandler?(.failure(SmartCardConnectionError.beginSessionFailed))
self?.connectingHandler = nil
}
}
Expand All @@ -303,10 +303,10 @@ fileprivate class TKSmartCardWrapper {
break;
case .monitoring, .initatingSession:
// should we call the closingHandler as well?
self.connectingHandler?(.failure("Cancelled from stop()"))
self.connectingHandler?(.failure(ConnectionError.cancelled))
case .connected(let session):
session.endSession()
self.closingHandler?("Closed by user")
self.closingHandler?(ConnectionError.closed)
}
self.state = .ready
self.connectingHandler = nil
Expand All @@ -318,3 +318,11 @@ fileprivate class TKSmartCardWrapper {
}
}
}

/// SmartCardConnection specific errors
public enum SmartCardConnectionError: Error {
/// CryptoTokenKit failed to return a TKSmartCardSlot.
case getSmartCardSlotFailed
/// CryptoTokenKit failed to start a session for the TKSmartCard.
case beginSessionFailed
}
4 changes: 4 additions & 0 deletions YubiKit/YubiKit/YubiKit.docc/Resources/ConnectionExtension.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@
### Sending data to the YubiKey

- ``send(apdu:)``

### Errors

- ``ConnectionError``
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

- ``session(withConnection:)``
- ``end()``
- ``sessionDidEnd()``

### Running commands in the Management application

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,7 @@
### Sending data to the YubiKey

- ``Connection/send(apdu:)``

### Errors

- ``NFCConnectionError``
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
- ``session(withConnection:)``
- ``end()``
- ``reset()``
- ``sessionDidEnd()``

### Running commands in the OATH application

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@
### Sending data to the YubiKey

- ``Connection/send(apdu:)``

### Errors

- ``SmartCardConnectionError``

0 comments on commit da2775d

Please sign in to comment.