Kameraaufnahme, Anlernen über drei Posen, Fenster je Bildschirm auf Bildschirmschoner-Ebene, Passwortrückfall. **Das Anmeldepasswort wird nicht gespeichert.** Ein Spike hat den Weg freigeräumt: `ODRecord.verifyPassword` ist öffentliche API, die Eingabe geht direkt hinein, das Ergebnis ist ein `Bool`, danach ist die Zeichenkette wieder weg. Damit fällt der Grund weg, aus dem vergleichbare Programme sich das Passwort geben lassen und in die Keychain legen. Angelernt wird über drei Posen mit je fünf Aufnahmen. Der Merkmalsvektor ist ein allgemeiner Bildmerkmalsvektor und kein Gesichtsmerkmal — er reagiert auf Kopfhaltung und Beleuchtung mit. Eine einzige Aufnahme beschreibt genau eine Haltung bei genau einem Licht. Die Vektoren liegen in der Keychain, nicht in einer Datei: aus ihnen lässt sich kein Bild zurückgewinnen, aber wer sie hat, kann prüfen, ob eine bestimmte Person vor der Kamera stand. Die Einschränkung steht auf dem Sperrbildschirm selbst und nicht im Kleingedruckten: Bequemlichkeit, keine Sicherheitsgrenze; der Vergleich läuft über die Webcam und lässt sich mit einem Foto täuschen. Wer sich darauf verlässt, tut es dort. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
63 lines
2.4 KiB
Swift
63 lines
2.4 KiB
Swift
import Foundation
|
|
import OpenDirectory
|
|
import os
|
|
|
|
private let log = Logger(subsystem: "com.scarriffleservices.onyx", category: "Face")
|
|
|
|
/// Prüft eine Passworteingabe gegen die echte Anmeldung.
|
|
///
|
|
/// **Und speichert dabei nichts.** Die Zeichenkette geht direkt in
|
|
/// `ODRecord.verifyPassword`, das Ergebnis ist ein `Bool`, und danach ist sie
|
|
/// wieder weg. Siehe `docs/spikes/I-passwortpruefung.md`.
|
|
///
|
|
/// Damit fällt der Grund weg, aus dem vergleichbare Programme sich das
|
|
/// Anmeldepasswort geben lassen und in die Keychain legen. Ein gespeichertes
|
|
/// Anmeldepasswort ist die Sorte Sicherheitsschuld, die man nicht aufnimmt,
|
|
/// wenn es auch ohne geht.
|
|
public struct PasswordVerifier: Sendable {
|
|
|
|
private let username: String
|
|
|
|
public init(username: String = NSUserName()) {
|
|
self.username = username
|
|
}
|
|
|
|
/// `true`, wenn die Eingabe stimmt.
|
|
///
|
|
/// Ein Fehler beim Nachschlagen gilt als **falsch**, nicht als richtig: wer
|
|
/// hier großzügig ist, öffnet den Sperrbildschirm, sobald der
|
|
/// Verzeichnisdienst hakt.
|
|
public func verify(_ password: String) -> Bool {
|
|
guard !password.isEmpty else { return false }
|
|
do {
|
|
let node = try ODNode(session: ODSession.default(),
|
|
type: ODNodeType(kODNodeTypeAuthentication))
|
|
let record = try node.record(withRecordType: kODRecordTypeUsers,
|
|
name: username, attributes: nil)
|
|
try record.verifyPassword(password)
|
|
return true
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
|
|
/// Ob der Weg zum Benutzerdatensatz überhaupt offensteht.
|
|
///
|
|
/// Getrennt von `verify`, damit die Einstellungen es prüfen können, ohne
|
|
/// ein Passwort zu verlangen — und damit ein kaputter Verzeichnisdienst
|
|
/// nicht erst am Sperrbildschirm auffällt, wo man ihn am wenigsten
|
|
/// gebrauchen kann.
|
|
public var isAvailable: Bool {
|
|
do {
|
|
let node = try ODNode(session: ODSession.default(),
|
|
type: ODNodeType(kODNodeTypeAuthentication))
|
|
_ = try node.record(withRecordType: kODRecordTypeUsers,
|
|
name: username, attributes: nil)
|
|
return true
|
|
} catch {
|
|
log.error("Benutzerdatensatz nicht erreichbar — Sperrbildschirm ohne Rückfall")
|
|
return false
|
|
}
|
|
}
|
|
}
|