Actualizaciones incrementales y por qué importan
ISO 32000-2 §7.5.6 Evidence: Standard-backed
De un vistazo
Sección titulada «De un vistazo»Cuando un PDF cambia después de escribirse, la forma correcta de guardarlo no es reescribir el archivo. En su lugar, se añaden al final los objetos modificados y una nueva sección de referencias cruzadas, dejando cada byte original exactamente donde estaba. Esta página explica cómo funciona y por qué esto permite que una firma digital sobreviva a una edición posterior.
Por qué importa
Sección titulada «Por qué importa»Una firma protege un rango de bytes. Si al guardar el cambio de una sola palabra se reescribiera el archivo, todos los desplazamientos de byte se moverían. El rango firmado dejaría de describir el mismo contenido. La firma se rompería, aunque el contenido firmado en sí no se hubiera tocado.
Las actualizaciones incrementales existen para que eso no ocurra. Los bytes originales, incluidos los bytes que cubre una firma, se mantienen en su sitio. Un revisor puede tomar un documento firmado y editado después, y verificar la primera firma contra la revisión original. El revisor ve exactamente lo que se firmó y, por separado, lo que cambió después. Si esto se hace mal, o bien se invalidan firmas válidas o, peor aún, se pierde la capacidad de demostrar qué fue lo que una firma realmente certificó.
La versión breve
Sección titulada «La versión breve»- Una actualización incremental añade objetos nuevos y modificados, una nueva sección de referencias cruzadas y un nuevo tráiler, todo al final del archivo.
- El contenido del archivo original se deja intacto — no se modifica en su ubicación original.
- El nuevo tráiler lleva una entrada
/Prev: el desplazamiento de byte de la sección de referencias cruzadas anterior. Las secciones forman una cadena hacia atrás. - Un lector construye su índice recorriendo esa cadena de la más reciente a la más antigua. Para cualquier número de objeto, prevalece la entrada más reciente.
- Como nada se sobrescribió, el rango de bytes cubierto por una firma anterior sigue siendo idéntico byte a byte — por eso la firma sigue verificándose y el documento se puede recuperar exactamente como se firmó.
Cómo lo aborda NextPDF
Sección titulada «Cómo lo aborda NextPDF»NextPDF escribe el documento base tal como se describe en la página anterior y luego expone las tres cosas que necesita una actualización incremental.
Tras build(), el escritor (src/Writer/PdfWriter.php) conserva:
- el búfer de salida, que se puede recuperar mediante
getBuffer(), para que una actualización pueda añadirse exactamente al final de los bytes existentes; - el desplazamiento de byte de la última sección de referencias cruzadas, mediante
getLastXrefOffset(), que se convierte en el valor/Prevde la nueva sección; - las entradas del diccionario de catálogo, mediante
getCatalogEntries(), para que una actualización que deba volver a emitir el catálogo (por ejemplo, para adjuntar una referencia de firma) no pierda ninguna clave anterior.
Una revisión añadida asigna nuevos números de objeto (o reutiliza los existentes para los objetos que reemplaza) usando el mismo ObjectRegistry, de modo que la numeración de objetos se mantenga coherente entre revisiones. La nueva sección de referencias cruzadas enumera solo los objetos que esta revisión tocó. El nuevo tráiler repite las entradas del tráiler anterior y añade /Prev, que apunta de vuelta a la sección anterior. Esa cadena es lo que sigue un lector.
Donde esto importa con mayor claridad es en la firma. El ByteRangeCalculator de NextPDF (src/Security/Signature/ByteRangeCalculator.php) calcula el array /ByteRange como dos segmentos: todo lo anterior al valor de la firma y todo lo posterior a él — de modo que la firma cubre toda la revisión excepto sus propios bytes. Como una edición posterior se añade en lugar de sobrescribir esos bytes, ese rango nunca se mueve.
- Write base revision Header, body, xref section, trailer — the original bytes.
- Sign A /ByteRange digest covers the whole revision except the signature value itself.
- Edit and save Changed objects + a new xref section are appended; originals are untouched.
- New trailer chains back The appended trailer carries /Prev = offset of the previous xref section.
- Verify The first signature still covers the same unchanged bytes; the chain shows what came after.
Qué dice la evidencia
Sección titulada «Qué dice la evidencia»La regla de añadir únicamente es normativa. Spec: ISO 32000-2, §7.5.6 ISO 32000-2 §7.5.6 establece que el contenido de un PDF puede actualizarse de forma incremental sin reescribir todo el archivo, y que, al hacerlo, los cambios deberán añadirse al final del archivo, dejando intacto el contenido original. Evidence: Standard-backed
La misma cláusula define la mecánica. Una sección de referencias cruzadas para una actualización incremental contiene entradas solo para los objetos que se modificaron, reemplazaron o eliminaron. Los objetos eliminados se dejan en el archivo, pero se marcan como eliminados a través de sus entradas de referencias cruzadas. El tráiler añadido deberá contener una entrada /Prev que indique la ubicación de la sección de referencias cruzadas anterior. La entrada de la actualización para un objeto modificado lleva el desplazamiento de byte de la nueva copia, anulando el desplazamiento antiguo. Un lector construye su información de referencias cruzadas de modo que la copia más reciente de cada objeto sea aquella a la que se accede.
La consecuencia para la firma se enuncia directamente en
Spec: ISO 32000-2, §12.8.1 ISO 32000-2 §12.8.1 : un resumen de rango de bytes
se calcula sobre un rango del archivo — normalmente el archivo entero, excluyendo
el valor de la firma (la entrada /Contents). A continuación, el estándar señala que,
si un documento firmado se modifica y se guarda mediante una actualización incremental, se conservan los datos
correspondientes al rango de bytes de la firma original, de modo que, si
la firma es válida, puede recrearse el estado del documento en el momento de la firma.
Añadir únicamente no es un detalle accesorio. Es la propiedad de la que depende el
modelo de firma.
Ejemplo práctico
Sección titulada «Ejemplo práctico»Así se ve, estructuralmente, un PDF firmado y editado después. La revisión original termina en su propio %%EOF. La segunda revisión se añade debajo.
%PDF-2.0... original objects, including the signature dictionary ...xref0 8... entries for the original revision ...trailer<< /Size 8 /Root 1 0 R >>startxref920%%EOF <-- end of revision 1: the signed bytes stop here9 0 obj <-- revision 2, appended<< /Type /Annot /Subtype /Text /Contents (added after signing) >>endobjxref0 19 0 obj-entry...8 90000001740 00000 ntrailer<< /Size 10 /Root 1 0 R /Prev 920 >>startxref1980%%EOFUn validador lee el último tráiler, ve /Prev 920 y obtiene la cadena completa. Puede verificar la firma contra los bytes hasta el primer %%EOF, que no han cambiado. Luego puede informar por separado de que la revisión 2 añadió una anotación. El historial está en el archivo. No se ocultó nada mediante sobrescritura.
Concepto erróneo habitual
Sección titulada «Concepto erróneo habitual»La trampa es «una actualización incremental significa que el cambio es pequeño, así que es inofensivo». Añadir tiene que ver con la preservación de bytes, no con el tamaño. Una actualización incremental puede añadir una gran cantidad de contenido. Lo que la convierte en una actualización incremental es que no toca los bytes que ya estaban ahí. El corolario también suele pasarse por alto: una herramienta que «optimiza» o «lineariza» un PDF firmado reescribiéndolo desde cero producirá un archivo más pequeño y limpio, pero también una firma rota, porque el rango de bytes firmado ya no existe. No es lo mismo guardar un PDF firmado que volver a guardarlo.
Límites y fronteras
Sección titulada «Límites y fronteras»Añadir únicamente protege los bytes. No indica, por sí solo, si los cambios añadidos estaban autorizados. Una segunda revisión puede añadir legítimamente una segunda firma, o puede añadir contenido que el primer firmante nunca pretendió. Decidir cuál es el caso corresponde a la validación de firmas y a la política de detección de modificaciones (DocMDP). Añadir es la base que hace posible ese análisis, no el análisis en sí.
Esta página tampoco cubre cómo se calculan y se enlazan los dos rangos de bytes de una firma, ni qué comprueba una validación completa. Esos son temas separados. Y la garantía descrita aquí se aplica a archivos escritos y actualizados por un escritor conforme: un archivo cuyas revisiones anteriores ya estaban mal formadas no pasa a estar bien formado simplemente por añadirle contenido.
Mini-FAQ
Sección titulada «Mini-FAQ»¿Cómo sé cuántas revisiones tiene un PDF? Contar los marcadores %%EOF y seguir la cadena /Prev desde el último tráiler. Cada sección de referencias cruzadas alcanzada es una revisión guardada.
¿Eliminar un objeto lo quita del archivo? No. Una actualización incremental marca el objeto como eliminado en su entrada de referencias cruzadas, pero los bytes del objeto permanecen en revisiones anteriores. «Eliminado» significa «no referenciado por la revisión actual», no «borrado».
¿Puede una actualización incremental cambiar la versión del PDF? Sí, estableciendo la entrada /Version en el catálogo de la revisión añadida. La cabecera permanece tal como se escribió. La entrada /Version del catálogo tiene prioridad si indica una versión posterior.
Documentos relacionados
Sección titulada «Documentos relacionados»- Qué es realmente un PDF — el modelo de objetos y la única sección de referencias cruzadas que una actualización extiende.
- Cómo se sitúan las firmas en un PDF — el mecanismo de rango de bytes que las actualizaciones incrementales están diseñadas para proteger.
- Validar una firma correctamente — qué comprueba una validación correcta a lo largo del historial de revisiones de un archivo.
Glosario
Sección titulada «Glosario»- Actualización incremental — guardar un cambio añadiendo los objetos modificados, una nueva sección de referencias cruzadas y un nuevo tráiler al final del archivo, sin alterar los bytes existentes.
/Prev— la entrada del tráiler (o del flujo de referencias cruzadas) que contiene el desplazamiento de byte de la sección de referencias cruzadas anterior. Enlaza las revisiones en una cadena hacia atrás.- Revisión — el estado del archivo capturado por una sección de referencias cruzadas y su tráiler. Un archivo con N secciones de referencias cruzadas tiene N revisiones.
/ByteRange— el array de un diccionario de firma que indica los dos segmentos de bytes que cubre el resumen de la firma (todo excepto el valor de la firma en sí).- Rango de bytes firmado — los bytes exactos sobre los que se calculó el resumen de la firma. Las actualizaciones incrementales existen para que estos bytes nunca se muevan ni se sobrescriban.