Repository navigation
El DNIe en macOS, con rdnie y los intentos restantes - #2086
Conversation
Implements #2070: Los intentos restantes exactos en el aviso del PIN - PinWarning::AttemptsLeft(n): el diálogo y la terminal dicen cuántos quedan; con 1 o 2, resaltado - Sin intentos no hay diálogo: prompted_until_accepted termina como PIN bloqueado - TokenError lleva los intentos exactos (with_attempts_left) y attempts_left los da
feat(identity): Los intentos restantes exactos en el aviso del PIN
Implements #2071: El DNIe aparece en la lista en macOS, leído con rdnie - DnieToken, adaptador del puerto Token sobre rdnie con un CardOpener inyectable: PcscCard en producción, rdnie-emulator en las pruebas. - MacosToken reparte en tres: llavero, almacén del DNIe (dnie:pcsc, clase tarjeta) y RealToken. - En macOS se vigilan los lectores; sin DNIe legible el almacén no enseña nada. - El DNIe aún no firma: offers responde mecanismo no ofrecido hasta #2075.
…agent/developer/issue-2071
feat(identity): el DNIe aparece en la lista en macOS, leído con rdnie
… la DGP Implements #2072: La línea ilegible de macOS ya no remite a OpenSC ni a la DGP - macOS enseña solo el mensaje genérico; Linux y Windows conservan su ayuda
feat(identity): la línea ilegible de macOS ya no remite a OpenSC ni a la DGP
…ódulo PKCS#11 Implements #2073: El DNIe visto también por un módulo PKCS#11 firma por rdnie - Store::is_the_dnie sustituye a dnie::is_the_dnie
…agent/developer/issue-2073
feat(identity): la fusión de copias prefiere la del DNIe a la de un módulo PKCS#11
Implements #2075: El DNIe firma en macOS con los intentos restantes - DnieToken firma RSA PKCS#1 v1.5 con SHA-256/384/512 por rdnie; offers rechaza SHA-1 y PSS - pin_warning da los intentos exactos y abre la tarjeta solo para leerlos - Si los intentos cambian entre el aviso y el envío, el PIN no se envía - Los errores de rdnie pasan a PIN incorrecto, bloqueado o tarjeta retirada - El login compartido de un lote se anida y su primer fallo corta las firmas que quedan
…agent/developer/issue-2075
Implements #2074: --debug-info y plantilla de issue del DNIe en macOS - sección DNIe con ATR, versión, juego de claves y versión de rdnie, sin datos del titular - la versión de rdnie sale de Cargo.lock en build.rs
La copia que la fusión prefiere desde #2073 firma por el adaptador de rdnie con sus intentos.
…agent/developer/issue-2074
feat(identity): el DNIe firma en macOS con los intentos restantes
…11_MODULE Implements #2077: El manual y la web: el DNIe en macOS y `RFIRMA_PKCS11_MODULE` - la tabla comparativa dice que rFirma lee el DNIe también en macOS, en los cinco idiomas
docs(web): El manual y la web: el DNIe en macOS y RFIRMA_PKCS11_MODULE
… compartido Implements #2076: Un lote con el DNIe pide el PIN una vez - el PIN aceptado se guarda en memoria protegida solo dentro de un login compartido - se borra al cortarse el lote o al cerrar el último nivel
…agent/developer/issue-2076
…agent/developer/fix-pr-2083 # Conflicts: # rfirma-app/src-tauri/src/identity/adapters/macos_store/dnie.rs # rfirma-app/src-tauri/src/identity/adapters/macos_store/dnie/tests.rs
feat(identity): Un lote con el DNIe pide el PIN una vez
feat(identity): --debug-info y plantilla de issue del DNIe en macOS
sgomez
left a comment
There was a problem hiding this comment.
NEEDS_FIXES: un [blocking] y tres [fix].
Revisión de alcance completo (no había revisiones previas) de 68b3d765, contra la spec #2069 y sus sub-issues #2070 a #2077.
Checks: el CI seguía en marcha en 68b3d765 cuando revisé (Imagen nativa, Plataformas / macOS, Plataformas / Windows, Cadena TypeScript, Cadena Java, Cadena Rust, Landing, y Analyze en sus cinco variantes). No he corrido la suite en local; la puerta de checks los espera.
Combinaciones que comparten mecanismo (prompted_until_accepted, el login compartido y el estado Login de DnieToken). Las comprobé con pruebas desechables contra rdnie-emulator, ya borradas y sin commit:
- Lote o firma contra el servidor trifásico, con un PIN incorrecto: falla, ver el [blocking].
- Ciclo del panel, con su
in_one_loginen cada intento, y un PIN incorrecto: funciona. El corte se borra al cerrar cada ciclo y el reintento llega a la tarjeta con los intentos releídos. - Cero intentos en el bucle compartido (panel, lote, terminal): funciona. No hay diálogo y termina en
pinLocked.
[blocking]
dnie.rs:296: en un lote, o en la firma contra el servidor trifásico, un PIN incorrecto no se vuelve a pedir.accepts_the_secretno verifica el PIN, así que el error salta en la primera firma, fuera del bucle del diálogo (historia 10, ADR-0047 regla 5).
[fix]
secret.rs:112: el resaltado con uno o dos intentos solo existe en GTK, y el DNIe por rdnie solo existe en macOS, cuyo diálogo de AppKit no resalta nada (historia 8).dnie.rs:59:Login.pines una copia muerta del PIN, y sus dos pruebas miran estado interno.dnie.rs:165:--debug-infodice «no hay DNIe» cuando hay un DNIe que rdnie no abre, que es justo el caso que hay que reportar.
[note]
ChosenCopy::amongantepone la copia recordada a la del DNIe. Si alguien ya firmaba con el DNIe porRFIRMA_PKCS11_MODULEy tiene recordada esa copia, seguirá firmando por PKCS#11, sin los intentos restantes, hasta que elija otra cosa.seen_dniereinicia la tarjeta para leer el ATR. Un--debug-infolanzado mientras la app firma hace que esa firma acabe como tarjeta retirada.
Lo demás encaja con la spec: ID-557 (dependencias y versiones fijas), ID-558 (offers rechaza SHA-1 y PSS antes del PIN), ID-560 (MacosToken reparte en tres), ID-561 (TD-150), ID-563 (traducción de errores, con faults), ID-565 y #2072 (vigilancia y línea del lector, sin la ayuda de macOS), ID-566, ID-567 (campos sin datos del titular), ID-568 (plantilla), ID-569 (manual y comparativa en los cinco idiomas) y TD-152 (los condicionales siguen en identity/adapters/mod.rs).
| if !has_a_length_the_dnie_admits(secret) { | ||
| return Err(token_error(&rdnie::Error::InvalidPinFormat)); | ||
| } | ||
| let left = self.attempts_left()?; |
There was a problem hiding this comment.
[blocking] En un lote, local o remoto, y en la firma contra el servidor trifásico de una sede, un PIN mal tecleado no se vuelve a pedir: el trámite entero termina con incorrectPin.
Los tres caminos pasan por batch_signed_with_one_secret → secret_for_the_batch, cuyo bucle solo reintenta lo que rechaza accepts_the_secret. Con PKCS#11 ese paso hace el C_Login, y un PIN incorrecto vuelve al diálogo con el aviso releído. Aquí accepts_the_secret solo mira la longitud y los intentos, sin VERIFY, así que acepta cualquier PIN de 8 a 16 caracteres. El fallo llega en la primera sign_with_secret, fuera del bucle, y corta el lote. Incumple la historia 10 de #2069 («me vuelva a pedir el PIN con los intentos que quedan ahora») y la regla 5 del ADR-0047 en esos caminos: la sede se queda sin firma por una errata.
Lo he reproducido con una prueba desechable (ya borrada) contra el emulador, en 3.0 y 4.0: batch_signed_with_one_secret(&dnie, …) con el tecleo [a_wrong_pin(), the_pin()] devuelve Err(Failure { situation: "incorrectPin", attempts_left: Some(2) }) tras un solo diálogo y un PIN enviado. Con PKCS#11 serían dos diálogos y la firma.
rdnie 0.1.1 no tiene un VERIFY sin firma (Session solo expone pin_retries y sign). Hay dos salidas: añadir a rdnie una verificación del PIN y usarla aquí, o que el lote vuelva al diálogo cuando la primera firma falla con IncorrectPin antes de haber firmado nada. Falta una prueba por batch_signed_with_one_secret con el emulador: un PIN incorrecto y luego el correcto deben dar dos diálogos, el segundo con AttemptsLeft(2), y el lote firmado. Hoy a_batch_stops_at_the_first_failure_without_spending_more_attempts fija lo contrario y habrá que ajustarla.
| } | ||
|
|
||
| /// Si el aviso se resalta: el último intento, o quedan uno o dos. | ||
| pub fn stands_out(self) -> bool { |
There was a problem hiding this comment.
[fix] Solo el diálogo GTK usa stands_out, y AttemptsLeft solo sale de rdnie, que solo existe en macOS. El diálogo de macOS (gtk_prompter/appkit.rs, texts_of) mete el aviso en el informativeText sin resaltarlo. Así, con uno o dos intentos el aviso no destaca en el único diálogo que llega a enseñarlo: la historia 8 de #2069 y el criterio «con 1 o 2, resaltado» de #2070 no le llegan a nadie. (No lo marco como blocking porque ID-562 dice «como hoy el último intento», y en macOS el último intento tampoco se resalta hoy.) Haz que el diálogo de AppKit resalte el aviso cuando request.pin_warning.stands_out(), por ejemplo con una etiqueta en negrita junto al campo seguro, y pruébalo en las pruebas de texts_of, sin AppKit.
There was a problem hiding this comment.
Corregido en f67052e. texts_of saca el aviso a AlertTexts.highlighted cuando stands_out(), y la alerta de AppKit lo muestra en negrita encima del campo seguro, dentro de una vista accesoria. Si el aviso no destaca, sigue en el informativeText. Esto también cubre el último intento (FinalTry), como ya hace GTK. Pruebas en texts_of sin AppKit: con 1 y 2 intentos el aviso destaca, con 3 no; además he ajustado la del último intento. He añadido las features NSFont, NSLayoutConstraint y objc2-core-foundation de objc2-app-kit, sin cambios en Cargo.lock.
| shown_attempts: Option<u8>, | ||
| depth: usize, | ||
| cut: Option<TokenError>, | ||
| pin: Option<ProtectedSecret>, |
There was a problem hiding this comment.
[fix] Login.pin se escribe en keep_while_shared y solo lo lee holds_a_pin, que es #[cfg(test)]. Cada firma del lote usa el secreto que le pasa batch_signed_with_one_secret, y ese ya es el «secreto aceptado en memoria protegida» de ID-564. Esto es una segunda copia del PIN en memoria que no sirve para nada. Además, las dos pruebas que la miran (the_accepted_pin_is_kept_while_the_shared_login_lasts_and_wiped_when_it_closes y a_pin_accepted_outside_a_shared_login_is_not_kept) comprueban estado interno, en contra de TD-145. Quita el campo pin, keep_while_shared, holds_a_pin y esas dos pruebas.
|
|
||
| /// El DNIe que abre `opener`; `None` si no hay ninguno legible. | ||
| pub fn seen_dnie<O: CardOpener>(opener: &O) -> Option<SeenDnie> { | ||
| let session = opener.open().ok()?; |
There was a problem hiding this comment.
[fix] seen_dnie devuelve None con cualquier error de open(), y el informe lo escribe como «tarjeta: no hay DNIe». Pero esta sección existe para un caso concreto (la historia 24, y la nota de #2069 sobre el DNIe 4.0 y el chip 19WL34 sin probar): un DNIe que rdnie no consigue abrir. Justo ahí el informe dice que no hay DNIe. Escribe «no hay DNIe» solo con NoReader, NoCard y NotDnie. Para cualquier otro error, escribe el error de rdnie, después de comprobar que su texto no lleva datos del titular. Añade una prueba en stores_tests.rs y otra con el emulador fallando en Step::Opening.
El lote comprueba el PIN del DNIe antes de firmar, el diálogo de AppKit resalta el aviso con uno o dos intentos, DnieToken no guarda una segunda copia del PIN y --debug-info da el error de rdnie de un DNIe que no abre.
sgomez
left a comment
There was a problem hiding this comment.
CLEAN: ningún [blocking] ni [fix]; dos [note].
Revisión incremental desde 68b3d765 (la revisión anterior) hasta f67052e7, contra la spec #2069 y sus sub-issues #2070 a #2077.
Checks: el CI seguía en marcha en f67052e7 cuando revisé (Imagen nativa, Plataformas / macOS, Plataformas / Windows, Cadena TypeScript, Cadena Rust y Analyze (rust)). No he corrido la suite; la puerta de checks los espera. Sí he corrido en macOS las pruebas del adaptador del DNIe (macos_store::dnie) con las desechables de abajo: todas en verde.
Hallazgos anteriores, comprobados en el código:
- [blocking] PIN incorrecto en lote o trifásico: corregido.
accepts_the_secretcomprueba ahora el PIN con una firma desechada, dentro del bucle del diálogo.a_batch_asks_again_after_a_wrong_pin_with_the_attempts_left_and_signs_with_the_right_onepasa porbatch_signed_with_one_secrety habría fallado con el código anterior. - [fix] Resaltado en AppKit: corregido.
texts_ofsaca ahighlightedel aviso questands_out(), la alerta lo pone en negrita sobre el campo y hay pruebas con 1, 2 y 3 intentos y con el último intento. - [fix]
Login.pin: corregido. Se han quitado el campo,keep_while_shared,holds_a_piny sus dos pruebas. - [fix]
--debug-infocon un DNIe que rdnie no abre: corregido.DnieInTheReader::Unopenedlleva el error de rdnie, cuyos textos (rdnie::Error) no tienen datos del titular. Hay una prueba enstores_tests.rsy otra con el emulador enStep::Opening.LegacyDnieyBurnedDnietambién acaban enUnopened, que es lo que interesa.
Combinaciones sobre el mecanismo que ha cambiado (accepts_the_secret y shown_attempts.take(), que solo usa el lote). Las comprobé con pruebas desechables contra rdnie-emulator, en 3.0 y 4.0, ya borradas y sin commit:
- Lote con un intento y un PIN incorrecto: termina en
pinLockedtras un solo diálogo y un PIN enviado. Funciona. - Lote con dos PIN incorrectos y luego el bueno: tres diálogos (3, 2 y 1, con reintento) y el lote firmado. Funciona.
- Lote con cero intentos: sin diálogo y sin PIN enviado,
pinLocked. Funciona. - Tarjeta retirada al firmar: la comprobación termina como
tokenAbsenty no vuelve a pedir el PIN. Funciona. - Ciclo del panel después de un lote con un fallo: el aviso se relee (3 tras el PIN bueno) y firma. No queda ningún
shown_attemptsviejo. Funciona.
[note]
dnie.rs:88: cada lote o firma trifásica con el DNIe hace ahora una firma de más con la clave de firma, sobre una constante y con su propio canal seguro. Es el precio de no tener un VERIFY suelto en rdnie 0.1.1. Cuando rdnie lo exponga, conviene cambiar a él. El comentario cita el ADR-0047, pero el ADR no recoge esta decisión.- Siguen en pie las dos notas anteriores (la copia recordada por delante del DNIe y el reinicio de
seen_dnieen plena firma). No las ha tocado nada de este pase.
Delivers spec #2069.
Closes #2069
Closes #2070
Closes #2071
Closes #2072
Closes #2073
Closes #2074
Closes #2075
Closes #2076
Closes #2077