feat: add DocMDP validation handler for PDF signature permissions

- Implement DocMdpHandler to validate PDF Document Modification Detection and Prevention (DocMDP)
- Add allowsAdditionalSignatures() method to check if PDF permits additional signatures
- Support detection of DocMDP level 1 (no changes allowed) certification
- Parse PDF signature dictionaries and transformation parameters
- Prevent signatures on DocMDP level 1 certified documents per PDF specification
- Enable compliance with PDF document certification and signature workflows

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
This commit is contained in:
Vitor Mattos 2025-12-08 12:03:13 -03:00
parent 5bbc90b111
commit 0dd43eb364
No known key found for this signature in database
GPG key ID: 6FECE2AD4809003A

View file

@ -166,7 +166,7 @@ class DocMdpHandler {
* @return array Array of objects with keys: objNum, dict, position
*/
private function parsePdfObjects(string $content): array {
if (!preg_match_all('/(\d+)\s+\d+\s+obj\s*(<<.*?>>)\s*endobj/s', $content, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {
if (!preg_match_all('/(\d+)\s+\d+\s+obj(.*?)endobj/s', $content, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {
return [];
}
@ -174,7 +174,7 @@ class DocMdpHandler {
foreach ($matches as $match) {
$objects[] = [
'objNum' => $match[1][0],
'dict' => $match[2][0],
'dict' => trim($match[2][0]),
'position' => $match[2][1],
];
}
@ -458,16 +458,11 @@ class DocMdpHandler {
return $this->validateDictionaryEntries($sigDict);
}
/**
* Find signature dictionary with /Reference entry
*
* @param array $objects Parsed PDF objects
* @return string|null Dictionary content or null
*/
private function findSignatureDictionary(array $objects): ?string {
foreach ($objects as $obj) {
if (preg_match('/\/Reference\s*\[/', $obj['dict'])) {
return $obj['dict'];
$dict = $obj['dict'];
if (preg_match('/\/Type\s*\/Sig\b/', $dict) && preg_match('/\/Reference\s*\[/', $dict)) {
return $dict;
}
}
return null;
@ -480,7 +475,7 @@ class DocMdpHandler {
* @return bool True if all required entries are valid
*/
private function validateDictionaryEntries(string $dict): bool {
if (preg_match('/\/Type\s*\/(\w+)/', $dict, $typeMatch) && $typeMatch[1] !== 'Sig') {
if (!preg_match('/\/Type\s*\/Sig\b/', $dict)) {
return false;
}