Skip to content

Commit

Permalink
Removed the sessionDidEnd() method from Session since it probably isn…
Browse files Browse the repository at this point in the history
…'t needed. Some clean up in session ending as well.
  • Loading branch information
jensutbult committed Nov 29, 2023
1 parent f799611 commit 39ac52e
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 50 deletions.
27 changes: 7 additions & 20 deletions YubiKit/YubiKit/Management/ManagementSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,27 +33,21 @@ public enum ManagementSessionError: Error {
/// [Yubico developer website](https://developers.yubico.com/yubikey-manager/Config_Reference.html).
public final actor ManagementSession: Session, InternalSession {

var _connection: Connection?

func connection() async -> Connection? {
private weak var _connection: Connection?
internal func connection() async -> Connection? {
return _connection
}

func setConnection(_ connection: Connection?) async {
internal func setConnection(_ connection: Connection?) async {
_connection = connection
}


public nonisolated let version: Version
internal weak var connection: Connection?
private var sessionEnded = false
var endingResult: Result<String, Error>?

private init(connection: Connection) async throws {
let result = try await connection.selectApplication(application: .management)
guard let version = Version(withManagementResult: result) else { throw ManagementSessionError.unexpectedData }
self.version = version
self.connection = connection
self._connection = connection
let internalConnection = await self.internalConnection()
await internalConnection?.setSession(self)
}
Expand All @@ -71,19 +65,12 @@ public final actor ManagementSession: Session, InternalSession {
public func end() async {
let internalConnection = await internalConnection()
await internalConnection?.setSession(nil)
self.connection = nil
}

public func sessionDidEnd() async -> Error? {
print("await ManagementSession sessionDidEnd")
// _ = try await connection?.send(apdu: SmartCardInterface.APDU())
print("ManagementSession session did end\(endingResult != nil ? " with result: \(endingResult!)" : "")")
return nil
await setConnection(nil)
}

/// Returns the DeviceInfo for the connected YubiKey.
public func getDeviceInfo() async throws -> DeviceInfo {
guard let connection else { throw SessionError.noConnection }
guard let connection = _connection else { throw SessionError.noConnection }
let apdu = APDU(cla: 0, ins: 0x1d, p1: 0, p2: 0)
let data = try await connection.send(apdu: apdu)
return try DeviceInfo(withData: data, fallbackVersion: version)
Expand All @@ -103,7 +90,7 @@ public final actor ManagementSession: Session, InternalSession {

/// Enable or disable an application over the specified transport.
public func setEnabled(_ enabled: Bool, application: ApplicationType, overTransport transport: DeviceTransport, reboot: Bool = false) async throws {
guard let connection else { throw SessionError.noConnection }
guard let connection = _connection else { throw SessionError.noConnection }
let deviceInfo = try await getDeviceInfo()
guard enabled != deviceInfo.config.isApplicationEnabled(application, overTransport: transport) else { return }
guard deviceInfo.isApplicationSupported(application, overTransport: transport) else {
Expand Down
49 changes: 20 additions & 29 deletions YubiKit/YubiKit/OATH/OATHSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,33 +44,29 @@ public enum OATHSessionError: Error {
/// The OATHSession is an interface to the OATH appcliation on the YubiKey that will
/// let you store, calculate and edit TOTP and HOTP credentials on the YubiKey. Learn
/// more about OATH on the [Yubico developer website](https://developers.yubico.com/OATH/).
public final class OATHSession: Session, InternalSession {
func connection() async -> Connection? {
public final actor OATHSession: Session, InternalSession {

private weak var _connection: Connection?
internal func connection() async -> Connection? {
return _connection
}

func setConnection(_ connection: Connection?) async {
internal func setConnection(_ connection: Connection?) async {
_connection = connection
}


internal weak var _connection: Connection?
private var sessionEnded = false
var endingResult: Result<String, Error>?

private struct SelectResponse {
let salt: Data
let challenge: Data?
let version: Version
let deviceId: String
}

private var selectResponse: SelectResponse
private var selectResponse: SelectResponse?

private init(connection: Connection) async throws {
print("⚡️ init OATHSession")
self.selectResponse = try await Self.selectApplication(withConnection: connection)
await self.setConnection(connection)
self._connection = connection
let internalConnection = await internalConnection()
await internalConnection?.setSession(self)
}
Expand Down Expand Up @@ -103,18 +99,12 @@ public final class OATHSession: Session, InternalSession {
}

public func end() async {
let internalConnection = _connection as? InternalConnection
self.selectResponse = nil
let internalConnection = await internalConnection()
await internalConnection?.setSession(nil)
self._connection = nil
}

public func sessionDidEnd() async -> Error? {
print("await OATH sessionDidEnd")
// _ = try await connection?.send(apdu: APDU())
print("OATH session did end\(endingResult != nil ? " with result: \(endingResult!)" : "")")
return nil
}

public func reset() async throws {
guard let connection = _connection else { throw SessionError.noConnection }
print("Reset OATH application")
Expand All @@ -134,7 +124,7 @@ public final class OATHSession: Session, InternalSession {
/// - Returns: The newly added credential.
@discardableResult
public func addCredential(template: CredentialTemplate) async throws -> Credential {
guard let connection = _connection else { throw SessionError.noConnection }
guard let connection = _connection, let selectResponse else { throw SessionError.noConnection }
guard let nameData = template.identifier.data(using: .utf8) else { throw OATHSessionError.unexpectedData }
let nameTlv = TKBERTLVRecord(tag: 0x71, value: nameData)
var keyData = Data()
Expand Down Expand Up @@ -171,7 +161,7 @@ public final class OATHSession: Session, InternalSession {
/// List credentials on YubiKey.
/// - Returns: An array of Credentials.
public func listCredentials() async throws -> [Credential] {
guard let connection = _connection else { throw SessionError.noConnection }
guard let connection = _connection, let selectResponse else { throw SessionError.noConnection }
let apdu = APDU(cla: 0, ins: 0xa1, p1: 0, p2: 0)
let data = try await connection.send(apdu: apdu)
guard let result = TKBERTLVRecord.sequenceOfRecords(from: data) else { throw OATHSessionError.responseDataNotTLVFormatted }
Expand Down Expand Up @@ -201,9 +191,9 @@ public final class OATHSession: Session, InternalSession {
/// - timestamp: The timestamp which is used as start point for TOTP, this is ignored for HOTP.
/// - Returns: Calculated code.
public func calculateCode(credential: Credential, timestamp: Date = Date()) async throws -> Code {
guard let connection = _connection else { throw SessionError.noConnection }
guard let connection = _connection, let selectResponse else { throw SessionError.noConnection }

guard credential.deviceId == self.selectResponse.deviceId else { throw OATHSessionError.credentialNotPresentOnCurrentYubiKey }
guard credential.deviceId == selectResponse.deviceId else { throw OATHSessionError.credentialNotPresentOnCurrentYubiKey }
let challengeTLV: TKBERTLVRecord

switch credential.type {
Expand Down Expand Up @@ -262,7 +252,7 @@ public final class OATHSession: Session, InternalSession {
let bigChallenge = CFSwapInt64HostToBig(challenge)
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 }
guard let connection = _connection, let selectResponse else { throw SessionError.noConnection }
let data = try await connection.send(apdu: apdu)
guard let result = TKBERTLVRecord.sequenceOfRecords(from: data)?.tuples() else { throw OATHSessionError.responseDataNotTLVFormatted }

Expand All @@ -278,7 +268,7 @@ public final class OATHSession: Session, InternalSession {
credentialType = .TOTP(period: credentialId.period ?? oathDefaultPeriod)
}

let credential = Credential(deviceId: self.selectResponse.deviceId, id: name.value, type: credentialType, name: credentialId.account, issuer: credentialId.issuer)
let credential = Credential(deviceId: selectResponse.deviceId, id: name.value, type: credentialType, name: credentialId.account, issuer: credentialId.issuer)

if response.value.count == 5 {
if credentialId.period != oathDefaultPeriod {
Expand Down Expand Up @@ -343,7 +333,7 @@ public final class OATHSession: Session, InternalSession {
/// See the [YKOATH protocol specification](https://developers.yubico.com/OATH/) for further details.
/// - Parameter accessKey: The shared access key.
public func unlockWithAccessKey(_ accessKey: Data) async throws {
guard let connection = _connection, let responseChallenge = self.selectResponse.challenge else { throw SessionError.noConnection }
guard let connection = _connection, let responseChallenge = self.selectResponse?.challenge else { throw SessionError.noConnection }
let reponseTlv = TKBERTLVRecord(tag: tagResponse, value: responseChallenge.hmacSha1(usingKey: accessKey))
let challenge = Data.random(length: 8)
let challengeTlv = TKBERTLVRecord(tag: tagChallenge, value: challenge)
Expand Down Expand Up @@ -378,7 +368,7 @@ public final class OATHSession: Session, InternalSession {
}
}

struct DeriveAccessKeyError: Error {
public struct DeriveAccessKeyError: Error {
let cryptorStatus: CCCryptorStatus
}

Expand All @@ -389,13 +379,14 @@ extension OATHSession {
/// - Parameter password: A user-supplied password.
/// - Returns: Access key for unlocking the session.
public func deriveAccessKey(from password: String) throws -> Data {
guard let selectResponse else { throw SessionError.noConnection }
var derivedKey = Data(count: 16)
try derivedKey.withUnsafeMutableBytes { (outputBytes: UnsafeMutableRawBufferPointer) in
let status = CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2),
password,
password.utf8.count,
self.selectResponse.salt.bytes,
self.selectResponse.salt.bytes.count,
selectResponse.salt.bytes,
selectResponse.salt.bytes.count,
CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA1),
1000,
outputBytes.baseAddress?.assumingMemoryBound(to: UInt8.self),
Expand Down
4 changes: 3 additions & 1 deletion YubiKit/YubiKit/Session.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ public protocol Session: AnyObject {

/// Returns a new session using the supplied connection.
static func session(withConnection connection: Connection) async throws -> Self

/// End the session. This will remove its internal connection and discard any state saved by the session.
/// The connection to the YubiKey will be kept open.
func end() async
func sessionDidEnd() async -> Error?
}

internal protocol InternalSession {
Expand Down

0 comments on commit 39ac52e

Please sign in to comment.