Files
asepharyana 1cdb82a76f Sync config from arch
- hypr/apps.lua
- hypr/autostart.lua
- hypr/envs.lua
- hypr/hyprland.lua
- hypr/hyprsunset.conf
- hypr/input.lua
- hypr/looknfeel.lua
- hypr/omasettings.lua
- hypr/xdph.conf
- omarchy/branding/about.txt
- omarchy/branding/screensaver.txt
- omarchy/extensions/omarchy-menu.jsonc
- omarchy/hooks/battery-low.d/play-warning-sound.sample
- omarchy/hooks/font-set.d/show-font-notification.sample
- omarchy/hooks/post-boot.d/weather.sample
- omarchy/hooks/post-update.d/install-voxtype.hook
- omarchy/hooks/post-update.d/setup-agent.hook
- omarchy/hooks/post-update.d/setup-fingerprint.hook
- omarchy/hooks/post-update.d/show-update-notification.sample
- omarchy/hooks/pre-refresh-pacman.d/add-custom-repo.sample
- omarchy/hooks/theme-set.d/show-theme-notification.sample
- omarchy/shell.json
- omarchy/shell.toml
- omarchy/theme.name
- omarchy/themes/azure-glow/README.md
- omarchy/themes/azure-glow/alacritty.toml
- omarchy/themes/azure-glow/btop.theme
- omarchy/themes/azure-glow/hyprland.conf
- omarchy/themes/azure-glow/hyprlock.conf
- omarchy/themes/azure-glow/icons.theme
- … 269 more
2026-09-23 15:19:12 +07:00

203 lines
6.7 KiB
Rust

