Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions rfirma-app/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,9 @@ glib = "0.18"
# secretos dentro del flatpak, Secret Service por D-Bus fuera de él (ADR-0034).
oo7 = "0.5"

# El vigilante de lectores y tarjetas: `libpcsclite` en Linux y `winscard.dll`
# en Windows (ADR-0048).
[target.'cfg(any(target_os = "linux", windows))'.dependencies]
# El vigilante de lectores y tarjetas: `libpcsclite` en Linux, `winscard.dll`
# en Windows y `PCSC.framework` en macOS (ADR-0048).
[target.'cfg(any(target_os = "linux", windows, target_os = "macos"))'.dependencies]
pcsc = "2.9"

# Windows no trae OpenSSL: se compila desde las fuentes, y solo alli (ADR-0035).
Expand Down
12 changes: 6 additions & 6 deletions rfirma-app/src-tauri/src/identity/adapters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub mod folder;
pub mod keyring;
#[cfg(target_os = "macos")]
pub mod macos_store;
#[cfg(any(target_os = "linux", windows))]
#[cfg(any(target_os = "linux", windows, target_os = "macos"))]
pub mod pcsc;
#[cfg(target_os = "macos")]
pub mod pending_macos_keychain;
Expand All @@ -30,21 +30,21 @@ pub use pending_macos_keychain::PendingMacosKeychain as DesktopKeyring;
pub use windows_credential_manager::WindowsCredentialManager as DesktopKeyring;

/// Una consulta puntual a los lectores; `None`, si PC/SC no responde.
#[cfg(any(target_os = "linux", windows))]
#[cfg(any(target_os = "linux", windows, target_os = "macos"))]
pub use pcsc::survey_readers;
/// El vigilante de lectores de esta plataforma.
#[cfg(any(target_os = "linux", windows))]
#[cfg(any(target_os = "linux", windows, target_os = "macos"))]
pub use pcsc::PcscReaderWatch as DesktopReaderWatch;
/// Una consulta puntual a los lectores; sin PC/SC en esta plataforma, nunca hay respuesta.
#[cfg(not(any(target_os = "linux", windows)))]
#[cfg(not(any(target_os = "linux", windows, target_os = "macos")))]
pub fn survey_readers() -> Option<Vec<crate::identity::domain::readers::Reader>> {
None
}
/// Si esta plataforma habla con PC/SC.
pub const SPEAKS_PCSC: bool = cfg!(any(target_os = "linux", windows));
pub const SPEAKS_PCSC: bool = cfg!(any(target_os = "linux", windows, target_os = "macos"));

/// El vigilante de lectores de esta plataforma.
#[cfg(not(any(target_os = "linux", windows)))]
#[cfg(not(any(target_os = "linux", windows, target_os = "macos")))]
pub use readers::UnavailableReaderWatch as DesktopReaderWatch;

/// El token de esta plataforma.
Expand Down
4 changes: 2 additions & 2 deletions rfirma-app/src-tauri/src/identity/adapters/pcsc.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! El vigilante PC/SC de Linux y Windows: espera a `SCardGetStatusChange` sobre todos los lectores y las notificaciones PnP (ADR-0048).
//! El vigilante PC/SC de Linux, Windows y macOS: espera a `SCardGetStatusChange` sobre todos los lectores y las notificaciones PnP (ADR-0048).

use std::ffi::CString;
use std::time::Duration;
Expand Down Expand Up @@ -70,7 +70,7 @@ impl<S: StatusSource> ReaderWatch for StatusWatch<S> {
}
}

