crates/gunnar-grpc/tests/security_key.rs
//! **A FIDO2 security key (`sk-ssh-ed25519`) signs for the CLI — with a touch**
//! (5.4 §3b passkey-cli, owner 2026-10-10: "CLI uses a FIDO2 sk-ssh key or a
//! `gunnar login` browser device flow").
//!
//! An sk key's private half never leaves the authenticator, and the signature
//! carries the authenticator's flags. ssh-key verifies the signature over
//! those flags but does not READ them: a credential the key produced without
//! anybody touching it (`no-touch-required`, or malware driving a key left
//! plugged in) would verify. gunnar's one verifier refuses it.
mod security_key_common;
use security_key_common::{SecurityKey, USER_PRESENT};
const NONCE: &str = "0123456789abcdef0123456789abcdef";
#[test]
fn an_sk_credential_signed_with_a_touch_verifies() {
let mut key = SecurityKey::new(0x21);
let credential = key.credential(1_700_000_000_000, NONCE, "Repos.List", USER_PRESENT);
assert!(
gunnar_grpc::auth::verify_signature(&key.openssh(), &credential),
"an sk-ssh-ed25519 key the person touched is a credential gunnar accepts"
);
}
#[test]
fn an_sk_credential_signed_without_a_touch_is_refused() {
let mut key = SecurityKey::new(0x22);
let credential = key.credential(1_700_000_000_000, NONCE, "Repos.List", 0x00);
assert!(
!gunnar_grpc::auth::verify_signature(&key.openssh(), &credential),
"a security key that signed with nobody present is not the person"
);
// Not a dead check: the same key, touched, verifies.
let touched = key.credential(1_700_000_000_000, NONCE, "Repos.List", USER_PRESENT | 0x04);
assert!(gunnar_grpc::auth::verify_signature(
&key.openssh(),
&touched
));
}
#[test]
fn an_sk_credential_is_bound_to_its_method_like_any_other() {
let mut key = SecurityKey::new(0x23);
let mut credential = key.credential(1_700_000_000_000, NONCE, "Repos.List", USER_PRESENT);
credential.method = Some("Admin.Shutdown".to_owned());
assert!(!gunnar_grpc::auth::verify_signature(
&key.openssh(),
&credential
));
}