github fcorbelli/zpaqfranz 65.19
Windows 32/64 bit executables and source code

one hour ago

zpaqfranz 65.19 update can be trusted (more or less)

IMPORTANT: A DIGITAL SIGNATURE IS NOT A WARRANTY OF ANY KIND

My digital signature does not make me responsible for whether this software works, fails, or meets your expectations. zpaqfranz is open-source software, provided free of charge, which you are entirely free to use or not to use, at your own discretion.

The signature is simply a security measure designed to reduce the risk of installing an update from an untrusted source. It helps you verify that an update really comes from me and has not been tampered with.

This isn't a perfect solution (after all, nothing is), but it should be seen as an improvement: a way to mitigate a potential risk. In short, a service.

The best approach (and I've made all the necessary technical information available) is to compile the program from source. This is standard practice in the non-Windows world (although binary packages and even containers are also very common). In short, I hope you can see my commitment to the highest possible level of transparency.

And one more thing: YOU are always in control. YOU decide whether to run the update command. zpaqfranz NEVER updates itself automatically without an explicit command from YOU. No hidden "Windows update" here 😄

Ok, going back...

update downloads a new zpaqfranz from my site and replaces the one you have.
What makes the new exe "good" is my digital signature: an Italian qualified
electronic signature. In 65.19, that check
has become stricter: zpaqfranz knows my certificate, byte by byte, and accepts
nothing else. On Windows, BTW, downloads now use HTTPS, made by Windows itself
(urlmon, then WinINet), with its own certificates.
Not a big improvement (the signature is what counts), but better than nothing.

zpaqfranz update                    is there a newer one? (the signature is checked)
zpaqfranz update -force             if there is, install it
zpaqfranz update -paranoid          check my signature yourself (openssl, or a web site)
zpaqfranz update -force -kill       install it regardless (and, if the signature cannot
                                    be verified, only after you type its URL)

My signature: take it or leave it

The certificate is an Italian qualified electronic signature, on a SIM.
In Italy it has full legal value but outside Italy it is not widely used.

I use it because I already pay for it, instead of paying for one more
certificate to some "friend" of Microsoft. So, dear user: if you're comfortable
with it, use it. If you are not... don't.

Downloading an executable from the Internet always requires some care, no matter
where it comes from: it is a common problem, not specific to zpaqfranz. If you
have doubts, take zpaqfranz from GitHub or SourceForge, or compile it
yourself
: I made a tool for that, zpaqbuilder.

I cannot do much more than this (and it is already much more than 99.99% of the
software on the Internet).

BTW: if you are willing to pay for the M$ certificate for me, I'll adopt one :)

And:

  • update on Windows uses HTTPS, and falls back to HTTP by itself where HTTPS
    cannot work (an old Windows Server, zpaqfranz32.exe on XP): the signature is checked anyway.
  • update is only for zpaqfranz.exe, zpaqfranzhw.exe and zpaqfranz32.exe:
    zpaqfranz-full and the XP build refuse it (see the final note).
  • When my certificate expires (2028-12-24), the zpaqfranz you have cannot know
    the next one, and refuses it, as it must refuse a fake. update -force -kill
    can install it all the same, after a captcha: the URL of the exe, typed
    exactly. Or download it by hand, once.
expired

Other "things"

  • The replacement of the exe is safe: if anything goes wrong, the old
    zpaqfranz is kept. A user folder whose name contains accented characters now works.
  • Bugfix, with data loss: d -force could delete the original file and
    keep a link to it (very uncommon... but...). Now d also works on multiple folders at once.
  • Extraction on Linux, BSD, macOS: permissions are now handled as tar does.
  • l -attr now has its header.
  • Bugfix: -stdin -longpath stored the stream as a folder (issue 355); an
    a -verify that detected a wrong CRC-32 still ended with "all OK".

Part one, what is new, for the user

  1. update, in two words
  2. My signature, an Italian qualified signature
  3. How it was hardened
  4. https on Windows
  5. When my certificate expires: -force -kill
  6. The replacement of the exe
  7. A word on downloading executables
  8. d: never the original, and more folders
  9. Extraction on *nix: permissions as tar
  10. l -attr
  11. Bugfix

1. update, in two words

On my site, next to each exe, there is a small file with the same name and
.sha256.p7m (zpaqfranz.sha256.p7m, zpaqfranzhw.sha256.p7m,
zpaqfranz32.sha256.p7m). It contains a single line: the executable's SHA-256
hash, version and date.

7f6b9b2acefe53781c8e3a161b6907d92e10004c3845dd6fcb3654607ed94d0f *65.18r_(2026-11-07)

That line is signed by me. update:

  1. downloads the .p7m and checks my signature;
  2. reads the version: is it newer than yours?
  3. with -force, downloads the exe and computes its SHA-256;
  4. if it matches the one in the signed line, replaces your zpaqfranz.

The exe itself is not signed: its SHA-256 is. Change one byte in the exe,
and the hash is not the one I signed. Change one byte in the .p7m, and the
signature does not hold.

On Linux, the BSDs and macOS, update checks whether a newer version is available,
but does not replace anything (so there is very little to worry about).

On Windows, only zpaqfranz.exe, zpaqfranzhw.exe and zpaqfranz32.exe update
themselves: see the final note.

2. My signature, an Italian qualified signature

In Italy there is the firma elettronica qualificata (FEQ). It is issued by a
qualified trust service provider (mine: ArubaPEC), after verifying your identity
in person. The private key is stored in a chip (a SIM card) and cannot be
extracted.

The format is CAdES: a .p7m file (PKCS#7), with my certificate inside. Mine:

Subject   FRANCO CORBELLI, serialNumber=TINIT-CRBFNC72T25H294X (my tax code)
Issuer    ArubaPEC EU Qualified Certificates CA G1
Valid     2025-12-24 => 2028-12-24
Key       RSA 2048 bit, SHA-256

It is not widely used outside Italy: few people know about it, and few programs can
verify it. That is not a problem here, because zpaqfranz checks it by itself.

Why this one, and not a "code signing" certificate? Because I already pay
for this one. A code signing certificate is one more yearly fee, paid to one of the
companies Microsoft likes. I do not see why. If my signature is fine for you, use
update. If it is not, do not use it: point 7
says what else you can do.

3. How it was hardened

Now (65.19) zpaqfranz checks my certificate:

  • The certificate is pinned. zpaqfranz has the SHA-256 of my certificate
    written in its source, computed over the whole certificate: every byte of it (the key,
    the dates, the issuer, the extensions). The .p7m must contain that one, and only
    that one. No chain of authorities, no "any certificate with that name".
  • Every field in the envelope is checked, one by one. A .p7m has parts that the
    signature does not cover: the versions, the algorithms, the type of the content, who
    the signer is. Each one must be exactly what it should be, each container must end
    where its parent ends, and nothing may come after the signature.
  • The signature itself: RSA PKCS#1 v1.5 with SHA-256, with the padding checked
    to the last byte (the hash exactly at the end: no room for the forgeries
    known since Bleichenbacher).
  • A broken file cannot crash it: a length field claiming 2 GB inside the .p7m
    could make zpaqfranz read beyond its buffer and crash; 100 bytes of garbage after
    the end were accepted. Both are now rejected.
  • The expiry is shown: an Italian qualified certificate lasts exactly 3 years. From
    180 days before the date, update says so in yellow, with the date. The clock
    of your PC changes only the words, never the verdict: anyone can move a clock.

How thoroughly was it tested? Every byte of the actual .p7m file on my site
(2,831 bytes) was modified individually, twice (XOR 01 and XOR 80): FULL SCAN.
All 5,662 modified files were refused.

Check it yourself: update -paranoid downloads the .p7m, copies its path
to the clipboard and tells you how to check it: with openssl, or with the
verification site of the Italian notaries (https://vol.ca.notariato.it, it opens
it for you). AgID, the Italian agency, lists the other programs that can do it.

One more thing. The check is made by the zpaqfranz you already have. A
65.18 that updates itself still checks with the code of 65.18. The hardening
protects the updates made by 65.19 and later.

4. https on Windows

  • update, and every other download from my site (the DLLs, WinFsp...), now uses
    HTTPS, with the certificates of Windows.
  • If HTTPS is unavailable (zpaqfranz32.exe on XP, a Windows Server 2008 never updated, old root
    certificates, a clock far off), zpaqfranz displays a yellow warning and
    automatically falls back to HTTP. Is it safe? Yes: what makes a file good is
    not the channel, it is the check after it. The SHA-256 signed by me for update,
    the SHA-256 written in zpaqfranz for the DLLs and WinFsp.
  • -nohttps: HTTP straight away.
  • Windows (WinINet) errors are now displayed as readable messages: "cannot connect",
    "certificate authority not trusted (old root certificates?)", and so on, not a
    bare number.

On Linux, the BSDs and macOS, zpaqfranz speaks plain HTTP with its own small
client (rewritten: the status of the server, the announced size and the received
size are checked, with a 30-second timeout). The signature is checked in the same
way.

5. When my certificate expires: -force -kill

My certificate expires on 2028-12-24. Then I'll get a new SIM card, with a new key.
An older version of zpaqfranz cannot know about it: for that version, a signature
with the new key is exactly like a fake, and it is refused (it must be). The same
happens if one day Aruba changes the format of its signatures: my check is strict,
and refuses.

Two ways out:

  • Download zpaqfranz by hand, once (my site, GitHub, SourceForge). The new
    one knows the new certificate, and updates by itself again.
  • update -force -kill (new in 65.19o). With a signature that cannot be
    verified, zpaqfranz says so in red, shows the URL the exe comes from, and asks
    you to type it exactly (a captcha). It is better than nothing:
00552! P7M signature verification FAILED: good signature, but with a certificate this zpaqfranz does not know (...)
03858! THE SIGNATURE IS NOT VERIFIED: this zpaqfranz cannot tell the exe from a FAKE
03859! -force -kill: it is installed all the same, ONLY if you type EXACTLY the URL it comes from
03860! Do it only if you trust that URL (with https, the certificate of the site is all that is left)

To confirm a dangerous command
>>> update with a signature NOT verified: the URL of the exe
Captcha to continue:     https://www.francocorbelli.it/zpaqfranz/win64/zpaqfranz.exe
  • The captcha is always required, also with -nocaptcha. It is read from what
    you type: with nothing there (a scheduled task), nothing is done. A script can
    write the URL in it, on purpose: that is a choice, not an accident.
  • The exe comes from that URL only: no fallback to HTTP if HTTPS fails.
    Without my signature, the website's TLS certificate is all that remains.
  • If the .p7m still states which SHA-256 the exe has (not verified), the download
    is checked against it: that protects from a broken download, not from a fake.
    If not, the executable is accepted as downloaded.
  • In both cases its SHA-256 is printed: compare it with the one on GitHub, if
    you want.

Without -kill it is refused, as before, with one line saying that this way
exists.

6. The replacement of the exe

A running exe cannot be overwritten on Windows: update writes a small batch file,
which closes zpaqfranz and puts the new one in its place. That batch was rewritten.

Before: it deleted the exe, then copied the new one. A delete that worked and
a copy that did not = no zpaqfranz at all. If the exe could not be deleted, the
result was an endless, invisible loop. When run with its full path (a scheduled
task), zpaqfranz was not even found by taskkill: endless loop again. A user
folder with an accent (C:\Users\Niccolò) was not found: the batch is read by cmd
in the code page of the console, while the path was written in UTF-8.

Now:

  • before downloading anything, update checks that the folder of the exe can be
    written to (Program Files without administrator rights, a read-only share): if
    not, it stops, with nothing changed;
  • the old exe is renamed to .old (it works on NTFS, FAT32, exFAT), the new
    one is copied and compared byte by byte, and the .old is deleted only at the
    end. If anything goes wrong, the old executable is restored;
  • the batch is plain ASCII, and the paths are passed in the environment: accents,
    %, & in a path are just characters;
  • it runs in your console: its messages are visible.

A TEMP folder whose path contained accented characters caused zpaqfranz to crash
immediately, in update and elsewhere (x, work...). Fixed.

7. A word on downloading executables

Any executable downloaded from the Internet requires some care. It is not a problem
specific to zpaqfranz: it is the same for every program, from anywhere.

If you have doubts:

I cannot do much more than this.

8. d: never the original, and more folders

Bugfix, with data loss. d -force deletes the duplicate files and keeps the
first file by name. If the first one was a symbolic link to a file (Windows),
or the file seen through a bind mount (Linux), the link was kept and the
original deleted, with "all OK". The link then pointed to nothing. Now a name
that is the same file as the one that stays is never deleted. For the same reason,
the hard links of the file that stays are no longer removed: they freed nothing.

Multiple folders: one copy of each file across all of them.

zpaqfranz d c:\video\ d:\film\ -force
  • each folder must end with \ (or /): a typo must not become something to
    search for;
  • the first folder takes priority, followed by the first filename;
  • the same folder twice, or one inside the other: refused (exit code 2).

9. Extraction on *nix: permissions as tar

Previously, x restored the permissions stored in the archive exactly, regardless
of the user: 0777 stayed 0777, a setuid file stayed setuid, even for a non-root
user. Now permissions are handled as tar does:

  • a non-root user: archive permissions, adjusted by the umask, without
    setuid, setgid, sticky;
  • root: exactly as stored;
  • -sameperms: exactly as stored, for a non-root user too.

Archives made on Windows: as before (the umask).

10. l -attr

l -attr had no header, and its columns were shifted by one position. Now there is
a header, with Attr, aligned with the numbers, and the width of Size is computed
as without -attr. -terse is the same as before, byte by byte.

11. Bugfix

  • -stdin -longpath (issue 355, Windows): the name of the stream became the
    current folder (C:/Users/x/) and was stored as a folder: the name was lost, and x
    created a folder. With -stdin the name is a label, not a file: -longpath is
    now ignored, with a line that says so.
  • a -verify / a -collision: a file whose stored fragments did not give
    the CRC-32 of the data read was reported (SOMETHING WRONG, GURU-C), and then
    "all OK" was printed, with exit code 0. Now it is an error, with exit code 2.
  • update without the signature file (not downloaded): exit code 2. Before,
    -paranoid explained how to check a file that did not exist, and printed "all OK".
  • A TEMP with an accent (see point 6).

Part two, the details, for power users

1. The checks of the .p7m, one by one

verify_p7m_bytes, in this order; the first that fails is the error (00552!):

  • the whole file is one DER SEQUENCE, and nothing follows it;
  • pkcs7-signedData, SignedData version 1, digest algorithms: SHA-256 only;
  • the content is id-data, and it is present (not detached);
  • one certificate, and only one, with my tax code in its subject; its SHA-256
    must be in g_p7mchiavi
    (c3294652aaf8d9d30a06ec7033289b72e2072f8d507fadb925c8239406274b30);
  • one SignerInfo, version 1, sid = issuer and serial of that certificate,
    digest SHA-256;
  • signed attributes: contentType = id-data, messageDigest = SHA-256 of the
    content;
  • RSA, a signature of 256 bytes, and nothing after it (no unsigned attributes:
    nobody would check them);
  • PKCS#1 v1.5: 00 01 FF..FF 00, DigestInfo SHA-256, the hash, and the end
    of the block;
  • each container ends where its parent ends.

A good signature with a certificate that is not mine is refused at the end
(good signature, but with a certificate this zpaqfranz does not know, with its
SHA-256): this distinguishes "a new certificate (or a fake)" from "a broken file".

To add my next certificate: its SHA-256 and its expiry go in g_p7mchiavi, next
to the old one (the commands are written above the table).

2. The switches

Switch What
-nohttps Windows: HTTP straight away (default: HTTPS, then HTTP by itself)
-force -kill update: install even if not newer; with a signature that cannot be verified, after the captcha
-sameperms *nix, x: the permissions exactly as stored, also for a non-root user

Not in the help, for the tests: -updatefake YYYYMMDD (the expiry as on that
day), -signfake byte[:xor],... (the .p7m changed on disk after the
download, as someone in the middle would do), -httpsfake (HTTPS fails, as on
an old Windows).

3. What can change for a script

  • update: no signature file, exit code 2. On Windows, HTTPS first. The batch
    prints nothing when all goes well (no zpaqfranz updated). During the update,
    the old exe is <name>.old.
  • update on zpaqfranz-full and on the XP build: refused, exit code 2, with
    any switch (-force -kill too, the two URLs too). A scheduled
    zpaqfranz-full update -force -kill now fails.
  • *x on nix, as a non-root user: umask applied, no setuid, setgid, sticky.
    -sameperms for the old behaviour.
  • d: multiple folders only if each ends with \ or /; a second argument that
    is a folder without it, a folder that does not exist, the same folder twice, or one
    inside the other: exit code 2. A name that is the same file as the one that
    stays is never deleted.
  • l -attr: a header line (not with -terse).
  • a -verify / a -collision: a CRC-32 that does not match gives exit code 2.
  • a -stdin -longpath: the name as written.

4. The messages

Code When
00534 the signature is good (green), with the expiry of the certificate
00551! the signature file cannot be downloaded (exit code 2)
00552! the signature is not verified, and why (exit code 2, unless -force -kill and the captcha)
03826 HTTPS failed, now HTTP (yellow)
03835! the folder of the exe cannot be written to: nothing done
03838 / 03839! (no taskkill, XP Home) the exe is, or is not, the new one
03840-03850 the box about the expiry of my certificate
03855 -longpath ignored with -stdin
03856! files stored with fragments that do not give their CRC-32 (exit code 2)
03857 update: the line saying that -force -kill exists
03858!-03861 update -force -kill: the warning, the captcha, nothing done
03862, 03863$ the SHA-256 of the .p7m (not verified) was used, or none
03864-03866 the version unknown, the candidate, the SHA-256 downloaded
03867 zpaqfranz closes, the batch replaces it
03868!, 03869 update on zpaqfranz-full or on the XP build: refused (exit code 2), update it by hand
65944-65950 d with multiple folders: refused

Part three, for developers

1. Known limits and open points

  • The hardening protects the updates made by 65.19 and later (see the end of
    point 3 of part one).
  • If Aruba changes the format of its signatures (a chain of certificates, a
    timestamp), update refuses until a new zpaqfranz knows it: I try update
    after every publication.
  • XP: the batch has not been run on a real XP. I lost all my old VMs. Investigation is underway.
  • l -csv still prints the header with fixed columns: it is not CSV.
  • work, external, maxcpu and autotest run their own batches: with a
    TEMP containing accented characters they still fail. Please stick to ASCII characters: it's a feature, not a bug :)

A FINAL TECHNICAL NOTE

Just a reminder: the main source file is always available here:

https://www.francocorbelli.it/zpaqfranz/win64/zpaqfranz.cpp

Also, automatic updating is supported only by these Windows executables:

zpaqfranz.exe (64-bit) => https://www.francocorbelli.it/zpaqfranz/win64/zpaqfranz.exe
zpaqfranzhw.exe (64-bit) => https://www.francocorbelli.it/zpaqfranz/win64hw/zpaqfranzhw.exe
zpaqfranz32.exe (32-bit) => https://www.francocorbelli.it/zpaqfranz/win32/zpaqfranz32.exe

zpaqfranz-full.exe and the Windows XP-compatible version do not update themselves:
update refuses (exit code 2), because it would put zpaqfranz.exe or zpaqfranz32.exe
in their place. Neither do the open version (it has no update) and the Linux, BSD
and macOS builds (update only says whether a newer one exists). For these builds,
download or compile new versions manually.

Careful: a Windows build you compile yourself does update, and becomes my
zpaqfranz.exe (or zpaqfranzhw.exe, if compiled with -DHWSHA1). If you compile it
so as to trust only the source, do not use update.

Download zpaqfranz

Don't miss a new zpaqfranz release

NewReleases is sending notifications on new releases.