/// La fuente real: `libpcsclite` en Linux, `winscard.dll` en Windows.
/// La fuente real: `libpcsclite` en Linux, `winscard.dll` en Windows, `PCSC.framework` en macOS.
#[derive(Default)]
pub struct PcscSource {
context: Option<Context>,
Expand Down
6 changes: 3 additions & 3 deletions rfirma-app/src-tauri/src/identity/application/readers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub fn watches_the_readers(platform: Platform, channel: Channel) -> bool {
match platform {
Platform::Linux => matches!(channel, Channel::Native | Channel::Flatpak),
Platform::Windows => channel == Channel::Windows,
Platform::MacOs => false,
Platform::MacOs => channel == Channel::Native,
}
}

Expand Down Expand Up @@ -190,11 +190,11 @@ fn listed_in(token: &dyn Token, stores: &[Store]) -> Vec<TokenCertificate> {
token.list_across(stores).unwrap_or_default()
}

/// Si el almacén se vuelve a listar al cambiar los lectores: uno de tarjeta, o el de Windows, que copia las suyas.
/// Si el almacén se vuelve a listar al cambiar los lectores: uno de tarjeta, o el de Windows o macOS, que copian las suyas.
fn shows_cards(store: &Store, installed_dir: &Path) -> bool {
matches!(
store.class_under(installed_dir),
StoreClass::Card | StoreClass::Windows
StoreClass::Card | StoreClass::Windows | StoreClass::Macos
)
}

Expand Down
60 changes: 58 additions & 2 deletions rfirma-app/src-tauri/src/identity/application/readers/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,14 +229,14 @@ fn with_no_full_listing_yet_every_store_is_opened() {
}

#[test]
fn the_readers_are_watched_on_linux_and_windows_but_not_on_macos() {
fn the_readers_are_watched_on_linux_windows_and_macos() {
use crate::desktop::domain::channel::Channel;
use crate::desktop::domain::platform::Platform;

assert!(watches_the_readers(Platform::Linux, Channel::Native));
assert!(watches_the_readers(Platform::Linux, Channel::Flatpak));
assert!(watches_the_readers(Platform::Windows, Channel::Windows));
assert!(!watches_the_readers(Platform::MacOs, Channel::Native));
assert!(watches_the_readers(Platform::MacOs, Channel::Native));
}

const CARD_KSP: &str = "Microsoft Smart Card Key Storage Provider";
Expand Down Expand Up @@ -421,3 +421,59 @@ fn without_the_smart_card_service_there_is_no_reader_and_the_listing_goes_on() {
assert_eq!(listed.len(), 1);
assert_eq!(listed[0].label, IN_SOFTWARE);
}

fn the_macos_store() -> Store {
Store::module(format!(
"{}search-list",
crate::identity::domain::store::MACOS_STORE_PREFIX
))
}

fn in_macos(label: &str, der: Vec<u8>) -> TokenCertificate {
TokenCertificate::new(
CertificateRef::new(
the_macos_store(),
"login.keychain-db",
label,
label.as_bytes().to_vec(),
),
der,
)
}

#[test]
fn a_card_in_the_reader_with_no_card_class_certificate_in_macos_is_unreadable() {
let token = Counting::showing(|_store| {
vec![in_macos(
IN_SOFTWARE,
TestAuthority::root("Firmante en el disco").der(),
)]
});
let arranged = Arranged::over(token, vec![the_macos_store()]);
let steps = [true];
let (card, _) = WindowsWithACard::of(Vec::new());

let heard = followed(&arranged, &card, &steps);

assert_eq!(
heard.iter().map(|news| news.reader).collect::<Vec<_>>(),
vec![ReaderStatus::Reading, ReaderStatus::Unreadable]
);
assert_eq!(labels_in(&heard[1]), vec![IN_SOFTWARE]);
}

#[test]
fn a_change_in_the_readers_lists_the_macos_store_and_the_card_modules_again() {
let token = Counting::new();
let arranged = Arranged::over(token, vec![the_macos_store(), Store::module(CARD)]);
arranged.listed_in_full();
arranged.token.asked();
let (card, _) = WindowsWithACard::of(Vec::new());

followed(&arranged, &card, &[false, true]);

let mut asked = arranged.token.asked();
asked.dedup();
assert!(asked.contains(&the_macos_store()));
assert!(asked.contains(&Store::module(CARD)));
}
Loading