mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-26 20:25:22 +00:00
Make panic wipe deterministic and device-bound (#1431)
* Make panic wipe deterministic and device-bound * Scope install markers to iOS * Harden panic recovery and service shutdown * Invalidate queued BLE ingress during panic * Harden panic keychain and media cleanup --------- Co-authored-by: jack <jackjackbits@users.noreply.github.com> Co-authored-by: jack <jack@deck.local>
This commit is contained in:
@@ -26,6 +26,8 @@ protocol VoiceCaptureSession: AnyObject {
|
||||
/// nothing valid was captured.
|
||||
func finish() async -> URL?
|
||||
func cancel() async
|
||||
/// Stops capture and suppresses every later send before returning.
|
||||
func panicCancelSynchronously()
|
||||
}
|
||||
|
||||
/// The classic record-then-send backend, wrapping the shared `VoiceRecorder`.
|
||||
@@ -55,6 +57,10 @@ final class VoiceNoteCaptureSession: VoiceCaptureSession {
|
||||
func cancel() async {
|
||||
await recorder.cancelRecording(owner: owner)
|
||||
}
|
||||
|
||||
func panicCancelSynchronously() {
|
||||
recorder.panicCancelSynchronously(owner: owner)
|
||||
}
|
||||
}
|
||||
|
||||
/// Testable surface of the live capture engine. Production uses
|
||||
@@ -216,6 +222,13 @@ final class PTTLiveVoiceSession: VoiceCaptureSession {
|
||||
}
|
||||
}
|
||||
|
||||
func panicCancelSynchronously() {
|
||||
// Do not emit a canceled packet: it would itself be pre-panic
|
||||
// conversation data racing the emergency transport reset.
|
||||
completed = true
|
||||
capture.cancel()
|
||||
}
|
||||
|
||||
private func sendControlPacket(_ kind: VoiceBurstPacket.Kind) {
|
||||
guard let packet = VoiceBurstPacket(burstID: burstID, seq: stream.packetizer.nextSeq, kind: kind) else { return }
|
||||
sendPacket(packet.encode())
|
||||
|
||||
@@ -246,6 +246,21 @@ actor VoiceRecorder {
|
||||
currentURL = nil
|
||||
}
|
||||
|
||||
/// Panic is a synchronous security boundary: the caller must know the
|
||||
/// microphone, audio-session lease, and partial file are gone before it
|
||||
/// rotates identities or deletes the media tree. VoiceRecorder is an
|
||||
/// independent actor and this cleanup path never hops to MainActor, so a
|
||||
/// short semaphore join is safe even when invoked by the UI actor.
|
||||
nonisolated
|
||||
func panicCancelSynchronously(owner: RecordingOwner) {
|
||||
let finished = DispatchSemaphore(value: 0)
|
||||
Task {
|
||||
await cancelRecording(owner: owner)
|
||||
finished.signal()
|
||||
}
|
||||
finished.wait()
|
||||
}
|
||||
|
||||
/// The audio session was interrupted (call, Siri) or reconfigured: stop
|
||||
/// the recorder but keep `recorder`/`currentURL` so the caller's pending
|
||||
/// `stopRecording()` still returns the partial note.
|
||||
|
||||
Reference in New Issue
Block a user