Hi there, bit of an odd one, we have no idea how this happened but now we can't seem to figure out how to fix.
Our app requests Touch ID on macOS to authenticate a user. This is done in the ever so standard way [LAContent evaluatePolicy:...]... Functionally everything is fine, but for some reason there is no App Name on the system dialog... We don't even know when this started happening...
Our App Icon is there but not the name, it's blank so the dialog looks strange (see attached pic). The text doesn't really make sense without the App Name.
I wouldn't have even thought this was possible, the standard info.plist keys like CFBundleName and CFBundleDisplayName are all set correctly. Everything else seems totally fine. We're seeing this across every target/build/version/sku so it seems unrelated to a particular plist. There are no localizations for the App Name either, no InfoPlist.strings involved here.
What could cause this, does anyone know?
@eskimo, I'm afraid turning things up to 11 didn't help, so hoping you've got an idea?
Post
Replies
Boosts
Views
Activity
Hi there, we're looking to build a Bonjour service for our users so that they can share data between devices. Things are mostly going ok, but we would like to make sure the connection is secure.
Being good developers we took a look at the TicTacToe example from WWDC. This looks great! We'd love to secure our comms with the latest TLS via a Pre Shared Key (PSK) e.g. a Passcode in our case.
In the normal happy path, things work well, we can send and receive messages and all is well. However, when we enter the wrong passcode we don't receive any notification back on the client side. The server can detect the incorrect passcode, but the client is left hanging around.
The issue only appears to affect a Bonjour service or mode (not quite sure of the terminology here). If we explicitly specify a host (e.g. "localhost" and port (e.g. 12345) for connection/listening then we get the expected callbacks on both client/server that the PIN was incorrect.
However if we just setup a service and try to connect to it (in our case we use NWBrowser in our App, but below we create an endpoint manually), everything works fine for a good passcode, but for a bad passcode we don't receive any callback and have no way to know the passcode was no good and inform the user.
So, we'd love to be able to detect that incorrect passcode on the client side. What are we doing wrong.
Sample code below (mostly shamelessly ripped from some of @eskimos sample code in another issue) demonstrates the issue, change the ServiceMode / Passcodes inside main() to see the issue.
Hoping we can page Dr. @eskimo and Dr. @meaton - Could really do with your expertise here. Ta!
import CryptoKit
import Foundation
import Network
let ServerName = "My-Bonjour-Server"
let ServiceName = "_my_bonjour_service._tcp"
var listenerRef: NWListener?
var receiveConnectionRef: NWConnection?
var sendConnectionRef: NWConnection?
enum ServiceMode {
case explicitHostAndPort // This works all the time
case bonjourService // This doesn't work for an incorrect passcode
}
extension NWParameters { // Just ripped from the TicTacToe example
convenience init(passcode: String) {
self.init(tls: NWParameters.tlsOptions(passcode: passcode))
}
private static func tlsOptions(passcode: String) -> NWProtocolTLS.Options {
let tlsOptions = NWProtocolTLS.Options()
let authenticationKey = SymmetricKey(data: passcode.data(using: .utf8)!)
let authenticationCode = HMAC<SHA256>.authenticationCode(for: ServiceName.data(using: .utf8)!, using: authenticationKey)
let authenticationDispatchData = authenticationCode.withUnsafeBytes {
DispatchData(bytes: $0)
}
sec_protocol_options_add_pre_shared_key(tlsOptions.securityProtocolOptions,
authenticationDispatchData as __DispatchData,
stringToDispatchData(ServiceName)! as __DispatchData)
sec_protocol_options_append_tls_ciphersuite(tlsOptions.securityProtocolOptions,
tls_ciphersuite_t(rawValue: TLS_PSK_WITH_AES_128_GCM_SHA256)!)
return tlsOptions
}
private static func stringToDispatchData(_ string: String) -> DispatchData? {
guard let stringData = string.data(using: .utf8) else {
return nil
}
let dispatchData = stringData.withUnsafeBytes {
DispatchData(bytes: $0)
}
return dispatchData
}
}
func startListener(passcode: String, serviceMode: ServiceMode) {
let listener: NWListener
switch serviceMode {
case .explicitHostAndPort:
listener = try! NWListener(using: NWParameters(passcode: passcode), on: 12345)
case .bonjourService:
listener = try! NWListener(using: NWParameters(passcode: passcode))
listener.service = NWListener.Service(name: ServerName, type: ServiceName)
}
listenerRef = listener
listener.stateUpdateHandler = { state in
print("listener: state did change, new: \(state)")
}
listener.newConnectionHandler = { conn in
if let old = receiveConnectionRef {
print("listener: will cancel old connection")
old.cancel()
receiveConnectionRef = nil
}
receiveConnectionRef = conn
startReceive(on: conn)
conn.start(queue: .main)
}
listener.start(queue: .main)
}
func startReceive(on connection: NWConnection) {
connection.receive(minimumIncompleteLength: 1, maximumLength: 2048) { dataQ, _, _, errorQ in
if let data = dataQ, let str = String(data: data, encoding: .utf8) {
print("receiver: did receive: \"\(str)\"")
}
if let error = errorQ {
if case let .tls(oSStatus) = error, oSStatus == errSSLBadRecordMac {
print("receiver has detected an Incorrect PIN")
} else {
print("receiver: did fail, error: \(error)")
}
return
}
}
}
func startSender(passcode: String, serviceMode: ServiceMode) {
let connection: NWConnection
switch serviceMode {
case .explicitHostAndPort:
connection = NWConnection(host: "localhost", port: 12345, using: NWParameters(passcode: passcode))
case .bonjourService:
let endpoint = NWEndpoint.service(name: ServerName, type: ServiceName, domain: "local.", interface: nil)
connection = NWConnection(to: endpoint, using: NWParameters(passcode: passcode))
}
sendConnectionRef = connection
connection.stateUpdateHandler = { state in
if case let .waiting(error) = state {
if case let .tls(os) = error, os == errSSLPeerBadRecordMac { // Incorrect PIN
print("Sender has detected an Incorrect PIN")
}
} else {
print("sender: state did change, new: \(state)")
}
}
connection.send(content: "It goes to 11".data(using: .utf8), completion: .idempotent)
connection.start(queue: .main)
}
func main() {
let serviceMode: ServiceMode = .explicitHostAndPort // Set this to Bonjour to see the issue
// Change one of the Passcodes below to see the incorrect pin message(s) or lack thereof
startListener(passcode: "1234", serviceMode: serviceMode)
// Wait for server to spin up...
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
startSender(passcode: "1234", serviceMode: serviceMode)
}
dispatchMain()
}
main()
exit(EXIT_SUCCESS)
On macOS, in the Apple Passwords app (currently inside Settings but soon to be it's own full fledged app in Sequoia) the user is presented with a screen requesting that they touch the fingerprint reader (see attached).
If we'd like to do something similar, e.g. unlock some sensitive/secure part of our app, by requesting the user touch the Touch ID sensor, but without doing the whole system prompt (LAContext.evaluatePolicy()), how can we do that?
Is that possible for mere mortal developers, and if not, why not?
Similar to this thread. How do we go about testing or triggering the UI for this new iOS/macOS 18 API?
https://developer.apple.com/documentation/authenticationservices/ascredentialproviderviewcontroller/prepareinterfaceforuserchoosingtexttoinsert()
Thanks!