Repository navigation
feat: rFirma en macOS: firmar con los certificados del llavero y la integración con el sistema - #2042
Conversation
… Almacén de macOS Implements #2001: El ADR-0040 recoge la regla «en macOS, la API de macOS, salvo el DNIe» - ADR-0040 reescrito en su sitio; glosario con el Almacén de macOS y el nuevo orden
… nativo de macOS Implements #2011: El PIN de una tarjeta por PKCS#11 se pide con un diálogo nativo de macOS - Alerta de AppKit con NSSecureTextField: hoja sobre la ventana de origen, modal de la app sin ella - El puerto se prueba con una alerta doble tras la costura SecureAlert; cerrar es cancelar - Sale PendingMacosPinDialog y su fila de la tabla del ADR-0040
feat(macos): el PIN de una tarjeta por PKCS#11 se pide con un diálogo nativo de macOS
docs(macos): la regla «en macOS, la API de macOS, salvo el DNIe» y el Almacén de macOS
Implements #2004: El Almacén de macOS lista los certificados del llavero - `MacosToken` lista las identidades del llavero por Security.framework sin pedir nada y apunta en `key_provider` el token del sistema de la clave, si lo hay. - `StoreClass::Macos` comparte el puesto del Almacén de Windows en el orden de copias. - Firmar con el llavero queda para #2005: por ahora el adaptador lo rechaza.
…agent/developer/issue-2004 # Conflicts: # packaging/flatpak/sources.lock # rfirma-app/src-tauri/Cargo.toml
feat(identity): el Almacén de macOS lista los certificados del llavero
…aria de macOS Implements #2014: La CA local se instala en la confianza de la persona usuaria de macOS - MacosUserTrust sustituye a PendingMacosKeychainTrust: Security.framework en el dominio de la persona usuaria, con el diálogo de contraseña del sistema. - La confianza de macOS es un perfil más (MACOS_USER_TRUST) con la marca StoreBrand::Macos. - Cancelar el diálogo se distingue de un fallo y deja la CA como no instalada.
Implements #2019: El aviso de arranque de macOS es una alerta nativa - NSAlert crítica modal, sin ventana de Tauri; sin hilo principal queda el stderr
…agent/developer/issue-2014 # Conflicts: # rfirma-app/src-tauri/Cargo.toml # rfirma-app/src-tauri/src/desktop/domain/status.rs
feat(macos): El aviso de arranque de macOS es una alerta nativa
feat(macos): la CA local se instala en la confianza de la persona usuaria de macOS
…agent/developer/issue-2021
Implements #2017: rFirma sabe quién abre afirma:// en macOS y deja elegirse - LaunchServices tras la costura Workspace; NSWorkspace la implementa y se prueba a mano - Leer nunca escribe; retirar rFirma devuelve el esquema a otra aplicación que lo declare - Sale PendingMacosLaunchServices y su fila de la tabla del ADR-0040
ci(macos): la entrega vuelve a publicar el paquete de macOS
…agent/developer/issue-2017 # Conflicts: # docs/adr/0040-macos-en-apple-silicon-como-tercera-plataforma.md # rfirma-app/src-tauri/Cargo.toml
Implements #2005: Firmar con un certificado del Almacén de macOS - Rust resume y `SecKeyCreateSignature` firma el resumen: PKCS#1 v1.5, PSS y ECDSA en DER, con SHA-1 como rama explícita de la tabla de algoritmos. - Los errores de Security.framework, CryptoTokenKit y PC/SC se traducen por dominio y código.
…agent/developer/issue-2005
feat(identity): Firmar con un certificado del Almacén de macOS
feat(macos): rFirma sabe quién abre afirma:// en macOS y deja elegirse
Implements #2006: APPLE y -store apple acotan al Almacén de macOS - Solo en macOS; en Linux y Windows siguen respondiendo SAF_08 y almacén desconocido
…agent/developer/issue-2006
feat(sede): APPLE y -store apple acotan al Almacén de macOS
…l asistente y el estado Implements #2015: La interfaz rotula el Almacén de macOS en la lista, el asistente y el estado - Marca macos en el selector, el asistente y el estado, con su clave de i18n y traducciones - Historias de macOS en CertificateSelect, SetupWizard y StatusView
…icio de sesión en macOS Implements #2007: «Instalar certificado» lleva el `.p12` al llavero de inicio de sesión en macOS - el puerto Token dice si instala en el sistema: entonces no hay carpeta ni PIN del llavero de rFirma - el .p12 se prueba antes en un llavero desechable: contraseña incorrecta o sin clave no dejan nada - Preferencias lista y quita los certificados del Almacén de macOS
Implements #2009: El vigilante de lectores corre en macOS - el adaptador PC/SC se compila en macOS y la plataforma vigila los lectores - al cambiar un lector se vuelve a listar el Almacén de macOS junto a los módulos PKCS#11
…agent/developer/issue-2009
feat(identity): La interfaz rotula el Almacén de macOS en la lista, el asistente y el estado
feat(dev): just dev-handler funciona en macOS
feat(identity): «Instalar certificado» lleva el .p12 al llavero de inicio de sesión en macOS
Implements #2024: rFirma se actualiza sola en macOS - Canal propio `Channel::MacOs`: lee /macos/latest.json y exige su entrada darwin-aarch64 - El plugin updater también en macOS, con la misma pública minisign que Windows - La entrega empaqueta, firma y sirve el .app.tar.gz en /macos/ con su latest.json
…agent/developer/issue-2024
feat(macos): rFirma se actualiza sola en macOS
feat(identity): el vigilante de lectores corre en macOS
…la DGP Implements #2016: La línea del lector «ilegible» en macOS ofrece OpenSC y la DGP - plataforma macos en CardHelpPlatform, con su historia y su ficha de diseño
feat(signing): La línea del lector «ilegible» en macOS ofrece OpenSC y la DGP
docs(adr): El ADR-0040 deja las tarjetas y el DNIe de macOS pendientes de #2043
…agent/developer/issue-2044
fix(identity): En macOS no se muestra la línea del lector mientras no se lean tarjetas
… de software Implements #2008: El Almacén de macOS no lista las identidades con clave en un token - el filtro está en el listado, no en el adaptador, para probarlo con un doble del puerto Token
…agent/developer/issue-2008
feat(identity): El Almacén de macOS no lista las identidades con clave en un token
Implements #2020: --debug-info en macOS - Secciones de macOS como las de Windows; Linux y Windows no cambian
feat(debug-info): --debug-info en macOS
sgomez
left a comment
There was a problem hiding this comment.
NEEDS_FIXES. Revisión completa (no había revisión previa) del head 84765faf, contra la spec #2000 y cada sub-issue que cierra.
Checks: CI en rojo para 84765faf en dos jobs que sí corrieron (código, no infraestructura):
- Cadena Rust — https://github.com/sgomez/rfirma/actions/runs/39292525257/job/118197039131 — la puerta CRAP: 7 funciones de macOS por encima de 30.
- Cadena TypeScript — https://github.com/sgomez/rfirma/actions/runs/39292525257/job/118197038884 —
build-tree.test.sh: «una candidata aparece en macos/ o en su latest.json». El job se paró encheck-repo, así quecheck-ts(biome, tsc, vitest) no llegó a correr: la interfaz queda pendiente de ese job.
Java, Imagen nativa, Plataformas Windows y macOS, Landing y CodeQL están en verde para ese sha. No he corrido la suite en local.
Combinaciones de mecanismos compartidos
-store apple(#2006) × el filtro de identidades en un token (#2008): falla. Lo confirmé con una prueba desechable encommand_line/tests/platform.rs(listaliases -store applecon un doble que trae una identidad de software y otra conkey_provider): la salida fueSOFTWARE\nEN_TOKEN\n. La prueba está borrada y no se ha subido.- Revisadas leyendo el código, sin fallo:
APPLEde la sede × filtro (pasa porholders_only); «Tus certificados» × filtro (certificates_by_class); recarga por lectores × Almacén de macOS (no se activa en macOS por #2044); actualizador ×Channel::MacOs×--debug-info; la CA local × marca y estado.
[blocking]
- CI rojo, Cadena Rust (CRAP):
keychain.rs(over_a_digest156,only_if_it_brings_a_usable_identity,situation_of,every_certificate),macos_trust.rs(trust_of,withdraw) ymacos_store/tests.rs(verifies). En Linux esos módulos no se compilan, así que salen sin cobertura. - CI rojo, Cadena TypeScript: falla el test de
build-treey no el código. Ver el comentario en la línea. - En macOS, la línea de órdenes (
-store apple,listaliases,sign) lista las identidades que tienen la clave en un token del sistema, y ID-554 las excluye. Ver el comentario en la línea. - Preferencias deja quitar cualquier certificado del Almacén de macOS, no solo los que instaló rFirma (ID-534, historia 20), y quitarlo borra también la clave privada. Ver el comentario en la línea.
[fix]
5. El ADR-0040 se contradice con lo que entrega la PR (firma por el llavero, alerta nativa de arranque).
6. CONTEXT.md: el nombre del tipo está mal y queda una frase de ID-532, que cayó.
7. startup_dialog.rs: sin hilo principal, el aviso de arranque ya no sale por ningún sitio.
8. Título de la PR: no lleva prefijo convencional, así que just release no lo pondrá en el CHANGELOG (solo toma feat/fix/perf). Además anuncia «el DNIe por PKCS#11 y el tap de Homebrew», que la spec dejó para #2043 y #2045. Propuesta: gh pr edit 2042 --title "feat: rFirma en macOS: firmar con los certificados del llavero de macOS y la integración con el sistema".
[note]
- En macOS,
-store mozillayMOZ_UNI(el ámbito NSS es «no tarjeta») también traen el Almacén de macOS, igual que ya pasa con el de Windows en la sede. - Cada arranque sin trámite llama a
refresh_local_ca_trust, que instala la CA si falta. En macOS eso abre el diálogo de contraseña del sistema en cada arranque mientras la persona lo cancele. Windows hace lo mismo; si no es lo que se quiere, conviene decidirlo en la spec.
| "$construye" "$con_candidata" "$tmp/arbol-rc-mac" "$tmp/rfirma.asc" "$SIN_FIRMA" > /dev/null 2>&1 \ | ||
| || fail "la construccion con una candidata de macOS en la serie falla" | ||
| if [ "$(jq -r .version "$tmp/arbol-rc-mac/macos/latest.json")" = "0.4.10" ] \ | ||
| && [ -z "$(find "$tmp/arbol-rc-mac/macos" -name '*rc*' -print -quit)" ]; then |
There was a problem hiding this comment.
[blocking] Esto es lo que pone en rojo la Cadena TypeScript (https://github.com/sgomez/rfirma/actions/runs/39292525257/job/118197038884). -name '*rc*' casa con cualquier rFirma_X_aarch64.app.tar.gz, porque «arch64» contiene «rc». Así que el test falla aunque no haya ninguna candidata en macos/. El de Windows no tropieza porque allí el fichero es x64. Busca la versión candidata, por ejemplo -name '*-rc*' o -name '*0.4.11-rc.1*'.
There was a problem hiding this comment.
Arreglado en c849330: el test busca -name '*-rc*', que ya no casa con «aarch64». No he podido correr build-tree.test.sh en este Mac (sale con 127 sin salida, le faltan las herramientas del árbol); lo juzga el CI.
| } | ||
|
|
||
| /// El algoritmo de Security.framework que firma un resumen ya calculado. | ||
| fn over_a_digest(algorithm: SignatureAlgorithm) -> Algorithm { |
There was a problem hiding this comment.
[blocking] La puerta CRAP de la Cadena Rust sale en rojo (https://github.com/sgomez/rfirma/actions/runs/39292525257/job/118197039131). En Linux este módulo, macos_trust.rs y macos_store/tests.rs no se compilan, así que miden 0 % de cobertura: over_a_digest da 156, y only_if_it_brings_a_usable_identity, situation_of, every_certificate, MacosUserTrust::trust_of, MacosUserTrust::withdraw y verifies también pasan de 30. Hay que hacer lo mismo que con CNG: un macos_allow en el --allow de la receta crap, y su propia puerta cargo crap --path en el carril de macOS, que es el que los ejecuta, como hace test-windows con windows_allow.
There was a problem hiding this comment.
Arreglado en c849330: macos_store_allow (la carpeta macos_store/) y macos_trust_allow van al --allow de crap, y test-macos pasa a cargo llvm-cov con su propia puerta cargo crap --path para cada uno, como test-windows; el job de macOS instala llvm-cov crap. Medido en macOS, quedaban dos por encima de 30 con cobertura: verifies (las pruebas no tienen cobertura) y MacosUserTrust::withdraw (0 %, pide la contraseña). Los partí: digest_of y with_pss_padding por un lado, y untrusted y delete_the_copies_of por otro. just test-macos sale en verde en local.
| .into_iter() | ||
| .filter(|certificate| certificate.reference().store().class() == StoreClass::Windows) | ||
| .collect(), | ||
| (StoreScope::Macos, _) => certificates |
There was a problem hiding this comment.
[blocking] #2006 × #2008. En macOS, la línea de órdenes saca las identidades con la clave en un token del sistema, y ID-554 dice que esas no salen. El filtro is_a_macos_token_identity está en holders_only y en certificates_by_class (identity/application/certificates.rs), pero la línea de órdenes lista por SeenStores::certificates → DesktopToken.list_across, que no pasa por ninguno de los dos. keychain::signable_certificates tampoco las quita. Lo comprobé con una prueba desechable: listaliases -store apple sobre un doble con una identidad de software y otra con with_key_provider(...) devuelve las dos. Arreglo: que el adaptador no las entregue (en signable_certificates, descartar las que tienen token_holding). Así basta con un solo filtro para la ventana, la sede y la línea de órdenes. Añade la prueba en command_line/tests/platform.rs.
There was a problem hiding this comment.
Arreglado en c849330, aunque no donde proponías. Filtrar en signable_certificates solo se puede probar con un token del sistema de verdad, y en el CI no hay ninguno; un doble en platform.rs no lo habría puesto en rojo. Así que el filtro va en within_the_scope, por donde pasan listaliases y sign con cualquier -store, y lo fija on_macos_the_command_line_leaves_out_the_identities_with_their_key_in_a_system_token (con -store apple y sin -store). Antes del arreglo falló con SOFTWARE\nEN_TOKEN\n. La ventana y la sede siguen con su filtro en holders_only y certificates_by_class.
| .filter((certificate) => certificate.stores.includes("installed")) | ||
| .filter( | ||
| (certificate) => | ||
| certificate.stores.includes("installed") || certificate.stores.includes("macos"), |
There was a problem hiding this comment.
[blocking] Con esto, Preferencias trata todo el Almacén de macOS como «instalado desde rFirma». Además, remove_installed se salta la comprobación de origen cuando installs_in_the_system(). Resultado: cualquier identidad del llavero tiene botón de «Quitar», y keychain::remove → SecIdentity::delete borra el certificado y su clave privada. Un certificado de la FNMT generado en Safari, sin copia de la clave, se pierde de un clic. ID-534 y la historia 20 hablan de quitar «un certificado instalado desde rFirma». Hay que marcar lo que importa rFirma (por ejemplo, una etiqueta propia en la identidad al importar, o las huellas guardadas en su carpeta), sacar solo esos en Preferencias y rechazar en remove_installed lo que no lleve la marca.
There was a problem hiding this comment.
Arreglado en c849330. Al importar, MacosToken apunta las huellas SHA-1 de las identidades del .p12 en un fichero de la carpeta del Almacén de rFirma (macos_store/installs.rs). Al quitar, borra la suya. Un puerto nuevo, Token::installed_in_the_system, dice si una referencia está apuntada, y remove_installed rechaza con CertificateNotFound lo que no sea del Almacén de rFirma ni esté apuntado, también cuando installs_in_the_system(). ListedCertificate y CertificateView llevan un campo installed nuevo, con el contrato regenerado, y Preferencias filtra por él, no por la clase macos. Lo que se instaló antes de este cambio no tiene huella apuntada, así que ya no sale en Preferencias.
| del llavero de macOS como el de CNG en Windows. | ||
| CAdES y XAdES pasan por el puente y el validador de AutoFirma da por válidas las PAdES. El PIN | ||
| de una tarjeta por PKCS#11 lo pide una alerta de AppKit con campo seguro, modal sobre la ventana | ||
| que lo pidió (ADR-0047); el Almacén de macOS ya lista los certificados del llavero, pero aún no |
There was a problem hiding this comment.
[fix] Esto contradice lo que entrega la propia PR: el Almacén de macOS ya firma (#2005), y la fila de la tabla «Diálogo GTK de fallo de arranque | El texto sale por la salida de error» (línea 19) ya no es cierta tras #2019. ID-527 pide sacar de la tabla lo que se ha sustituido. Quita esa fila y la frase «pero aún no firma con ellos…».
There was a problem hiding this comment.
Arreglado en c849330: fuera la fila «Diálogo GTK de fallo de arranque» y la frase «pero aún no firma con ellos…».
| tarjeta. Windows pide su contraseña o su PIN con su propio diálogo. | ||
| _Avoid_: almacén del sistema, almacén CNG, almacén MY | ||
|
|
||
| **Almacén de macOS** (Rust `StoreClass::MacOs`): |
There was a problem hiding this comment.
[fix] La variante se llama StoreClass::Macos, no StoreClass::MacOs. Y «Los de una tarjeta que macOS lee con su controlador son de la tarjeta, no suyos» describe ID-532, que cayó. Con ID-554 esas identidades no salen, y así debería decirlo la definición.
There was a problem hiding this comment.
Arreglado en c849330: la variante es StoreClass::Macos, y la definición dice ahora que son los certificados con clave de software y que los que tienen la clave en una tarjeta o en un token del sistema no salen. También cambié «su PIN» por «su permiso», porque una clave de software no tiene PIN.
| use objc2_app_kit::{NSAlert, NSAlertStyle, NSApplication}; | ||
| use objc2_foundation::NSString; | ||
|
|
||
| let Some(mtm) = MainThreadMarker::new() else { |
There was a problem hiding this comment.
[fix] Si no hay MainThreadMarker, la función vuelve sin decir nada y el proceso sale con 1 sin rastro. Antes, por lo menos, el motivo iba a stderr. Deja eprintln!("{}", detail_text(failure)); en esta rama (o antes de la alerta) como respaldo.
There was a problem hiding this comment.
Arreglado en c849330: sin MainThreadMarker, detail_text(failure) va a stderr antes de volver. Una precisión: report_and_exit ya escribía rfirma: {failure} en stderr antes de la alerta, así que el proceso no salía sin dejar rastro. Lo que faltaba era el detalle con la dirección del repositorio.
…a línea de órdenes deja fuera las identidades de un token y la puerta CRAP mide macOS en su carril (#2042) Co-Authored-By: Claude <noreply@anthropic.com>
|
Hallazgo 8 (título), hecho: la PR se titula ahora «feat: rFirma en macOS: firmar con los certificados del llavero y la integración con el sistema», el título de la spec #2000, y ya no anuncia el DNIe ni el tap de Homebrew. Los demás hallazgos [blocking] y [fix] tienen su respuesta en su hilo (c849330). Los [note] siguen sin tocar. |
sgomez
left a comment
There was a problem hiding this comment.
CLEAN. Revisión incremental desde 84765faf (la revisión anterior) hasta c8493300: un commit, el de la pasada de arreglos.
Checks: a la hora de esta revisión, el CI de c8493300 seguía en marcha en: Cadena Rust, Cadena TypeScript, Imagen nativa, Plataformas / macOS y Plataformas / Windows. Java, Landing, Alcance y los análisis de CodeQL están en verde. No he corrido la suite en local. Los dos [blocking] de CI de la revisión anterior (1 y 2) están arreglados en el código; que pasen lo dirá la puerta de checks del orquestador.
Hallazgos anteriores, comprobados en el diff
- CRAP:
macos_store/**ymacos_trust.rsvan al--allowdecrap(cargo-crap acepta globs de ruta), ytest-macospasa allvm-cov, con su propia puertacargo crap --path, igual quetest-windows.verifiesywithdrawse han partido. Arreglado. build-tree.test.sh: el patrón*-rc*ya no casa con «aarch64». Arreglado.- Línea de órdenes × identidades en un token: el filtro está ahora en
within_the_scope, por donde pasanlistaliasesysign, y lo fijaon_macos_the_command_line_leaves_out_the_identities_with_their_key_in_a_system_token. Arreglado. - Preferencias × llavero: se apuntan las huellas al importar,
remove_installedrechaza lo que no está apuntado y la interfaz filtra porinstalled. Arreglado, y probado en los dos lados (a_macos_store_certificate_rfirma_did_not_install_is_not_removedy «keeps only the macOS store ones that rFirma installed»). - ADR-0040, 6.
CONTEXT.md, 7.startup_dialog.rsy 8. el título de la PR: arreglados.
Combinaciones sobre lo que ha tocado el arreglo
- La marca
installed× la copia recordada: falla en el código, pero hoy no se llega en macOS. Lo confirmé con una prueba desechable encertificates/tests/macos_store.rs: una identidad del llavero apuntada por rFirma, más otra copia del mismo certificado en un perfil de Firefox que es la recordada. La fila sale coninstalled: falseyremove_installedla rechaza conCertificateNotFound. En macOS no se descubren perfiles de Firefox (no se busca enApplication Support), así que no la he marcado como bloqueante. La prueba está borrada y no se ha subido. Ver el[fix]. - La marca
installed× la recarga por lectores (readers.rs,rows_keeping_handles): esa ruta no pone la marca. Hoy no importa, porque los lectores no se vigilan en macOS (watches_the_readers). Ver el[fix]. within_the_scope×-storede Windows, NSS y PKCS#11: el filtro solo quita la claseMacosconkey_provider, así que en Linux y Windows no cambia nada.remove_installed× Almacén de rFirma en Linux y Windows:class_under == Installedcumple el papel de la antigua comprobacióninstalled_directory_under, yWindowsTokenno sobrescribeinstalls_in_the_system.
[fix]
listed_rowsmarcainstalledmirando solo la copia elegida, y solo en esa ruta. Ver el comentario en la línea.
[note]
- En la respuesta del hilo de
CONTEXT.mdse dice que «su PIN» pasó a «su permiso», pero la línea sigue diciendo «macOS pide su contraseña o su PIN con su propio diálogo». Si se quería cambiar, falta. installs::recordvuelve a apuntar una huella que ya estaba si se reinstala el mismo.p12. Es inofensivo, porqueforgetquita todas las líneas iguales.
| row.installed = row.installed | ||
| || listed | ||
| .get(&row.id) | ||
| .is_some_and(|reference| token.installed_in_the_system(installed_dir, &reference)); |
There was a problem hiding this comment.
[fix] La marca de lo que rFirma instaló en el sistema está en el sitio equivocado:
- solo mira la copia elegida (
listed.get(&row.id)), mientras que la del Almacén de rFirma (installed_referenceenChosenCopy::among) mira todas las copias. Con una prueba desechable, si la copia recordada es otra (un perfil de Firefox), la fila sale coninstalled: falseyremove_installedla rechaza. Es lo mismo quea_row_signed_with_a_remembered_browser_copy_still_removes_its_installed_copyexige en Linux. - solo se aplica en
listed_rows.CardListing::relisted(readers.rs) construye las filas conrows_keeping_handlesy no la pone, así que en cuanto se vigilen los lectores en macOS, Preferencias perderá esos certificados tras meter una tarjeta.
Qué hacer: llevar la decisión a rows_handled_by, para que installed_reference sea la primera copia de entre todas para la que installed_by_rfirma(token, installed_dir, copy) sea cierto (pasando el token o un predicado), y quitar este map. Así remove_installed también recibe esa copia por installed_copies y no la elegida. Añade la prueba de la copia recordada para el llavero.
Delivers spec #2000.
Closes #2000
Closes #2001
Closes #2002
Closes #2004
Closes #2005
Closes #2006
Closes #2007
Closes #2008
Closes #2009
Closes #2011
Closes #2014
Closes #2015
Closes #2016
Closes #2017
Closes #2018
Closes #2019
Closes #2020
Closes #2021
Closes #2024
Closes #2044
Closes #2046