use qs_bitwarden_ssh_agent::keystore::{
CandidateItem, KeyStore, LoadError, SkipCode, MAX_FILTERED_BYTES, MAX_KEYS, MAX_PEM_BYTES,
};
use qs_bitwarden_ssh_agent::state::VaultState;
use rand_core::OsRng;
use signature::Verifier;
use ssh_key::private::RsaKeypair;
use ssh_key::{Algorithm, HashAlg, PrivateKey};
use zeroize::Zeroizing;
fn item(id: &str, key: &PrivateKey) -> CandidateItem {
CandidateItem {
item_id: id.to_owned(),
name: format!("key {id}"),
private_key_pem: Zeroizing::new(
key.to_openssh(Default::default())
.unwrap()
.as_bytes()
.to_vec(),
),
public_key: key.public_key().to_openssh().unwrap(),
fingerprint: key.public_key().fingerprint(HashAlg::Sha256).to_string(),
requires_reprompt: false,
}
}
fn ed25519() -> PrivateKey {
PrivateKey::random(&mut OsRng, Algorithm::Ed25519).unwrap()
}
#[test]
fn candidate_skips_bad_mismatched_reprompt_and_duplicate_items() {
let valid = ed25519();
let other = ed25519();
let mut store = KeyStore::new();
let mut load = store.begin_load(1, 4096).unwrap();
assert_eq!(load.add(item("valid", &valid)).unwrap(), None);
assert_eq!(
load.add(CandidateItem {
private_key_pem: Zeroizing::new(b"not a private key".to_vec()),
..item("malformed", &other)
})
.unwrap(),
Some(SkipCode::MalformedPrivateKey)
);
assert_eq!(
load.add(CandidateItem {
public_key: other.public_key().to_openssh().unwrap(),
..item("public-mismatch", &valid)
})
.unwrap(),
Some(SkipCode::PublicKeyMismatch)
);
assert_eq!(
load.add(CandidateItem {
fingerprint: "SHA256:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".to_owned(),
..item("fingerprint-mismatch", &valid)
})
.unwrap(),
Some(SkipCode::FingerprintMismatch)
);
assert_eq!(
load.add(CandidateItem {
requires_reprompt: true,
..item("reprompt", &other)
})
.unwrap(),
Some(SkipCode::RequiresReprompt)
);
assert_eq!(
load.add(item("duplicate", &valid)).unwrap(),
Some(SkipCode::Duplicate)
);
let report = store.publish(load).unwrap();
assert_eq!(report.loaded, 1);
assert_eq!(report.skipped.len(), 5);
assert_eq!(store.state(), VaultState::Unlocked);
assert_eq!(store.public_identities().len(), 1);
assert_eq!(store.public_identities()[0].item_id, "valid");
}
#[test]
fn global_limits_reject_the_whole_candidate_and_leave_no_private_set() {
let key = ed25519();
let mut store = KeyStore::new();
let mut initial = store.begin_load(1, 4096).unwrap();
initial.add(item("old", &key)).unwrap();
store.publish(initial).unwrap();
assert!(store
.authorize(store.public_identities()[0].public_blob())
.is_some());
assert_eq!(
store.begin_load(2, MAX_FILTERED_BYTES + 1).unwrap_err(),
LoadError::FilteredPayloadTooLarge
);
assert_eq!(store.state(), VaultState::Loading);
assert!(store
.authorize(key.public_key().to_bytes().unwrap().as_slice())
.is_none());
let mut too_many = store.begin_load(3, 4096).unwrap();
for index in 0..MAX_KEYS {
let unique = ed25519();
assert_eq!(
too_many.add(item(&index.to_string(), &unique)).unwrap(),
None
);
}
assert_eq!(
too_many.add(item("overflow", &ed25519())).unwrap_err(),
LoadError::TooManyKeys
);
let mut oversized = store.begin_load(4, MAX_PEM_BYTES).unwrap();
let mut huge = item("huge", &key);
huge.private_key_pem = Zeroizing::new(vec![b'x'; MAX_PEM_BYTES + 1]);
assert_eq!(oversized.add(huge).unwrap_err(), LoadError::PemTooLarge);
}
#[test]
fn publish_is_atomic_and_stale_or_failed_loads_cannot_mix_epochs() {
let first = ed25519();
let second = ed25519();
let mut store = KeyStore::new();
let mut load = store.begin_load(7, 4096).unwrap();
load.add(item("first", &first)).unwrap();
store.lock(8);
assert_eq!(store.publish(load).unwrap_err(), LoadError::StaleEpoch);
assert!(store.public_identities().is_empty());
let mut replacement = store.begin_load(9, 4096).unwrap();
replacement.add(item("second", &second)).unwrap();
store.publish(replacement).unwrap();
assert_eq!(store.public_identities().len(), 1);
assert_eq!(store.public_identities()[0].item_id, "second");
}
#[test]
fn lock_invalidates_authorization_before_dropping_keys_and_keeps_public_cache() {
let key = ed25519();
let public_blob = key.public_key().to_bytes().unwrap();
let mut store = KeyStore::new();
let mut load = store.begin_load(11, 4096).unwrap();
load.add(item("work", &key)).unwrap();
store.publish(load).unwrap();
let permit = store.authorize(&public_blob).unwrap();
store.lock(12);
assert_eq!(store.state(), VaultState::LockedCached);
assert_eq!(store.public_identities().len(), 1);
assert!(store.sign(&permit, b"must not sign", 0).is_none());
assert!(store.authorize(&public_blob).is_none());
}
#[test]
fn current_epoch_permit_signs_without_cloning_private_keys() {
let key = ed25519();
let public_blob = key.public_key().to_bytes().unwrap();
let mut store = KeyStore::new();
let mut load = store.begin_load(21, 4096).unwrap();
load.add(item("work", &key)).unwrap();
store.publish(load).unwrap();
let permit = store.authorize(&public_blob).unwrap();
let signature = store.sign(&permit, b"authorized payload", 0).unwrap();
Verifier::verify(key.public_key(), b"authorized payload", &signature).unwrap();
}
#[test]
fn undersized_rsa_is_rejected_during_load() {
let weak_rsa = rsa::RsaPrivateKey::new(&mut OsRng, 1024).unwrap();
let weak = PrivateKey::from(RsaKeypair::try_from(weak_rsa).unwrap());
let mut store = KeyStore::new();
let mut load = store.begin_load(31, 4096).unwrap();
assert_eq!(
load.add(item("weak-rsa", &weak)).unwrap(),
Some(SkipCode::InvalidPrivateKey)
);
assert_eq!(store.publish(load).unwrap().loaded, 0);
}
#[test]
fn logout_invalidates_permits_and_clears_public_and_private_sets() {
let key = ed25519();
let public_blob = key.public_key().to_bytes().unwrap();
let mut store = KeyStore::new();
let mut load = store.begin_load(41, 4096).unwrap();
load.add(item("work", &key)).unwrap();
store.publish(load).unwrap();
let permit = store.authorize(&public_blob).unwrap();
store.logout(42);
assert_eq!(store.state(), VaultState::LoggedOut);
assert!(store.public_identities().is_empty());
assert!(store.sign(&permit, b"must not sign", 0).is_none());
}