Skip to content

Commit ba96f13

Browse files
committed
Make device PIN optional for assertion, add utility function to check if PIN exists
1 parent f981dfd commit ba96f13

File tree

1 file changed

+30
-36
lines changed

1 file changed

+30
-36
lines changed

LibFido2Swift/Sources/LibFido2Swift/LibFido2Swift.swift

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ public struct ChallengeResponse: Encodable {
1616
public struct ChallengeArgs {
1717
public let rpId: String
1818
public let validCredentials: [String]
19-
public let devPin: String
19+
public let devPin: String?
2020
public let challenge: String
2121
public let origin: String
2222

23-
public init(rpId: String, validCredentials: [String], devPin: String, challenge: String, origin: String) {
23+
public init(rpId: String, validCredentials: [String], devPin: String?, challenge: String, origin: String) {
2424
self.rpId = rpId
2525
self.validCredentials = validCredentials
2626
self.devPin = devPin
@@ -66,6 +66,21 @@ public class FIDO2 {
6666

6767
public init() {}
6868

69+
public func deviceHasPin() throws -> Bool {
70+
let devPath = try findFirstDevicePath()
71+
72+
var dev = fido_dev_new()
73+
defer { fido_dev_free(&dev) }
74+
75+
let openResult = fido_dev_open(dev, devPath)
76+
guard openResult == FIDO_OK else {
77+
throw FIDO2Error.internalError
78+
}
79+
defer { fido_dev_close(dev) }
80+
81+
return fido_dev_has_pin(dev)
82+
}
83+
6984
/// Responds to the given WebAuthn challenge
7085
/// Note that this is a **blocking** method if the challenge requires user verification (don't call it on the main thread!)
7186
/// (The method blocks waiting for the user to touch the security device)
@@ -107,14 +122,10 @@ public class FIDO2 {
107122
throw FIDO2Error.notFido2Device
108123
}
109124

110-
guard let matchingCredId = try getMatchingCredId(from: dev, validCredentials: validCredentials, rpId: rpId, devPin: devPin) else {
111-
// The device has no valid credentials, we cannot continue
112-
throw FIDO2Error.errorNoValidCredentials
113-
}
114-
115125
try makeAssertion(using: dev, fidoAssertion: fa, devicePin: devPin)
116126

117127
let authDataBase64Str = try getAuthData(fidoAssertion: fa)
128+
let credentialId = try getCredentialIdUsed(fidoAssertion: fa)
118129
let signatureDataBase64Str = try getSignatureData(fidoAssertion: fa)
119130
let userHandleBase64Str = try getUserData(fidoAssertion: fa)
120131

@@ -124,7 +135,7 @@ public class FIDO2 {
124135
signatureData: signatureDataBase64Str,
125136
authenticatorData: authDataBase64Str,
126137
userHandle: userHandleBase64Str,
127-
credentialID: matchingCredId,
138+
credentialID: credentialId,
128139
rpId: rpId)
129140
return response
130141
}
@@ -169,34 +180,7 @@ public class FIDO2 {
169180
return String(cString: devPath)
170181
}
171182

172-
private func getMatchingCredId(from dev: OpaquePointer?, validCredentials: [String], rpId: String, devPin: String) throws -> String? {
173-
var rk = fido_credman_rk_new() // Resident credentials array
174-
defer { fido_credman_rk_free(&rk) }
175-
176-
let get_dev_array_result = fido_credman_get_dev_rk(dev, rpId, rk, devPin)
177-
guard get_dev_array_result == FIDO_OK else {
178-
throw FIDO2Error.libfido2ErrorInternal(get_dev_array_result)
179-
}
180-
181-
let rkCount = fido_credman_rk_count(rk)
182-
for i in 0..<rkCount {
183-
let cred = fido_credman_rk(rk, i)
184-
guard let idPtr = fido_cred_id_ptr(cred) else {
185-
throw FIDO2Error.internalError
186-
}
187-
let idLen = fido_cred_id_len(cred)
188-
let idData = Data(bytes: idPtr, count: idLen)
189-
let idBase64 = idData.base64EncodedString()
190-
if validCredentials.contains(idBase64) {
191-
// Return the first found, valid credential
192-
return idBase64
193-
}
194-
}
195-
196-
return nil
197-
}
198-
199-
private func makeAssertion(using device: OpaquePointer?, fidoAssertion: OpaquePointer?, devicePin: String) throws {
183+
private func makeAssertion(using device: OpaquePointer?, fidoAssertion: OpaquePointer?, devicePin: String?) throws {
200184
let assertResult = fido_dev_get_assert(device, fidoAssertion, devicePin)
201185
guard assertResult == FIDO_OK else {
202186
if assertResult == FIDO_ERR_ACTION_TIMEOUT {
@@ -220,6 +204,16 @@ public class FIDO2 {
220204
return authDataBase64Str
221205
}
222206

207+
private func getCredentialIdUsed(fidoAssertion fa: OpaquePointer?) throws -> String {
208+
guard let credentialIdPtr = fido_assert_id_ptr(fa, 0) else {
209+
throw FIDO2Error.internalError
210+
}
211+
let credIdLen = fido_assert_id_len(fa, 0)
212+
let credIdData = Data(bytes: credentialIdPtr, count: credIdLen)
213+
let credIdBase64Str = credIdData.base64EncodedString()
214+
return credIdBase64Str
215+
}
216+
223217
private func getSignatureData(fidoAssertion fa: OpaquePointer?) throws -> String {
224218
guard let signatureDataPtr = fido_assert_sig_ptr(fa, 0) else {
225219
throw FIDO2Error.internalError

0 commit comments

Comments
 (0)