mirror of
https://github.com/openai/codex.git
synced 2026-10-21 17:48:44 +00:00
## Why Keychain signing reconstructs its own authentication context, so invalidating the caller's context does not reliably cancel the context that owns the biometric prompt. ## What changed Evaluate the stored key's access-control policy explicitly with `LAContext` before signing. On cancellation, invalidate that context and wait for its authorization callback before releasing the lifecycle lock, rejecting any late success. Reload the key with the authorized context and disable further interaction so signing cannot open another prompt. ## Testing Update unit tests to cover waiting for cancellation completion and rejecting completed authorization after cancellation. Add an ignored macOS hardware test and setup instructions covering persisted Secure Enclave key reuse, signature verification, cancellation, retry signing, and cleanup. GitOrigin-RevId: 4ea9756430c5649654e95e047d1666617503072c
71 lines
2.5 KiB
Rust
71 lines
2.5 KiB
Rust
//! Exercises cancellation completion and late-result rejection without biometric hardware.
|
|
|
|
use super::*;
|
|
use crate::UserVerificationCancellationReason;
|
|
use pretty_assertions::assert_eq;
|
|
use std::sync::atomic::AtomicBool;
|
|
use std::sync::atomic::Ordering;
|
|
|
|
#[test]
|
|
fn cancellation_waits_for_completion_on_the_context_owner_thread() {
|
|
let guard = UserVerificationRequestGuard::default();
|
|
let queued = guard.clone();
|
|
let (done_tx, done_rx) = mpsc::sync_channel(0);
|
|
let (cancelled_tx, cancelled_rx) = mpsc::channel();
|
|
let owner = std::thread::spawn(move || {
|
|
let owner_thread = std::thread::current().id();
|
|
wait_with_cancellation(&queued, done_rx, || {
|
|
assert_eq!(std::thread::current().id(), owner_thread);
|
|
cancelled_tx.send(()).expect("dismiss authentication");
|
|
})
|
|
});
|
|
guard.cancel();
|
|
cancelled_rx
|
|
.recv_timeout(Duration::from_secs(/*secs*/ 5))
|
|
.expect("authentication invalidated");
|
|
assert!(!owner.is_finished());
|
|
done_tx
|
|
.send(Ok("late authorization"))
|
|
.expect("complete authentication");
|
|
assert_eq!(
|
|
owner.join().expect("owner returned"),
|
|
Err(UserVerificationError::Cancelled {
|
|
reason: UserVerificationCancellationReason::Interrupted,
|
|
message: "the verification operation is no longer active".to_string(),
|
|
})
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn completed_operation_does_not_cancel_its_context() {
|
|
let cancelled = AtomicBool::new(/*v*/ false);
|
|
let (done_tx, done_rx) = mpsc::channel();
|
|
done_tx
|
|
.send(Ok("authorization"))
|
|
.expect("complete authentication");
|
|
assert_eq!(
|
|
wait_with_cancellation(&UserVerificationRequestGuard::default(), done_rx, || {
|
|
cancelled.store(/*val*/ true, Ordering::Release)
|
|
},),
|
|
Ok("authorization")
|
|
);
|
|
assert!(!cancelled.load(Ordering::Acquire));
|
|
}
|
|
|
|
#[test]
|
|
fn cancellation_rejects_already_completed_authorization() {
|
|
let guard = UserVerificationRequestGuard::default();
|
|
let (done_tx, done_rx) = mpsc::channel();
|
|
done_tx.send(Ok(())).expect("complete authentication");
|
|
guard.cancel();
|
|
assert_eq!(
|
|
wait_with_cancellation(&guard, done_rx, || panic!(
|
|
"authentication already completed"
|
|
)),
|
|
Err(UserVerificationError::Cancelled {
|
|
reason: UserVerificationCancellationReason::Interrupted,
|
|
message: "the verification operation is no longer active".to_string(),
|
|
})
|
|
);
|
|
}
|