Skip to content

Read signed .p7m files as the file inside them - #1096

Merged
KnifeLemon merged 2 commits into
mainfrom
feat/p7m-signed-files
Nov 4, 2026
Merged

KnifeLemon merged 2 commits into
mainfrom
feat/p7m-signed-files

Conversation

@KnifeLemon

@KnifeLemon KnifeLemon commented Nov 4, 2026 •

Copy link
Copy Markdown
Owner

What

Filee now reads .p7m signed files. It takes the signed file out of the envelope, then converts it on from its own format: invoice.pdf.p7m → invoice.pdf, or → invoice.png through PDFium; IT…_FPA01.xml.p7m → XML, or → JSON through the data engine. The .p7m is left as it was.

Why

#1010 asked for Italian e-signatures. A .p7m is a CMS / PKCS #7 SignedData envelope (CAdES), the usual form for Italian e-invoices (*.xml.p7m) and signed PDFs (*.pdf.p7m). It is signed, not encrypted, so the original can be taken out as it is.

How

  • Format: p7m is a read-only document input (FORMAT-RULES rule 5), graded general in FormatFields. Every Italian business sends and receives these, but they come from signing tools, not a mainstream consumer default. The General count in FormatFieldsTests goes from 94 to 95. New installs get it on the documents donut (its category's extensions). Existing libraries learn the extension with a v66 LearnExtensions step, as FIT and KRA did, and no slot changes. The schema goes to 66, because feat(tools): Add background tool #1081 (Add background) took 64 and Read DBML database schemas as text (#854) #1094 (DBML) took 65 on main.
  • Engine Signed/P7mConverter.cs (p7m, built in): reads the file (at most 512 MB) and decodes Base64 / PEM text when it isn't DER. It checks the content type: SignedData is opened with SignedCms.Decode; EnvelopedData (encrypted S/MIME) is refused with its own reason. It unwraps again while the content is itself a SignedData, at most 8 deep. The signature is not verified or trusted: no chain building, no revocation, no network. The code comment says so, and so does the engine description in Settings → Engines. System.Security.Cryptography.Pkcs was already referenced by Filee.Engines.csproj and is used here for the first time.
  • Format inside: the inner name decides when it is a known format (invoice.xml.p7m → XML). Otherwise the first bytes decide: PDF and pictures (WebFormats.Sniff), XML, HTML, RTF, a MIME message (→ EML), ZIP with DOCX / XLSX / PPTX / ODT / ODS / ODP / EPUB recognised by their parts, or plain UTF-8 text.
  • Chaining: nothing in the app could plan from the file inside an input. Archives and .gz only convert among archive formats, so I added a small, general hook:
    • IEnvelopeOpener (Core): Opens(format) and InnerFormat(path, format).
    • JobQueue.RunFileAsync asks the opener before planning. Then target "same format", frames, OCR, translation and the "no route" detail all use the inner format.
    • RoutePlanner.PlanThrough(from, through, to, …) gives the opening step first, then Plan(through, to). So a signed scanned PDF with OCR on goes P7M → PDF → OCR → DOCX.
    • The converter has an edge from p7m to every known format, so donut availability is what some inner file could become. At run time the route is exact, and an XML inside a "to MP4" slot fails with no route xml → mp4.
    • For envelopes, the shaping scan and "only if smaller" (which would copy the .p7m bytes over the result) are skipped.
  • Naming: FormatRegistry.NameWithoutExtension also drops a known inner extension behind .p7m (invoice.pdf.p7m → invoice, contract.pdf.p7m.p7m → contract, report.final.p7m → report.final). Output names and the "ask where to save" name follow it.
  • Strings: engine.p7m.name and engine.p7m.description, plus error.p7m_unreadable, _detached, _encrypted, _unknown_content and _too_large. All are in all 20 language files, vi.json included.
  • Docs: an ENGINES.md table row and a "Signed files" section, and an ARCHITECTURE.md line in "Plan" and the diagram.

Tests

  • P7mTests (Engines, 8). A self-signed RSA certificate is made in the test and content is signed with SignedCms; there are no sample files. The tests cover:
    • a signed PDF coming out byte for byte as invoice.pdf, and going on to invoice.png;
    • a Base64 e-invoice going to the same XML bytes and on to JSON;
    • PEM, double-signed and Base64-inside-DER envelopes;
    • an envelope with no inner name, where the first bytes decide;
    • sniffing DOCX / XLSX / ODT / ZIP / XML / PDF / EML / RTF / TXT, with OLE left unknown;
    • refusals for detached, damaged, not-CMS, encrypted (EnvelopedCms) and unknown content;
    • xml → mp4 as the no-route detail.
  • EnvelopeTests (Core, 13): the name rules, the format and documents donut, the v66 migration running once, PlanThrough (it never takes the direct p7m → png edge for a PDF inside), and JobQueue with a fake opener (outputs named after the inner file, refusal reasons passed through, no route reported from the inner format).
  • Locally: all of Core passes, 985 passed and 3 skipped (FormatFieldsTests included). In Engines, P7mTests and FonTests pass. FonTests now leaves the envelope opener out of its 'nothing makes FON' check: p7m → fon opens a signed FON and doesn't write one. In App, LocalizationTests, DefaultPlacement, *Availability* and *Engine* pass 276 of 276. The build and dotnet format --verify-no-changes are clean.

Closes #1010

🤖 Generated with Claude Code

@KnifeLemon
KnifeLemon force-pushed the feat/p7m-signed-files branch from f61fe18 to 01c483c Compare November 4, 2026 04:08
A .p7m is a CMS / PKCS #7 SignedData envelope (Italian e-invoices
*.xml.p7m, signed PDFs *.pdf.p7m). The new built-in "p7m" engine takes the
signed file out with SignedCms (DER, BER, Base64 or PEM; envelopes signed
again are opened again, at most 8 deep; at most 512 MB). The signature is
not verified or trusted: no chain, no revocation, no network.

The format inside comes from the inner name (invoice.xml.p7m) or the first
bytes. A new IEnvelopeOpener lets JobQueue ask for it before planning, and
RoutePlanner.PlanThrough opens the envelope first and plans on from there,
so P7M -> PDF -> PNG and P7M -> XML -> JSON are one conversion. Outputs are
named after the file inside (invoice.pdf.p7m -> invoice.pdf).

p7m is a read-only document input graded general; the documents donut
learns it (schema 66). New strings are in all 20 languages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@KnifeLemon
KnifeLemon force-pushed the feat/p7m-signed-files branch from 4359688 to 90403b5 Compare November 4, 2026 04:53
#1094 (DBML) took 65, #1101 (.kra~) 66 and #1099 (FLIC) 67.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@KnifeLemon
KnifeLemon merged commit e201472 into main Nov 4, 2026
9 checks passed
@KnifeLemon KnifeLemon mentioned this pull request Nov 4, 2026
1 task done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Format]: .p7m

1 participant