Skip to content

Commit

Permalink
Throw the expected SessionError.notSupported when trying to use PIVPi…
Browse files Browse the repository at this point in the history
…nPolicy.matchAlways or PIVPinPolicy.matchOnce on a non Bio YubiKey.
  • Loading branch information
jensutbult committed Jun 18, 2024
1 parent 793ed2d commit 8343e4f
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 12 deletions.
49 changes: 40 additions & 9 deletions FullStackTests/Tests/PIVFullStackTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -660,9 +660,48 @@ final class PIVFullStackTests: XCTestCase {
Logger.test.debug("✅ temporary pin verified.")
}
}

func testBioPinPolicyErrorOnNonBioKey() throws {
runAsyncTest {
let connection = try await AllowedConnections.anyConnection()
let managementSession = try await ManagementSession.session(withConnection: connection)
let deviceInfo = try await managementSession.getDeviceInfo()
guard deviceInfo.formFactor != .usbCBio && deviceInfo.formFactor != .usbABio else {
print("⚠️ Skip testBioPinPolicyErrorOnNonBioKey() since this is a bio key.")
return
}
let session = try await PIVSession.session(withConnection: connection)
try await self.authenticate(with: session)
do {
_ = try await session.generateKeyInSlot(slot: .signature, type: .ECCP384, pinPolicy: .matchAlways, touchPolicy: .defaultPolicy)
} catch {
guard let sessionError = error as? SessionError else { throw error }
XCTAssertEqual(sessionError, SessionError.notSupported)
}
do {
_ = try await session.generateKeyInSlot(slot: .signature, type: .ECCP384, pinPolicy: .matchOnce, touchPolicy: .defaultPolicy)
} catch {
guard let sessionError = error as? SessionError else { throw error }
XCTAssertEqual(sessionError, SessionError.notSupported)
}
}
}
}

extension XCTestCase {

func authenticate(with session: PIVSession) async throws {
let defaultManagementKey = Data(hexEncodedString: "010203040506070801020304050607080102030405060708")!
let keyType: PIVManagementKeyType
if session.supports(PIVSessionFeature.metadata) {
let metadata = try await session.getManagementKeyMetadata()
keyType = metadata.keyType
} else {
keyType = .tripleDES
}
try await session.authenticateWith(managementKey: defaultManagementKey, keyType: keyType)
}

func runPIVTest(named testName: String = #function,
in file: StaticString = #file,
at line: UInt = #line,
Expand All @@ -687,15 +726,7 @@ extension XCTestCase {
let connection = try await AllowedConnections.anyConnection()
let session = try await PIVSession.session(withConnection: connection)
try await session.reset()
let defaultManagementKey = Data(hexEncodedString: "010203040506070801020304050607080102030405060708")!
let keyType: PIVManagementKeyType
if session.supports(PIVSessionFeature.metadata) {
let metadata = try await session.getManagementKeyMetadata()
keyType = metadata.keyType
} else {
keyType = .tripleDES
}
try await session.authenticateWith(managementKey: defaultManagementKey, keyType: keyType)
try await self.authenticate(with: session)
Logger.test.debug("⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ PIV Session test ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️")
try await test(session)
Logger.test.debug("\(testName) passed")
Expand Down
9 changes: 6 additions & 3 deletions YubiKit/YubiKit/PIV/PIVSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ public final actor PIVSession: Session, InternalSession {
public func generateKeyInSlot(slot: PIVSlot, type: PIVKeyType, pinPolicy: PIVPinPolicy = .`defaultPolicy`, touchPolicy: PIVTouchPolicy = .`defaultPolicy`) async throws -> SecKey {
Logger.piv.debug("\(String(describing: self).lastComponent), \(#function)")
guard let connection = _connection else { throw SessionError.noConnection }
try checkKeyFeatures(keyType: type, pinPolicy: pinPolicy, touchPolicy: touchPolicy, generateKey: true)
try await checkKeyFeatures(keyType: type, pinPolicy: pinPolicy, touchPolicy: touchPolicy, generateKey: true)
let records: [TKBERTLVRecord] = [TKBERTLVRecord(tag: tagGenAlgorithm, value: type.rawValue.data),
pinPolicy != .`defaultPolicy` ? TKBERTLVRecord(tag: tagPinPolicy, value: pinPolicy.rawValue.data) : nil,
touchPolicy != .`defaultPolicy` ? TKBERTLVRecord(tag: tagTouchpolicy, value: touchPolicy.rawValue.data) : nil].compactMap { $0 }
Expand Down Expand Up @@ -195,7 +195,7 @@ public final actor PIVSession: Session, InternalSession {
Logger.piv.debug("\(String(describing: self).lastComponent), \(#function)")
guard let connection = _connection else { throw SessionError.noConnection }
guard let keyType = key.type else { throw PIVSessionError.unknownKeyType }
try checkKeyFeatures(keyType: keyType, pinPolicy: pinPolicy, touchPolicy: touchPolicy, generateKey: false)
try await checkKeyFeatures(keyType: keyType, pinPolicy: pinPolicy, touchPolicy: touchPolicy, generateKey: false)
var error: Unmanaged<CFError>?
guard let cfKeyData = SecKeyCopyExternalRepresentation(key, &error) else { throw error!.takeRetainedValue() as Error }
let keyData = cfKeyData as Data
Expand Down Expand Up @@ -741,14 +741,17 @@ extension PIVSession {
}
}

private func checkKeyFeatures(keyType: PIVKeyType, pinPolicy: PIVPinPolicy, touchPolicy: PIVTouchPolicy, generateKey: Bool) throws {
private func checkKeyFeatures(keyType: PIVKeyType, pinPolicy: PIVPinPolicy, touchPolicy: PIVTouchPolicy, generateKey: Bool) async throws {
Logger.piv.debug("\(String(describing: self).lastComponent), \(#function)")
if keyType == .ECCP384 {
guard self.supports(PIVSessionFeature.p384) else { throw SessionError.notSupported }
}
if pinPolicy != .`defaultPolicy` || touchPolicy != .`defaultPolicy` {
guard self.supports(PIVSessionFeature.usagePolicy) else { throw SessionError.notSupported }
}
if pinPolicy == .matchAlways || pinPolicy == .matchOnce {
_ = try await self.getBioMetadata() // This will throw SessionError.notSupported if the key is not a Bio key.
}
if generateKey && (keyType == .RSA1024 || keyType == .RSA2048) {
guard self.supports(PIVSessionFeature.rsaGeneration) else { throw SessionError.notSupported }
}
Expand Down

0 comments on commit 8343e4f

Please sign in to comment.